feat: 初始化项目基础结构和功能
添加项目基础文件结构,包括页面、组件、API、状态管理等 集成Taro4、Vue3、Pinia、TailwindCSS等技术栈 实现授权登录、页面导航、海报生成等核心功能 配置构建工具和开发环境
Showing
63 changed files
with
3377 additions
and
0 deletions
.editorconfig
0 → 100644
.eslintrc
0 → 100644
.gitignore
0 → 100644
| 1 | +## 项目介绍 | ||
| 2 | + | ||
| 3 | +基于Taro4的微信小程序模版,集成了常用的功能,如登录、注册、列表、详情、购物车等。 | ||
| 4 | + | ||
| 5 | +## 技术栈 | ||
| 6 | + | ||
| 7 | +- Taro4 | ||
| 8 | +- Vue3 | ||
| 9 | +- TypeScript | ||
| 10 | +- Pinia | ||
| 11 | +- Less | ||
| 12 | + | ||
| 13 | +## 项目结构 | ||
| 14 | + | ||
| 15 | +- src | ||
| 16 | + - api:请求接口 | ||
| 17 | + - assets:静态资源 | ||
| 18 | + - components:全局组件 | ||
| 19 | + - config:项目配置 | ||
| 20 | + - pages:页面 | ||
| 21 | + - stores:状态管理 | ||
| 22 | + - utils:工具函数 | ||
| 23 | + - app.config.js:项目配置 | ||
| 24 | + - app.js:应用入口 | ||
| 25 | + - app.less:全局样式 | ||
| 26 | +- taro.config.js:Taro配置 | ||
| 27 | +- tsconfig.json:TypeScript配置 | ||
| 28 | +- package.json:依赖配置 | ||
| 29 | + | ||
| 30 | +## 项目运行 | ||
| 31 | + | ||
| 32 | +1. 安装依赖 | ||
| 33 | + | ||
| 34 | +```bash | ||
| 35 | +npm install | ||
| 36 | +``` | ||
| 37 | + | ||
| 38 | +2. 运行项目 | ||
| 39 | + | ||
| 40 | +```bash | ||
| 41 | +npm run dev:weapp | ||
| 42 | +``` | ||
| 43 | + | ||
| 44 | +3. 打包项目 | ||
| 45 | + | ||
| 46 | +```bash | ||
| 47 | +npm run build:weapp | ||
| 48 | +``` | ... | ... |
babel.config.js
0 → 100644
components.d.ts
0 → 100644
| 1 | +/* eslint-disable */ | ||
| 2 | +/* prettier-ignore */ | ||
| 3 | +// @ts-nocheck | ||
| 4 | +// Generated by unplugin-vue-components | ||
| 5 | +// Read more: https://github.com/vuejs/core/pull/3399 | ||
| 6 | +export {} | ||
| 7 | + | ||
| 8 | +declare module 'vue' { | ||
| 9 | + export interface GlobalComponents { | ||
| 10 | + NavBar: typeof import('./src/components/navBar.vue')['default'] | ||
| 11 | + NutButton: typeof import('@nutui/nutui-taro')['Button'] | ||
| 12 | + NutToast: typeof import('@nutui/nutui-taro')['Toast'] | ||
| 13 | + Picker: typeof import('./src/components/time-picker-data/picker.vue')['default'] | ||
| 14 | + PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default'] | ||
| 15 | + RouterLink: typeof import('vue-router')['RouterLink'] | ||
| 16 | + RouterView: typeof import('vue-router')['RouterView'] | ||
| 17 | + } | ||
| 18 | +} |
config/dev.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2025-06-28 10:33:00 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-06-28 10:45:27 | ||
| 5 | + * @FilePath: /myApp/config/dev.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +export default { | ||
| 9 | + env: { | ||
| 10 | + NODE_ENV: '"development"' | ||
| 11 | + }, | ||
| 12 | + logger: { | ||
| 13 | + quiet: false, | ||
| 14 | + stats: true | ||
| 15 | + }, | ||
| 16 | + mini: {}, | ||
| 17 | + h5: {} | ||
| 18 | +} |
config/index.js
0 → 100644
| 1 | +import { defineConfig } from '@tarojs/cli' | ||
| 2 | + | ||
| 3 | +import devConfig from './dev' | ||
| 4 | +import prodConfig from './prod' | ||
| 5 | +import NutUIResolver from '@nutui/auto-import-resolver' | ||
| 6 | +import Components from 'unplugin-vue-components/webpack' | ||
| 7 | + | ||
| 8 | +const path = require('path') | ||
| 9 | +const { UnifiedWebpackPluginV5 } = require('weapp-tailwindcss/webpack') | ||
| 10 | + | ||
| 11 | +// https://taro-docs.jd.com/docs/next/config#defineconfig-辅助函数 | ||
| 12 | +export default defineConfig(async (merge) => { | ||
| 13 | + const baseConfig = { | ||
| 14 | + projectName: 'myApp', | ||
| 15 | + date: '2025-6-28', | ||
| 16 | + designWidth (input) { | ||
| 17 | + // 配置 NutUI 375 尺寸 | ||
| 18 | + if (input?.file?.replace(/\\+/g, '/').indexOf('@nutui') > -1) { | ||
| 19 | + return 375 | ||
| 20 | + } | ||
| 21 | + // 全局使用 Taro 默认的 750 尺寸 | ||
| 22 | + return 750 | ||
| 23 | + }, | ||
| 24 | + deviceRatio: { | ||
| 25 | + 640: 2.34 / 2, | ||
| 26 | + 750: 1, | ||
| 27 | + 375: 2, | ||
| 28 | + 828: 1.81 / 2 | ||
| 29 | + }, | ||
| 30 | + alias: { // 配置目录别名 | ||
| 31 | + "@/utils": path.resolve(__dirname, "../src/utils"), | ||
| 32 | + "@/components": path.resolve(__dirname, "../src/components"), | ||
| 33 | + "@/images": path.resolve(__dirname, "../src/assets/images"), | ||
| 34 | + "@/assets": path.resolve(__dirname, "../src/assets"), | ||
| 35 | + "@/composables": path.resolve(__dirname, "../src/composables"), | ||
| 36 | + "@/api": path.resolve(__dirname, "../src/api"), | ||
| 37 | + "@/stores": path.resolve(__dirname, "../src/stores"), | ||
| 38 | + "@/hooks": path.resolve(__dirname, "../src/hooks"), | ||
| 39 | + }, | ||
| 40 | + sourceRoot: 'src', | ||
| 41 | + outputRoot: 'dist', | ||
| 42 | + plugins: ['@tarojs/plugin-html', 'taro-plugin-pinia',], | ||
| 43 | + defineConstants: { | ||
| 44 | + }, | ||
| 45 | + copy: { | ||
| 46 | + patterns: [ | ||
| 47 | + ], | ||
| 48 | + options: { | ||
| 49 | + } | ||
| 50 | + }, | ||
| 51 | + framework: 'vue3', | ||
| 52 | + compiler: { | ||
| 53 | + type: 'webpack5', | ||
| 54 | + prebundle: { | ||
| 55 | + enable: false | ||
| 56 | + } | ||
| 57 | + }, | ||
| 58 | + cache: { | ||
| 59 | + enable: false // Webpack 持久化缓存配置,建议开启。默认配置请参考:https://docs.taro.zone/docs/config-detail#cache | ||
| 60 | + }, | ||
| 61 | + sass:{ | ||
| 62 | + data: `@import "@nutui/nutui-taro/dist/styles/variables.scss";` | ||
| 63 | + }, | ||
| 64 | + mini: { | ||
| 65 | + miniCssExtractPluginOption: { | ||
| 66 | + ignoreOrder: true | ||
| 67 | + }, | ||
| 68 | + postcss: { | ||
| 69 | + pxtransform: { | ||
| 70 | + enable: true, | ||
| 71 | + config: { | ||
| 72 | + | ||
| 73 | + } | ||
| 74 | + }, | ||
| 75 | + // url: { | ||
| 76 | + // enable: true, | ||
| 77 | + // config: { | ||
| 78 | + // limit: 1024 // 设定转换尺寸上限 | ||
| 79 | + // } | ||
| 80 | + // }, | ||
| 81 | + cssModules: { | ||
| 82 | + enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true | ||
| 83 | + config: { | ||
| 84 | + namingPattern: 'module', // 转换模式,取值为 global/module | ||
| 85 | + generateScopedName: '[name]__[local]___[hash:base64:5]' | ||
| 86 | + } | ||
| 87 | + } | ||
| 88 | + }, | ||
| 89 | + webpackChain(chain) { | ||
| 90 | + | ||
| 91 | + chain.plugin('unplugin-vue-components').use(Components({ | ||
| 92 | + resolvers: [NutUIResolver({taro: true})] | ||
| 93 | + })) | ||
| 94 | + | ||
| 95 | + chain.merge({ | ||
| 96 | + plugin: { | ||
| 97 | + install: { | ||
| 98 | + plugin: UnifiedWebpackPluginV5, | ||
| 99 | + args: [{ | ||
| 100 | + appType: 'taro', | ||
| 101 | + // 下面个配置,会开启 rem -> rpx 的转化 | ||
| 102 | + rem2rpx: true, | ||
| 103 | + injectAdditionalCssVarScope: true | ||
| 104 | + }] | ||
| 105 | + } | ||
| 106 | + } | ||
| 107 | + }) | ||
| 108 | + } | ||
| 109 | + }, | ||
| 110 | + h5: { | ||
| 111 | + publicPath: '/', | ||
| 112 | + staticDirectory: 'static', | ||
| 113 | + // esnextModules: ['nutui-taro', 'icons-vue-taro'], | ||
| 114 | + output: { | ||
| 115 | + filename: 'js/[name].[hash:8].js', | ||
| 116 | + chunkFilename: 'js/[name].[chunkhash:8].js' | ||
| 117 | + }, | ||
| 118 | + miniCssExtractPluginOption: { | ||
| 119 | + ignoreOrder: true, | ||
| 120 | + filename: 'css/[name].[hash].css', | ||
| 121 | + chunkFilename: 'css/[name].[chunkhash].css' | ||
| 122 | + }, | ||
| 123 | + postcss: { | ||
| 124 | + autoprefixer: { | ||
| 125 | + enable: true, | ||
| 126 | + config: {} | ||
| 127 | + }, | ||
| 128 | + cssModules: { | ||
| 129 | + enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true | ||
| 130 | + config: { | ||
| 131 | + namingPattern: 'module', // 转换模式,取值为 global/module | ||
| 132 | + generateScopedName: '[name]__[local]___[hash:base64:5]' | ||
| 133 | + } | ||
| 134 | + } | ||
| 135 | + }, | ||
| 136 | + webpackChain(chain) { | ||
| 137 | + | ||
| 138 | + chain.plugin('unplugin-vue-components').use(Components({ | ||
| 139 | + resolvers: [NutUIResolver({taro: true})] | ||
| 140 | + })) | ||
| 141 | + } | ||
| 142 | + }, | ||
| 143 | + rn: { | ||
| 144 | + appName: 'taroDemo', | ||
| 145 | + postcss: { | ||
| 146 | + cssModules: { | ||
| 147 | + enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true | ||
| 148 | + } | ||
| 149 | + } | ||
| 150 | + } | ||
| 151 | + } | ||
| 152 | + if (process.env.NODE_ENV === 'development') { | ||
| 153 | + // 本地开发构建配置(不混淆压缩) | ||
| 154 | + return merge({}, baseConfig, devConfig) | ||
| 155 | + } | ||
| 156 | + // 生产构建配置(默认开启压缩混淆等) | ||
| 157 | + return merge({}, baseConfig, prodConfig) | ||
| 158 | +}) |
config/prod.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2025-06-28 10:33:00 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-06-28 11:00:57 | ||
| 5 | + * @FilePath: /myApp/config/prod.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +export default { | ||
| 9 | + env: { | ||
| 10 | + NODE_ENV: '"production"' | ||
| 11 | + }, | ||
| 12 | + mini: {}, | ||
| 13 | + h5: { | ||
| 14 | + /** | ||
| 15 | + * WebpackChain 插件配置 | ||
| 16 | + * @docs https://github.com/neutrinojs/webpack-chain | ||
| 17 | + */ | ||
| 18 | + // webpackChain (chain) { | ||
| 19 | + // /** | ||
| 20 | + // * 如果 h5 端编译后体积过大,可以使用 webpack-bundle-analyzer 插件对打包体积进行分析。 | ||
| 21 | + // * @docs https://github.com/webpack-contrib/webpack-bundle-analyzer | ||
| 22 | + // */ | ||
| 23 | + // chain.plugin('analyzer') | ||
| 24 | + // .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, []) | ||
| 25 | + // /** | ||
| 26 | + // * 如果 h5 端首屏加载时间过长,可以使用 prerender-spa-plugin 插件预加载首页。 | ||
| 27 | + // * @docs https://github.com/chrisvfritz/prerender-spa-plugin | ||
| 28 | + // */ | ||
| 29 | + // const path = require('path') | ||
| 30 | + // const Prerender = require('prerender-spa-plugin') | ||
| 31 | + // const staticDir = path.join(__dirname, '..', 'dist') | ||
| 32 | + // chain | ||
| 33 | + // .plugin('prerender') | ||
| 34 | + // .use(new Prerender({ | ||
| 35 | + // staticDir, | ||
| 36 | + // routes: [ '/pages/index/index' ], | ||
| 37 | + // postProcess: (context) => ({ ...context, outputPath: path.join(staticDir, 'index.html') }) | ||
| 38 | + // })) | ||
| 39 | + // } | ||
| 40 | + } | ||
| 41 | +} |
package.json
0 → 100644
| 1 | +{ | ||
| 2 | + "name": "myApp", | ||
| 3 | + "version": "1.0.0", | ||
| 4 | + "private": true, | ||
| 5 | + "description": "myApp", | ||
| 6 | + "templateInfo": { | ||
| 7 | + "name": "vue3-NutUI", | ||
| 8 | + "typescript": false, | ||
| 9 | + "css": "Less", | ||
| 10 | + "framework": "Vue3" | ||
| 11 | + }, | ||
| 12 | + "scripts": { | ||
| 13 | + "build:weapp": "taro build --type weapp", | ||
| 14 | + "build:swan": "taro build --type swan", | ||
| 15 | + "build:alipay": "taro build --type alipay", | ||
| 16 | + "build:tt": "taro build --type tt", | ||
| 17 | + "build:h5": "taro build --type h5", | ||
| 18 | + "build:rn": "taro build --type rn", | ||
| 19 | + "build:qq": "taro build --type qq", | ||
| 20 | + "build:quickapp": "taro build --type quickapp", | ||
| 21 | + "dev:weapp": "npm run build:weapp -- --watch", | ||
| 22 | + "dev:swan": "npm run build:swan -- --watch", | ||
| 23 | + "dev:alipay": "npm run build:alipay -- --watch", | ||
| 24 | + "dev:tt": "npm run build:tt -- --watch", | ||
| 25 | + "dev:h5": "npm run build:h5 -- --watch", | ||
| 26 | + "dev:rn": "npm run build:rn -- --watch", | ||
| 27 | + "dev:qq": "npm run build:qq -- --watch", | ||
| 28 | + "dev:quickapp": "npm run build:quickapp -- --watch", | ||
| 29 | + "postinstall": "weapp-tw patch" | ||
| 30 | + }, | ||
| 31 | + "browserslist": [ | ||
| 32 | + "last 3 versions", | ||
| 33 | + "Android >= 4.1", | ||
| 34 | + "ios >= 8" | ||
| 35 | + ], | ||
| 36 | + "author": "", | ||
| 37 | + "license": "MIT", | ||
| 38 | + "dependencies": { | ||
| 39 | + "@babel/runtime": "^7.7.7", | ||
| 40 | + "@nutui/icons-vue-taro": "^0.0.9", | ||
| 41 | + "@nutui/nutui-taro": "^4.3.13", | ||
| 42 | + "@tarojs/components": "4.1.2", | ||
| 43 | + "@tarojs/helper": "4.1.2", | ||
| 44 | + "@tarojs/plugin-framework-vue3": "4.1.2", | ||
| 45 | + "@tarojs/plugin-html": "4.1.2", | ||
| 46 | + "@tarojs/plugin-platform-alipay": "4.1.2", | ||
| 47 | + "@tarojs/plugin-platform-h5": "4.1.2", | ||
| 48 | + "@tarojs/plugin-platform-jd": "4.1.2", | ||
| 49 | + "@tarojs/plugin-platform-qq": "4.1.2", | ||
| 50 | + "@tarojs/plugin-platform-swan": "4.1.2", | ||
| 51 | + "@tarojs/plugin-platform-tt": "4.1.2", | ||
| 52 | + "@tarojs/plugin-platform-weapp": "4.1.2", | ||
| 53 | + "@tarojs/runtime": "4.1.2", | ||
| 54 | + "@tarojs/shared": "4.1.2", | ||
| 55 | + "@tarojs/taro": "4.1.2", | ||
| 56 | + "axios-miniprogram": "^2.7.2", | ||
| 57 | + "pinia": "^3.0.3", | ||
| 58 | + "taro-plugin-pinia": "^1.0.0", | ||
| 59 | + "vue": "^3.3.0" | ||
| 60 | + }, | ||
| 61 | + "devDependencies": { | ||
| 62 | + "@babel/core": "^7.8.0", | ||
| 63 | + "@nutui/auto-import-resolver": "^1.0.0", | ||
| 64 | + "@tarojs/cli": "4.1.2", | ||
| 65 | + "@tarojs/taro-loader": "4.1.2", | ||
| 66 | + "@tarojs/webpack5-runner": "4.1.2", | ||
| 67 | + "@types/webpack-env": "^1.13.6", | ||
| 68 | + "@vue/babel-plugin-jsx": "^1.0.6", | ||
| 69 | + "@vue/compiler-sfc": "^3.0.0", | ||
| 70 | + "autoprefixer": "^10.4.21", | ||
| 71 | + "babel-preset-taro": "4.1.2", | ||
| 72 | + "css-loader": "3.4.2", | ||
| 73 | + "eslint": "^8.12.0", | ||
| 74 | + "eslint-config-taro": "4.1.2", | ||
| 75 | + "postcss": "^8.5.6", | ||
| 76 | + "style-loader": "1.3.0", | ||
| 77 | + "tailwindcss": "^3.4.0", | ||
| 78 | + "unplugin-vue-components": "^0.26.0", | ||
| 79 | + "vue-loader": "^17.0.0", | ||
| 80 | + "weapp-tailwindcss": "^4.1.10", | ||
| 81 | + "webpack": "5.78.0" | ||
| 82 | + } | ||
| 83 | +} |
postcss.config.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2025-06-30 13:27:35 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-06-30 13:27:42 | ||
| 5 | + * @FilePath: /myApp/postcss.config.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +// postcss 插件以 object 方式注册的话,是按照由上到下的顺序执行的 | ||
| 9 | +module.exports = { | ||
| 10 | + plugins: { | ||
| 11 | + tailwindcss: {}, | ||
| 12 | + autoprefixer: {}, | ||
| 13 | + }, | ||
| 14 | +} |
project.config.json
0 → 100644
| 1 | +{ | ||
| 2 | + "miniprogramRoot": "./dist", | ||
| 3 | + "projectname": "myApp", | ||
| 4 | + "description": "myApp", | ||
| 5 | + "appid": "touristappid", | ||
| 6 | + "setting": { | ||
| 7 | + "urlCheck": true, | ||
| 8 | + "es6": false, | ||
| 9 | + "enhance": false, | ||
| 10 | + "compileHotReLoad": false, | ||
| 11 | + "postcss": false, | ||
| 12 | + "minified": false | ||
| 13 | + }, | ||
| 14 | + "compileType": "miniprogram" | ||
| 15 | +} |
project.tt.json
0 → 100644
src/api/common.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2022-06-17 14:54:29 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2022-06-18 22:18:46 | ||
| 5 | + * @FilePath: /tswj/src/api/common.js | ||
| 6 | + * @Description: 通用接口 | ||
| 7 | + */ | ||
| 8 | +import { fn, fetch, uploadFn } from '@/api/fn'; | ||
| 9 | + | ||
| 10 | +const Api = { | ||
| 11 | + SMS: '/srv/?a=sms', | ||
| 12 | + TOKEN: '/srv/?a=upload', | ||
| 13 | + SAVE_FILE: '/srv/?a=upload&t=save_file', | ||
| 14 | +} | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * @description: 发送验证码 | ||
| 18 | + * @param {*} phone 手机号码 | ||
| 19 | + * @returns | ||
| 20 | + */ | ||
| 21 | +export const smsAPI = (params) => fn(fetch.post(Api.SMS, params)); | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * @description: 获取七牛token | ||
| 25 | + * @param {*} filename 文件名 | ||
| 26 | + * @param {*} file 图片base64 | ||
| 27 | + * @returns | ||
| 28 | + */ | ||
| 29 | +export const qiniuTokenAPI = (params) => fn(fetch.stringifyPost(Api.TOKEN, params)); | ||
| 30 | + | ||
| 31 | +/** | ||
| 32 | + * @description: 上传七牛 | ||
| 33 | + * @param {*} | ||
| 34 | + * @returns | ||
| 35 | + */ | ||
| 36 | +export const qiniuUploadAPI = (url, data, config) => uploadFn(fetch.basePost(url, data, config)); | ||
| 37 | + | ||
| 38 | +/** | ||
| 39 | + * @description: 保存图片 | ||
| 40 | + * @param {*} format | ||
| 41 | + * @param {*} hash | ||
| 42 | + * @param {*} height | ||
| 43 | + * @param {*} width | ||
| 44 | + * @param {*} filekey | ||
| 45 | + * @returns | ||
| 46 | + */ | ||
| 47 | +export const saveFileAPI = (params) => fn(fetch.stringifyPost(Api.SAVE_FILE, params)); |
src/api/fn.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2022-05-18 22:56:08 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2024-05-25 22:35:00 | ||
| 5 | + * @FilePath: /meihuaApp/src/api/fn.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +import axios from '@/utils/request'; | ||
| 9 | +import Taro from '@tarojs/taro' | ||
| 10 | +// import qs from 'qs' | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 网络请求功能函数 | ||
| 14 | + * @param {*} api 请求axios接口 | ||
| 15 | + * @returns 请求成功后,获取数据 | ||
| 16 | + */ | ||
| 17 | +export const fn = (api) => { | ||
| 18 | + return api | ||
| 19 | + .then(res => { | ||
| 20 | + if (res.data.code) { | ||
| 21 | + return res.data || true; | ||
| 22 | + } else { | ||
| 23 | + // tslint:disable-next-line: no-console | ||
| 24 | + console.warn(res); | ||
| 25 | + Taro.showToast({ | ||
| 26 | + title: res.data.msg, | ||
| 27 | + icon: 'none', | ||
| 28 | + duration: 2000 | ||
| 29 | + }); | ||
| 30 | + return false; | ||
| 31 | + } | ||
| 32 | + }) | ||
| 33 | + .catch(err => { | ||
| 34 | + // tslint:disable-next-line: no-console | ||
| 35 | + console.error(err); | ||
| 36 | + return false; | ||
| 37 | + }) | ||
| 38 | + .finally(() => { // 最终执行 | ||
| 39 | + }) | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +/** | ||
| 43 | + * 七牛返回格式 | ||
| 44 | + * @param {*} api | ||
| 45 | + * @returns | ||
| 46 | + */ | ||
| 47 | +export const uploadFn = (api) => { | ||
| 48 | + return api | ||
| 49 | + .then(res => { | ||
| 50 | + if (res.statusText === 'OK') { | ||
| 51 | + return res.data || true; | ||
| 52 | + } else { | ||
| 53 | + // tslint:disable-next-line: no-console | ||
| 54 | + console.warn(res); | ||
| 55 | + Taro.showToast({ | ||
| 56 | + title: res.data.msg, | ||
| 57 | + icon: 'none', | ||
| 58 | + duration: 2000 | ||
| 59 | + }); | ||
| 60 | + return false; | ||
| 61 | + } | ||
| 62 | + }) | ||
| 63 | + .catch(err => { | ||
| 64 | + // tslint:disable-next-line: no-console | ||
| 65 | + console.error(err); | ||
| 66 | + return false; | ||
| 67 | + }) | ||
| 68 | +} | ||
| 69 | + | ||
| 70 | +/** | ||
| 71 | + * 统一 GET/POST 不同传参形式 | ||
| 72 | + */ | ||
| 73 | +export const fetch = { | ||
| 74 | + get: function (api, params) { | ||
| 75 | + return axios.get(api, params) | ||
| 76 | + }, | ||
| 77 | + post: function (api, params) { | ||
| 78 | + return axios.post(api, params) | ||
| 79 | + }, | ||
| 80 | + // stringifyPost: function (api, params) { | ||
| 81 | + // return axios.post(api, qs.stringify(params)) | ||
| 82 | + // }, | ||
| 83 | + basePost: function (url, data, config) { | ||
| 84 | + return axios.post(url, data, config) | ||
| 85 | + } | ||
| 86 | +} |
src/api/index.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2023-12-22 10:29:37 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2024-06-06 09:31:34 | ||
| 5 | + * @FilePath: /meihuaApp/src/api/index.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +import { fn, fetch } from './fn'; | ||
| 9 | + | ||
| 10 | +const Api = { | ||
| 11 | + BIND_PHONE: '/srv/?a=room_order&t=bind_phone', | ||
| 12 | + SEND_SMS_CODE: '/srv/?a=room_order&t=send_sms_code', | ||
| 13 | + SHOW_SESSION: '/srv/?a=room_order&t=show_session', | ||
| 14 | + SAVE_CUSTOMER_INFO: '/srv/?a=room_order&t=save_customer_info', | ||
| 15 | + SYS_PARAM: '/srv/?a=room_order&t=sys_param', | ||
| 16 | + GET_LIST: '/srv/?a=room_data&t=get_list', | ||
| 17 | + GET_ROOM: '/srv/?a=room_data&t=get_room', | ||
| 18 | + ADD_ORDER: '/srv/?a=room_data&t=add_order', | ||
| 19 | + MY_ORDER: '/srv/?a=room_data&t=my_order', | ||
| 20 | + ORDER_CANCEL: '/srv/?a=room_data&t=order_cancel', | ||
| 21 | + PAY: '/srv/?a=pay', | ||
| 22 | + PAY_CHECK: '/srv/?a=pay_check', | ||
| 23 | + ORDER_SUCCESS: '/srv/?a=room_data&t=order_success', | ||
| 24 | + TMP_SYS_PARAM: '/srv/?a=get_item', | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +/** | ||
| 28 | + * @description: 绑定手机号(手机号登录) | ||
| 29 | + * @param phone 手机号 | ||
| 30 | + * @param sms_code 验证码 | ||
| 31 | + * @returns | ||
| 32 | + */ | ||
| 33 | +export const bindPhoneAPI = (params) => fn(fetch.post(Api.BIND_PHONE, params)); | ||
| 34 | + | ||
| 35 | +/** | ||
| 36 | + * @description: 发送验证码 | ||
| 37 | + * @param phone 手机号 | ||
| 38 | + * @returns | ||
| 39 | + */ | ||
| 40 | +export const sendSmsCodeAPI = (params) => fn(fetch.post(Api.SEND_SMS_CODE, params)); | ||
| 41 | + | ||
| 42 | +/** | ||
| 43 | + * @description: 获取我的信息 | ||
| 44 | + * @returns | ||
| 45 | + */ | ||
| 46 | +export const showMyInfoAPI = (params) => fn(fetch.get(Api.SHOW_SESSION, params)); | ||
| 47 | + | ||
| 48 | +/** | ||
| 49 | + * @description: 保存我的信息 | ||
| 50 | + * @param params | ||
| 51 | + * @returns | ||
| 52 | + */ | ||
| 53 | +export const saveCustomerInfoAPI = (params) => fn(fetch.post(Api.SAVE_CUSTOMER_INFO, params)); | ||
| 54 | + | ||
| 55 | +/** | ||
| 56 | + * @description: 获取系统参数 | ||
| 57 | + * @returns | ||
| 58 | + */ | ||
| 59 | +export const sysParamAPI = (params) => fn(fetch.get(Api.SYS_PARAM, params)); | ||
| 60 | + | ||
| 61 | +/** | ||
| 62 | + * @description: 获取房间列表 | ||
| 63 | + * @param start_date 入住时间 | ||
| 64 | + * @param end_date 离店时间 | ||
| 65 | + * @param offset 偏移量 | ||
| 66 | + * @param limit 条数 | ||
| 67 | + * @returns | ||
| 68 | + */ | ||
| 69 | +export const getListAPI = (params) => fn(fetch.get(Api.GET_LIST, params)); | ||
| 70 | + | ||
| 71 | +/** | ||
| 72 | + * @description: 获取房间详情 | ||
| 73 | + * @param start_date 入住时间 | ||
| 74 | + * @param end_date 离店时间 | ||
| 75 | + * @param room_type floor/room | ||
| 76 | + * @returns | ||
| 77 | + */ | ||
| 78 | +export const getRoomAPI = (params) => fn(fetch.get(Api.GET_ROOM, params)); | ||
| 79 | + | ||
| 80 | +/** | ||
| 81 | + * @description: 预定房间 | ||
| 82 | + * @param id ID | ||
| 83 | + * @param num 预定房间数量 | ||
| 84 | + * @param plan_in 入住时间 | ||
| 85 | + * @param plan_out 离店时间 | ||
| 86 | + * @param contact_name 联系人 | ||
| 87 | + * @param contact_phone 联系电话 | ||
| 88 | + * @param order_remark 备注 | ||
| 89 | + * @param room_type floor/room | ||
| 90 | + * @returns | ||
| 91 | + */ | ||
| 92 | +export const addOrderAPI = (params) => fn(fetch.post(Api.ADD_ORDER, params)); | ||
| 93 | + | ||
| 94 | +/** | ||
| 95 | + * @description: 支付 | ||
| 96 | + * @param order_id 订单ID | ||
| 97 | + * @returns | ||
| 98 | + */ | ||
| 99 | +export const payAPI = (params) => fn(fetch.post(Api.PAY, params)); | ||
| 100 | + | ||
| 101 | +/** | ||
| 102 | + * @description: 检查是否支付成功 | ||
| 103 | + * @param order_id 订单ID | ||
| 104 | + * @returns | ||
| 105 | + */ | ||
| 106 | +export const payCheckAPI = (params) => fn(fetch.post(Api.PAY_CHECK, params)); | ||
| 107 | + | ||
| 108 | +/** | ||
| 109 | + * @description: 获取我的订单列表 | ||
| 110 | + * @param pay_type | ||
| 111 | + * @param page | ||
| 112 | + * @param limit | ||
| 113 | + * @returns | ||
| 114 | + */ | ||
| 115 | +export const myOrderAPI = (params) => fn(fetch.get(Api.MY_ORDER, params)); | ||
| 116 | + | ||
| 117 | +/** | ||
| 118 | + * @description: 取消订单 | ||
| 119 | + * @param id | ||
| 120 | + * @returns | ||
| 121 | + */ | ||
| 122 | +export const orderCancelAPI = (params) => fn(fetch.post(Api.ORDER_CANCEL, params)); | ||
| 123 | + | ||
| 124 | +/** | ||
| 125 | + * @description: 订单成功 | ||
| 126 | + * @param id | ||
| 127 | + * @returns | ||
| 128 | + */ | ||
| 129 | +export const orderSuccessAPI = (params) => fn(fetch.post(Api.ORDER_SUCCESS, params)); | ||
| 130 | + | ||
| 131 | +/** | ||
| 132 | + * @description: | ||
| 133 | + * @param id | ||
| 134 | + * @returns | ||
| 135 | + */ | ||
| 136 | +export const tmpSysParamAPI = (params) => fn(fetch.get(Api.TMP_SYS_PARAM, params)); |
src/api/wx/config.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Author: hookehuyr hookehuyr@gmail.com | ||
| 3 | + * @Date: 2022-06-09 13:32:44 | ||
| 4 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 5 | + * @LastEditTime: 2022-06-14 14:47:01 | ||
| 6 | + * @FilePath: /tswj/src/api/wx/config.js | ||
| 7 | + * @Description: | ||
| 8 | + */ | ||
| 9 | +import { fn, fetch } from '@/api/fn'; | ||
| 10 | + | ||
| 11 | +const Api = { | ||
| 12 | + WX_JSAPI: '/srv/?a=wx_share', | ||
| 13 | +} | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * @description 获取微信CONFIG配置文件 | ||
| 17 | + * @param {*} url | ||
| 18 | + * @returns {*} cfg | ||
| 19 | + */ | ||
| 20 | +export const wxJsAPI = (params) => fn(fetch.get(Api.WX_JSAPI, params)); |
src/api/wx/jsApiList.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2022-06-13 14:18:57 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2022-06-13 14:27:21 | ||
| 5 | + * @FilePath: /tswj/src/api/wx/jsApiList.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +export const apiList = [ | ||
| 9 | + "updateAppMessageShareData", | ||
| 10 | + "updateTimelineShareData", | ||
| 11 | + "onMenuShareTimeline", | ||
| 12 | + "onMenuShareAppMessage", | ||
| 13 | + "onMenuShareQQ", | ||
| 14 | + "onMenuShareWeibo", | ||
| 15 | + "onMenuShareQZone", | ||
| 16 | + "startRecord", | ||
| 17 | + "stopRecord", | ||
| 18 | + "onVoiceRecordEnd", | ||
| 19 | + "playVoice", | ||
| 20 | + "pauseVoice", | ||
| 21 | + "stopVoice", | ||
| 22 | + "onVoicePlayEnd", | ||
| 23 | + "uploadVoice", | ||
| 24 | + "downloadVoice", | ||
| 25 | + "chooseImage", | ||
| 26 | + "previewImage", | ||
| 27 | + "uploadImage", | ||
| 28 | + "downloadImage", | ||
| 29 | + "translateVoice", | ||
| 30 | + "getNetworkType", | ||
| 31 | + "openLocation", | ||
| 32 | + "getLocation", | ||
| 33 | + "hideOptionMenu", | ||
| 34 | + "showOptionMenu", | ||
| 35 | + "hideMenuItems", | ||
| 36 | + "showMenuItems", | ||
| 37 | + "hideAllNonBaseMenuItem", | ||
| 38 | + "showAllNonBaseMenuItem", | ||
| 39 | + "closeWindow", | ||
| 40 | + "scanQRCode", | ||
| 41 | + "chooseWXPay", | ||
| 42 | + "openProductSpecificView", | ||
| 43 | + "addCard", | ||
| 44 | + "chooseCard", | ||
| 45 | + "openCard" | ||
| 46 | +] |
src/api/wx/pay.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Author: hookehuyr hookehuyr@gmail.com | ||
| 3 | + * @Date: 2022-06-09 13:32:44 | ||
| 4 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 5 | + * @LastEditTime: 2022-06-09 13:42:06 | ||
| 6 | + * @FilePath: /tswj/src/api/wx/config.js | ||
| 7 | + * @Description: | ||
| 8 | + */ | ||
| 9 | +import { fn, fetch } from '@/api/fn'; | ||
| 10 | + | ||
| 11 | +const Api = { | ||
| 12 | + WX_PAY: 'c/bill_paymentForBill.do', | ||
| 13 | +} | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * @description 微信支付接口 | ||
| 17 | + * @param {*} | ||
| 18 | + * @returns {*} | ||
| 19 | + */ | ||
| 20 | +export const wxPayAPI = (params) => fn(fetch.get(Api.WX_PAY, params)); |
src/app.config.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2025-06-28 10:33:00 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-06-28 11:05:47 | ||
| 5 | + * @FilePath: /myApp/src/app.config.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +export default { | ||
| 9 | + pages: [ | ||
| 10 | + 'pages/index/index', | ||
| 11 | + 'pages/auth/index', | ||
| 12 | + ], | ||
| 13 | + subpackages: [ // 配置在tabBar中的页面不能分包写到subpackages中去 | ||
| 14 | + { | ||
| 15 | + root: 'pages/demo', | ||
| 16 | + pages: ['index'], | ||
| 17 | + }, | ||
| 18 | + ], | ||
| 19 | + window: { | ||
| 20 | + backgroundTextStyle: 'light', | ||
| 21 | + navigationBarBackgroundColor: '#fff', | ||
| 22 | + navigationBarTitleText: 'WeChat', | ||
| 23 | + navigationBarTextStyle: 'black' | ||
| 24 | + } | ||
| 25 | +} |
src/app.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2025-06-28 10:33:00 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-06-28 11:04:17 | ||
| 5 | + * @FilePath: /myApp/src/app.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +import { createApp } from 'vue' | ||
| 9 | +import { createPinia } from 'pinia' | ||
| 10 | +import './app.less' | ||
| 11 | +import { routerStore } from '@/stores/router' | ||
| 12 | +import Taro from '@tarojs/taro' | ||
| 13 | + | ||
| 14 | +const App = createApp({ | ||
| 15 | + // 对应 onLaunch | ||
| 16 | + onLaunch(options) { | ||
| 17 | + // 未授权状态跳转授权页面,首页不需要权限 | ||
| 18 | + const path = options.path; | ||
| 19 | + const query = options.query; | ||
| 20 | + // 缓存没有权限的地址 | ||
| 21 | + const router = routerStore(); | ||
| 22 | + router.add(path); | ||
| 23 | + // if (path !== 'pages/index/index' && !wx.getStorageSync("sessionid")) { | ||
| 24 | + if (!wx.getStorageSync("sessionid")) { | ||
| 25 | + console.warn("没有权限"); | ||
| 26 | + // if (path === 'pages/detail/index') { | ||
| 27 | + // Taro.navigateTo({ | ||
| 28 | + // url: `./pages/auth/index?url=${path}&id=${query.id}&start_date=${query.start_date}&end_date=${query.end_date}`, | ||
| 29 | + // }) | ||
| 30 | + // } else { | ||
| 31 | + // Taro.navigateTo({ | ||
| 32 | + // url: './pages/auth/index?url=' + path, | ||
| 33 | + // }) | ||
| 34 | + // } | ||
| 35 | + } | ||
| 36 | + }, | ||
| 37 | + onShow(options) { | ||
| 38 | + }, | ||
| 39 | + // 入口组件不需要实现 render 方法,即使实现了也会被 taro 所覆盖 | ||
| 40 | +}); | ||
| 41 | + | ||
| 42 | +App.use(createPinia()) | ||
| 43 | + | ||
| 44 | +export default App |
src/app.less
0 → 100644
src/assets/images/arrow-down.png
0 → 100644
439 Bytes
src/assets/images/avatar.png
0 → 100644
650 Bytes
src/assets/images/icon/icon_book1@2x.png
0 → 100644
1.36 KB
src/assets/images/icon/icon_book2@2x.png
0 → 100644
1.27 KB
src/assets/images/icon/icon_home1@2x.png
0 → 100644
1.62 KB
src/assets/images/icon/icon_home2@2x.png
0 → 100644
1.49 KB
src/assets/images/icon/icon_my1@2x.png
0 → 100644
990 Bytes
src/assets/images/icon/icon_my2@2x.png
0 → 100644
910 Bytes
src/assets/images/icon/icon_server1.png
0 → 100644
1.25 KB
src/assets/images/icon/icon_server2.png
0 → 100644
1.17 KB
src/assets/images/icon_checked@2x.png
0 → 100644
19.4 KB
src/assets/styles/base.less
0 → 100644
| 1 | +@namespace: 'meihua'; | ||
| 2 | + | ||
| 3 | +/* ============ 颜色 ============ */ | ||
| 4 | + | ||
| 5 | +// 主色调 | ||
| 6 | +@base-color: #199A74; | ||
| 7 | + | ||
| 8 | +// 文字颜色 | ||
| 9 | +@base-font-color: #333333; | ||
| 10 | +@sub-font-color: #999999; | ||
| 11 | + | ||
| 12 | +// 定义一个映射 | ||
| 13 | +#colors() { | ||
| 14 | + base-color: @base-color; | ||
| 15 | + base-font-color: @base-font-color; | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +// 混合 | ||
| 19 | +.width100 { | ||
| 20 | + width: 100%; | ||
| 21 | +} |
src/components/PosterBuilder/index.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <canvas | ||
| 3 | + type="2d" | ||
| 4 | + :id="canvasId" | ||
| 5 | + :style="`height: ${height}rpx; width:${width}rpx; | ||
| 6 | + position: absolute; | ||
| 7 | + ${debug ? '' : 'transform:translate3d(-9999rpx, 0, 0)'}`" | ||
| 8 | + /> | ||
| 9 | +</template> | ||
| 10 | +<script lang="ts"> | ||
| 11 | +import Taro from "@tarojs/taro" | ||
| 12 | +import { defineComponent, onMounted, PropType, ref } from "vue" | ||
| 13 | +import { Image, DrawConfig } from "./types" | ||
| 14 | +import { drawImage, drawText, drawBlock, drawLine } from "./utils/draw" | ||
| 15 | +import { | ||
| 16 | + toPx, | ||
| 17 | + toRpx, | ||
| 18 | + getRandomId, | ||
| 19 | + getImageInfo, | ||
| 20 | + getLinearColor, | ||
| 21 | +} from "./utils/tools" | ||
| 22 | + | ||
| 23 | +export default defineComponent({ | ||
| 24 | + name: "PosterBuilder", | ||
| 25 | + props: { | ||
| 26 | + showLoading: { | ||
| 27 | + type: Boolean, | ||
| 28 | + default: false, | ||
| 29 | + }, | ||
| 30 | + config: { | ||
| 31 | + type: Object as PropType<DrawConfig>, | ||
| 32 | + default: () => ({}), | ||
| 33 | + }, | ||
| 34 | + }, | ||
| 35 | + emits: ["success", "fail"], | ||
| 36 | + setup(props, context) { | ||
| 37 | + const count = ref(1) | ||
| 38 | + const { | ||
| 39 | + width, | ||
| 40 | + height, | ||
| 41 | + backgroundColor, | ||
| 42 | + texts = [], | ||
| 43 | + blocks = [], | ||
| 44 | + lines = [], | ||
| 45 | + debug = false, | ||
| 46 | + } = props.config || {} | ||
| 47 | + | ||
| 48 | + const canvasId = getRandomId() | ||
| 49 | + | ||
| 50 | + /** | ||
| 51 | + * step1: 初始化图片资源 | ||
| 52 | + * @param {Array} images = imgTask | ||
| 53 | + * @return {Promise} downloadImagePromise | ||
| 54 | + */ | ||
| 55 | + const initImages = (images: Image[]) => { | ||
| 56 | + const imagesTemp = images.filter((item) => item.url) | ||
| 57 | + const drawList = imagesTemp.map((item, index) => | ||
| 58 | + getImageInfo(item, index) | ||
| 59 | + ) | ||
| 60 | + return Promise.all(drawList) | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * step2: 初始化 canvas && 获取其 dom 节点和实例 | ||
| 65 | + * @return {Promise} resolve 里返回其 dom 和实例 | ||
| 66 | + */ | ||
| 67 | + const initCanvas = () => | ||
| 68 | + new Promise<any>((resolve) => { | ||
| 69 | + setTimeout(() => { | ||
| 70 | + const pageInstance = Taro.getCurrentInstance()?.page || {} // 拿到当前页面实例 | ||
| 71 | + const query = Taro.createSelectorQuery().in(pageInstance) // 确定在当前页面内匹配子元素 | ||
| 72 | + query | ||
| 73 | + .select(`#${canvasId}`) | ||
| 74 | + .fields({ node: true, size: true, context: true }, (res) => { | ||
| 75 | + const canvas = res.node | ||
| 76 | + const ctx = canvas.getContext("2d") | ||
| 77 | + resolve({ ctx, canvas }) | ||
| 78 | + }) | ||
| 79 | + .exec() | ||
| 80 | + }, 300) | ||
| 81 | + }) | ||
| 82 | + | ||
| 83 | + /** | ||
| 84 | + * @description 保存绘制的图片 | ||
| 85 | + * @param { object } config | ||
| 86 | + */ | ||
| 87 | + const getTempFile = (canvas) => { | ||
| 88 | + Taro.canvasToTempFilePath( | ||
| 89 | + { | ||
| 90 | + canvas, | ||
| 91 | + success: (result) => { | ||
| 92 | + Taro.hideLoading() | ||
| 93 | + context.emit("success", result) | ||
| 94 | + }, | ||
| 95 | + fail: (error) => { | ||
| 96 | + const { errMsg } = error | ||
| 97 | + if (errMsg === "canvasToTempFilePath:fail:create bitmap failed") { | ||
| 98 | + count.value += 1 | ||
| 99 | + if (count.value <= 3) { | ||
| 100 | + getTempFile(canvas) | ||
| 101 | + } else { | ||
| 102 | + Taro.hideLoading() | ||
| 103 | + Taro.showToast({ | ||
| 104 | + icon: "none", | ||
| 105 | + title: errMsg || "绘制海报失败", | ||
| 106 | + }) | ||
| 107 | + context.emit("fail", errMsg) | ||
| 108 | + } | ||
| 109 | + } | ||
| 110 | + }, | ||
| 111 | + }, | ||
| 112 | + context | ||
| 113 | + ) | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + /** | ||
| 117 | + * step2: 开始绘制任务 | ||
| 118 | + * @param { Array } drawTasks 待绘制任务 | ||
| 119 | + */ | ||
| 120 | + const startDrawing = async (drawTasks) => { | ||
| 121 | + // TODO: check | ||
| 122 | + // const configHeight = getHeight(config) | ||
| 123 | + const { ctx, canvas } = await initCanvas() | ||
| 124 | + | ||
| 125 | + canvas.width = width | ||
| 126 | + canvas.height = height | ||
| 127 | + | ||
| 128 | + // 设置画布底色 | ||
| 129 | + if (backgroundColor) { | ||
| 130 | + ctx.save() // 保存绘图上下文 | ||
| 131 | + const grd = getLinearColor(ctx, backgroundColor, 0, 0, width, height) | ||
| 132 | + ctx.fillStyle = grd // 设置填充颜色 | ||
| 133 | + ctx.fillRect(0, 0, width, height) // 填充一个矩形 | ||
| 134 | + ctx.restore() // 恢复之前保存的绘图上下文 | ||
| 135 | + } | ||
| 136 | + // 将要画的方块、文字、线条放进队列数组 | ||
| 137 | + const queue = drawTasks | ||
| 138 | + .concat( | ||
| 139 | + texts.map((item) => { | ||
| 140 | + item.type = "text" | ||
| 141 | + item.zIndex = item.zIndex || 0 | ||
| 142 | + return item | ||
| 143 | + }) | ||
| 144 | + ) | ||
| 145 | + .concat( | ||
| 146 | + blocks.map((item) => { | ||
| 147 | + item.type = "block" | ||
| 148 | + item.zIndex = item.zIndex || 0 | ||
| 149 | + return item | ||
| 150 | + }) | ||
| 151 | + ) | ||
| 152 | + .concat( | ||
| 153 | + lines.map((item) => { | ||
| 154 | + item.type = "line" | ||
| 155 | + item.zIndex = item.zIndex || 0 | ||
| 156 | + return item | ||
| 157 | + }) | ||
| 158 | + ) | ||
| 159 | + | ||
| 160 | + queue.sort((a, b) => a.zIndex - b.zIndex) // 按照层叠顺序由低至高排序, 先画低的,再画高的 | ||
| 161 | + for (let i = 0; i < queue.length; i++) { | ||
| 162 | + const drawOptions = { | ||
| 163 | + canvas, | ||
| 164 | + ctx, | ||
| 165 | + toPx, | ||
| 166 | + toRpx, | ||
| 167 | + } | ||
| 168 | + if (queue[i].type === "image") { | ||
| 169 | + await drawImage(queue[i], drawOptions) | ||
| 170 | + } else if (queue[i].type === "text") { | ||
| 171 | + drawText(queue[i], drawOptions) | ||
| 172 | + } else if (queue[i].type === "block") { | ||
| 173 | + drawBlock(queue[i], drawOptions) | ||
| 174 | + } else if (queue[i].type === "line") { | ||
| 175 | + drawLine(queue[i], drawOptions) | ||
| 176 | + } | ||
| 177 | + } | ||
| 178 | + | ||
| 179 | + setTimeout(() => { | ||
| 180 | + getTempFile(canvas) // 需要做延时才能能正常加载图片 | ||
| 181 | + }, 300) | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | + // start: 初始化 canvas 实例 && 下载图片资源 | ||
| 185 | + const init = () => { | ||
| 186 | + if (props.showLoading) | ||
| 187 | + Taro.showLoading({ mask: true, title: "生成中..." }) | ||
| 188 | + if (props.config?.images?.length) { | ||
| 189 | + initImages(props.config.images) | ||
| 190 | + .then((result) => { | ||
| 191 | + // 1. 下载图片资源 | ||
| 192 | + startDrawing(result) | ||
| 193 | + }) | ||
| 194 | + .catch((err) => { | ||
| 195 | + Taro.hideLoading() | ||
| 196 | + Taro.showToast({ | ||
| 197 | + icon: "none", | ||
| 198 | + title: err.errMsg || "下载图片失败", | ||
| 199 | + }) | ||
| 200 | + context.emit("fail", err) | ||
| 201 | + }) | ||
| 202 | + } else { | ||
| 203 | + startDrawing([]) | ||
| 204 | + } | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | + onMounted(() => { | ||
| 208 | + init() | ||
| 209 | + }) | ||
| 210 | + | ||
| 211 | + return { | ||
| 212 | + canvasId, | ||
| 213 | + debug, | ||
| 214 | + width, | ||
| 215 | + height, | ||
| 216 | + } | ||
| 217 | + }, | ||
| 218 | +}) | ||
| 219 | +</script> |
src/components/PosterBuilder/types.d.ts
0 → 100644
| 1 | +export type DrawType = 'text' | 'image' | 'block' | 'line'; | ||
| 2 | + | ||
| 3 | +export interface Block { | ||
| 4 | + type?: DrawType; | ||
| 5 | + x: number; | ||
| 6 | + y: number; | ||
| 7 | + width?: number; | ||
| 8 | + height: number; | ||
| 9 | + paddingLeft?: number; | ||
| 10 | + paddingRight?: number; | ||
| 11 | + borderWidth?: number; | ||
| 12 | + borderColor?: string; | ||
| 13 | + backgroundColor?: string; | ||
| 14 | + borderRadius?: number; | ||
| 15 | + borderRadiusGroup?: number[]; | ||
| 16 | + text?: Text; | ||
| 17 | + opacity?: number; | ||
| 18 | + zIndex?: number; | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +export interface Text { | ||
| 22 | + type?: DrawType; | ||
| 23 | + x?: number; | ||
| 24 | + y?: number; | ||
| 25 | + text: string | Text[]; | ||
| 26 | + fontSize?: number; | ||
| 27 | + color?: string; | ||
| 28 | + opacity?: 1 | 0; | ||
| 29 | + lineHeight?: number; | ||
| 30 | + lineNum?: number; | ||
| 31 | + width?: number; | ||
| 32 | + marginTop?: number; | ||
| 33 | + marginLeft?: number; | ||
| 34 | + marginRight?: number; | ||
| 35 | + textDecoration?: 'line-through' | 'none'; | ||
| 36 | + baseLine?: 'top' | 'middle' | 'bottom'; | ||
| 37 | + textAlign?: 'left' | 'center' | 'right'; | ||
| 38 | + fontFamily?: string; | ||
| 39 | + fontWeight?: string; | ||
| 40 | + fontStyle?: string; | ||
| 41 | + zIndex?: number; | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +export interface Image { | ||
| 45 | + type?: DrawType; | ||
| 46 | + x: number; | ||
| 47 | + y: number; | ||
| 48 | + url: string; | ||
| 49 | + width: number; | ||
| 50 | + height: number; | ||
| 51 | + borderRadius?: number; | ||
| 52 | + borderRadiusGroup?: number[]; | ||
| 53 | + borderWidth?: number; | ||
| 54 | + borderColor?: string; | ||
| 55 | + zIndex?: number; | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | +export interface Line { | ||
| 59 | + type?: DrawType; | ||
| 60 | + startX: number; | ||
| 61 | + startY: number; | ||
| 62 | + endX: number; | ||
| 63 | + endY: number; | ||
| 64 | + width: number; | ||
| 65 | + color?: string; | ||
| 66 | + zIndex?: number; | ||
| 67 | +} | ||
| 68 | + | ||
| 69 | +export type DrawConfig = { | ||
| 70 | + width: number; | ||
| 71 | + height: number; | ||
| 72 | + backgroundColor?: string; | ||
| 73 | + debug?: boolean; | ||
| 74 | + blocks?: Block[]; | ||
| 75 | + texts?: Text[]; | ||
| 76 | + images?: Image[]; | ||
| 77 | + lines?: Line[]; | ||
| 78 | +}; |
src/components/PosterBuilder/utils/draw.ts
0 → 100644
| 1 | +/* eslint-disable no-underscore-dangle */ | ||
| 2 | +import { getLinearColor, getTextX, toPx } from './tools'; | ||
| 3 | + | ||
| 4 | +/** | ||
| 5 | + * 绘制圆角矩形 | ||
| 6 | + * @param { object } drawData - 绘制数据 | ||
| 7 | + * @param { number } drawData.x - 左上角x坐标 | ||
| 8 | + * @param { number } drawData.y - 左上角y坐标 | ||
| 9 | + * @param { number } drawData.w - 矩形的宽 | ||
| 10 | + * @param { number } drawData.h - 矩形的高 | ||
| 11 | + * @param { number } drawData.r - 圆角半径 | ||
| 12 | + * @param { object } drawOptions - 绘制对象 | ||
| 13 | + * @param { object } drawOptions.ctx - ctx对象 | ||
| 14 | + * @description arcTo 比 arc 更加简洁,三点画弧,但是比较难理解 参考资料:http://www.yanghuiqing.com/web/346 | ||
| 15 | + * ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise(是否逆时针画弧)) | ||
| 16 | + * ctx.arcTo(x1, y1, x2, y2, radius); // 当前点-x1点 画切线 x1点到x2点画切线, 用半径为radius的圆弧替换掉切线部分 | ||
| 17 | + */ | ||
| 18 | +export function _drawRadiusRect({ x, y, w, h, r }, { ctx }) { | ||
| 19 | + const minSize = Math.min(w, h); | ||
| 20 | + if (r > minSize / 2) r = minSize / 2; | ||
| 21 | + ctx.beginPath(); | ||
| 22 | + ctx.moveTo(x + r, y); | ||
| 23 | + ctx.arcTo(x + w, y, x + w, y + h, r); // 绘制上边框和右上角弧线 | ||
| 24 | + ctx.arcTo(x + w, y + h, x, y + h, r); // 绘制右边框和右下角弧线 | ||
| 25 | + ctx.arcTo(x, y + h, x, y, r); // 绘制下边框和左下角弧线 | ||
| 26 | + ctx.arcTo(x, y, x + w, y, r); // 绘制左边框和左上角弧线 | ||
| 27 | + ctx.closePath(); | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +/** | ||
| 31 | + * 绘制圆角矩形 | ||
| 32 | + * @param { object } drawData - 绘制数据 | ||
| 33 | + * @param { number } drawData.x - 左上角x坐标 | ||
| 34 | + * @param { number } drawData.y - 左上角y坐标 | ||
| 35 | + * @param { number } drawData.w - 矩形的宽 | ||
| 36 | + * @param { number } drawData.h - 矩形的高 | ||
| 37 | + * @param { number } drawData.g - 圆角半径数组 | ||
| 38 | + * @param { object } drawOptions - 绘制对象 | ||
| 39 | + * @param { object } drawOptions.ctx - ctx对象 | ||
| 40 | + */ | ||
| 41 | +export function _drawRadiusGroupRect({ x, y, w, h, g }, { ctx }) { | ||
| 42 | + const [ | ||
| 43 | + borderTopLeftRadius, | ||
| 44 | + borderTopRightRadius, | ||
| 45 | + borderBottomRightRadius, | ||
| 46 | + borderBottomLeftRadius | ||
| 47 | + ] = g; | ||
| 48 | + ctx.beginPath(); | ||
| 49 | + ctx.arc( | ||
| 50 | + x + w - borderBottomRightRadius, | ||
| 51 | + y + h - borderBottomRightRadius, | ||
| 52 | + borderBottomRightRadius, | ||
| 53 | + 0, | ||
| 54 | + Math.PI * 0.5 | ||
| 55 | + ); | ||
| 56 | + ctx.lineTo(x + borderBottomLeftRadius, y + h); | ||
| 57 | + // 左下角 | ||
| 58 | + ctx.arc( | ||
| 59 | + x + borderBottomLeftRadius, | ||
| 60 | + y + h - borderBottomLeftRadius, | ||
| 61 | + borderBottomLeftRadius, | ||
| 62 | + Math.PI * 0.5, | ||
| 63 | + Math.PI | ||
| 64 | + ); | ||
| 65 | + ctx.lineTo(x, y + borderTopLeftRadius); | ||
| 66 | + // 左上角 | ||
| 67 | + ctx.arc( | ||
| 68 | + x + borderTopLeftRadius, | ||
| 69 | + y + borderTopLeftRadius, | ||
| 70 | + borderTopLeftRadius, | ||
| 71 | + Math.PI, | ||
| 72 | + Math.PI * 1.5 | ||
| 73 | + ); | ||
| 74 | + ctx.lineTo(x + w - borderTopRightRadius, y); | ||
| 75 | + // 右上角 | ||
| 76 | + ctx.arc( | ||
| 77 | + x + w - borderTopRightRadius, | ||
| 78 | + y + borderTopRightRadius, | ||
| 79 | + borderTopRightRadius, | ||
| 80 | + Math.PI * 1.5, | ||
| 81 | + Math.PI * 2 | ||
| 82 | + ); | ||
| 83 | + ctx.lineTo(x + w, y + h - borderBottomRightRadius); | ||
| 84 | + // ctx.arcTo(x + w, y, x + w, y + h, r); // 绘制上边框和右上角弧线 | ||
| 85 | + // ctx.arcTo(x + w, y + h, x, y + h, r); // 绘制右边框和右下角弧线 | ||
| 86 | + // ctx.arcTo(x, y + h, x, y, r); // 绘制下边框和左下角弧线 | ||
| 87 | + // ctx.arcTo(x, y, x + w, y, r); // 绘制左边框和左上角弧线 | ||
| 88 | + ctx.closePath(); | ||
| 89 | +} | ||
| 90 | + | ||
| 91 | +/** | ||
| 92 | + * 计算文本长度 | ||
| 93 | + * @param { Array | Object } text 数组 或者 对象 | ||
| 94 | + * @param { object } drawOptions - 绘制对象 | ||
| 95 | + * @param { object } drawOptions.ctx - ctx对象 | ||
| 96 | + */ | ||
| 97 | +export function _getTextWidth(text, drawOptions) { | ||
| 98 | + const { ctx } = drawOptions; | ||
| 99 | + let texts: any[] = []; | ||
| 100 | + if (Object.prototype.toString.call(text) === '[object Object]') { | ||
| 101 | + texts.push(text); | ||
| 102 | + } else { | ||
| 103 | + texts = text; | ||
| 104 | + } | ||
| 105 | + let width = 0; | ||
| 106 | + texts.forEach( | ||
| 107 | + ({ | ||
| 108 | + fontSize, | ||
| 109 | + text: textStr, | ||
| 110 | + fontStyle = 'normal', | ||
| 111 | + fontWeight = 'normal', | ||
| 112 | + fontFamily = 'sans-serif', | ||
| 113 | + marginLeft = 0, | ||
| 114 | + marginRight = 0 | ||
| 115 | + }) => { | ||
| 116 | + ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`; | ||
| 117 | + width += ctx.measureText(textStr).width + marginLeft + marginRight; | ||
| 118 | + } | ||
| 119 | + ); | ||
| 120 | + return width; | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +/** | ||
| 124 | + * 渲染一段文字 | ||
| 125 | + * @param { object } drawData - 绘制数据 | ||
| 126 | + * @param { number } drawData.x - x坐标 rpx | ||
| 127 | + * @param { number } drawData.y - y坐标 rpx | ||
| 128 | + * @param { number } drawData.fontSize - 文字大小 rpx | ||
| 129 | + * @param { number } [drawData.color] - 颜色 | ||
| 130 | + * @param { string } [drawData.baseLine] - 基线对齐方式 top| middle|bottom|... | ||
| 131 | + * @param { string } [drawData.textAlign='left'] - 对齐方式 left|center|right | ||
| 132 | + * @param { string } drawData.text - 当Object类型时,参数为 text 字段的参数,marginLeft、marginRight这两个字段可用 | ||
| 133 | + * @param { number } [drawData.opacity=1] - 1为不透明,0为透明 | ||
| 134 | + * @param { string } [drawData.textDecoration='none'] | ||
| 135 | + * @param { number } [drawData.width] - 文字宽度 没有指定为画布宽度 | ||
| 136 | + * @param { number } [drawData.lineNum=1] - 根据宽度换行,最多的行数 | ||
| 137 | + * @param { number } [drawData.lineHeight=0] - 行高 | ||
| 138 | + * @param { string } [drawData.fontWeight='normal'] - 'bold' 加粗字体,目前小程序不支持 100 - 900 加粗 | ||
| 139 | + * @param { string } [drawData.fontStyle='normal'] - 'italic' 倾斜字体 | ||
| 140 | + * @param { string } [drawData.fontFamily="sans-serif"] - 小程序默认字体为 'sans-serif', 请输入小程序支持的字体 | ||
| 141 | + * | ||
| 142 | + * @param { object } drawOptions - 绘制对象 | ||
| 143 | + * @param { object } drawOptions.ctx - ctx对象 | ||
| 144 | + */ | ||
| 145 | +export function _drawSingleText(drawData, drawOptions) { | ||
| 146 | + const { | ||
| 147 | + x = 0, | ||
| 148 | + y = 0, | ||
| 149 | + text, | ||
| 150 | + color, | ||
| 151 | + width, | ||
| 152 | + fontSize = 28, | ||
| 153 | + baseLine = 'top', | ||
| 154 | + textAlign = 'left', | ||
| 155 | + opacity = 1, | ||
| 156 | + textDecoration = 'none', | ||
| 157 | + lineNum = 1, | ||
| 158 | + lineHeight = 0, | ||
| 159 | + fontWeight = 'normal', | ||
| 160 | + fontStyle = 'normal', | ||
| 161 | + fontFamily = 'sans-serif' | ||
| 162 | + } = drawData; | ||
| 163 | + const { ctx } = drawOptions; | ||
| 164 | + // 画笔初始化 | ||
| 165 | + ctx.save(); | ||
| 166 | + ctx.beginPath(); | ||
| 167 | + ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`; | ||
| 168 | + ctx.globalAlpha = opacity; | ||
| 169 | + ctx.fillStyle = color; | ||
| 170 | + ctx.textBaseline = baseLine; | ||
| 171 | + ctx.textAlign = textAlign; | ||
| 172 | + let textWidth = ctx.measureText(text).width; // 测量文本宽度 | ||
| 173 | + const textArr: string[] = []; | ||
| 174 | + | ||
| 175 | + // 文本超出换行 | ||
| 176 | + if (textWidth > width) { | ||
| 177 | + // 如果超出一行 ,则判断要分为几行 | ||
| 178 | + let fillText = ''; // 当前行已拼接的文字 | ||
| 179 | + let line = 1; // 当前是第几行 | ||
| 180 | + for (let i = 0; i <= text.length - 1; i++) { | ||
| 181 | + // 将文字转为数组,一行文字一个元素 | ||
| 182 | + fillText += text[i]; // 当前已拼接文字串 | ||
| 183 | + const nextText = i < text.length - 1 ? fillText + text[i + 1] : fillText; // 再拼接下一个文字 | ||
| 184 | + const restWidth = width - ctx.measureText(nextText).width; // 拼接下一个文字后的剩余宽度 | ||
| 185 | + | ||
| 186 | + if (restWidth < 0) { | ||
| 187 | + // 如果拼接下一个字就超出宽度则添加者省略号或者换行 | ||
| 188 | + if (line === lineNum) { | ||
| 189 | + // 已经是最后一行,就拼接省略号 | ||
| 190 | + if ( | ||
| 191 | + restWidth + ctx.measureText(text[i + 1]).width > | ||
| 192 | + ctx.measureText('...').width | ||
| 193 | + ) { | ||
| 194 | + // 剩余宽度能否放下省略号 | ||
| 195 | + fillText = `${fillText}...`; | ||
| 196 | + } else { | ||
| 197 | + fillText = `${fillText.substr(0, fillText.length - 1)}...`; | ||
| 198 | + } | ||
| 199 | + textArr.push(fillText); | ||
| 200 | + break; | ||
| 201 | + } else { | ||
| 202 | + // 如果不是最后一行,就换行 | ||
| 203 | + textArr.push(fillText); | ||
| 204 | + line++; | ||
| 205 | + fillText = ''; | ||
| 206 | + } | ||
| 207 | + } else if (i === text.length - 1) { | ||
| 208 | + textArr.push(fillText); | ||
| 209 | + } | ||
| 210 | + } | ||
| 211 | + textWidth = width; | ||
| 212 | + } else { | ||
| 213 | + textArr.push(text); | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + // 按行渲染文字 | ||
| 217 | + textArr.forEach((item, index) => | ||
| 218 | + ctx.fillText( | ||
| 219 | + item, | ||
| 220 | + getTextX(textAlign, x, width), // 根据文本对齐方式和宽度确定 x 坐标 | ||
| 221 | + y + (lineHeight || fontSize) * index // 根据行数、行高 || 字体大小确定 y 坐标 | ||
| 222 | + ) | ||
| 223 | + ); | ||
| 224 | + ctx.restore(); | ||
| 225 | + | ||
| 226 | + // 文本修饰,下划线、删除线什么的 | ||
| 227 | + if (textDecoration !== 'none') { | ||
| 228 | + let lineY = y; | ||
| 229 | + if (textDecoration === 'line-through') { | ||
| 230 | + // 目前只支持贯穿线 | ||
| 231 | + lineY = y; | ||
| 232 | + } | ||
| 233 | + ctx.save(); | ||
| 234 | + ctx.moveTo(x, lineY); | ||
| 235 | + ctx.lineTo(x + textWidth, lineY); | ||
| 236 | + ctx.strokeStyle = color; | ||
| 237 | + ctx.stroke(); | ||
| 238 | + ctx.restore(); | ||
| 239 | + } | ||
| 240 | + return textWidth; | ||
| 241 | +} | ||
| 242 | + | ||
| 243 | +/** | ||
| 244 | + * 渲染文字 | ||
| 245 | + * @param { object } params - 绘制数据 | ||
| 246 | + * @param { number } params.x - x坐标 rpx | ||
| 247 | + * @param { number } params.y - y坐标 rpx | ||
| 248 | + * @param { number } params.fontSize - 文字大小 rpx | ||
| 249 | + * @param { number } [params.color] - 颜色 | ||
| 250 | + * @param { string } [params.baseLine] - 基线对齐方式 top| middle|bottom | ||
| 251 | + * @param { string } [params.textAlign='left'] - 对齐方式 left|center|right | ||
| 252 | + * @param { string } params.text - 当Object类型时,参数为 text 字段的参数,marginLeft、marginRight这两个字段可用 | ||
| 253 | + * @param { number } [params.opacity=1] - 1为不透明,0为透明 | ||
| 254 | + * @param { string } [params.textDecoration='none'] | ||
| 255 | + * @param { number } [params.width] - 文字宽度 没有指定为画布宽度 | ||
| 256 | + * @param { number } [params.lineNum=1] - 根据宽度换行,最多的行数 | ||
| 257 | + * @param { number } [params.lineHeight=0] - 行高 | ||
| 258 | + * @param { string } [params.fontWeight='normal'] - 'bold' 加粗字体,目前小程序不支持 100 - 900 加粗 | ||
| 259 | + * @param { string } [params.fontStyle='normal'] - 'italic' 倾斜字体 | ||
| 260 | + * @param { string } [params.fontFamily="sans-serif"] - 小程序默认字体为 'sans-serif', 请输入小程序支持的字体 | ||
| 261 | + * | ||
| 262 | + * @param { object } drawOptions - 绘制对象 | ||
| 263 | + * @param { object } drawOptions.ctx - ctx对象 | ||
| 264 | + */ | ||
| 265 | +export function drawText(params, drawOptions) { | ||
| 266 | + const { x = 0, y = 0, text, baseLine } = params; | ||
| 267 | + if (Object.prototype.toString.call(text) === '[object Array]') { | ||
| 268 | + const preText = { x, y, baseLine }; | ||
| 269 | + | ||
| 270 | + // 遍历多行文字,一行一行渲染 | ||
| 271 | + text.forEach((item) => { | ||
| 272 | + preText.x += item.marginLeft || 0; | ||
| 273 | + // TODO:多段文字超出一行的处理 | ||
| 274 | + const textWidth = _drawSingleText( | ||
| 275 | + Object.assign(item, { ...preText, y: y + (item.marginTop || 0) }), | ||
| 276 | + drawOptions | ||
| 277 | + ); | ||
| 278 | + preText.x += textWidth + (item.marginRight || 0); // 下一段文字的 x 坐标为上一段字 x坐标 + 文字宽度 + marginRight | ||
| 279 | + }); | ||
| 280 | + } else { | ||
| 281 | + _drawSingleText(params, drawOptions); | ||
| 282 | + } | ||
| 283 | +} | ||
| 284 | + | ||
| 285 | +/** | ||
| 286 | + * @description 渲染线 | ||
| 287 | + * @param { number } startX - 起始坐标 | ||
| 288 | + * @param { number } startY - 起始坐标 | ||
| 289 | + * @param { number } endX - 终结坐标 | ||
| 290 | + * @param { number } endY - 终结坐标 | ||
| 291 | + * @param { number } width - 线的宽度 | ||
| 292 | + * @param { string } [color] - 线的颜色 | ||
| 293 | + * | ||
| 294 | + * @param { object } drawOptions - 绘制对象 | ||
| 295 | + * @param { object } drawOptions.ctx - ctx对象 | ||
| 296 | + */ | ||
| 297 | +export function drawLine(drawData, drawOptions) { | ||
| 298 | + const { startX, startY, endX, endY, color, width } = drawData; | ||
| 299 | + const { ctx } = drawOptions; | ||
| 300 | + if (!width) return; | ||
| 301 | + ctx.save(); | ||
| 302 | + ctx.beginPath(); | ||
| 303 | + ctx.strokeStyle = color; | ||
| 304 | + ctx.lineWidth = width; | ||
| 305 | + ctx.moveTo(startX, startY); | ||
| 306 | + ctx.lineTo(endX, endY); | ||
| 307 | + ctx.stroke(); | ||
| 308 | + ctx.closePath(); | ||
| 309 | + ctx.restore(); | ||
| 310 | +} | ||
| 311 | + | ||
| 312 | +/** | ||
| 313 | + * 渲染矩形 | ||
| 314 | + * @param { number } x - x坐标 | ||
| 315 | + * @param { number } y - y坐标 | ||
| 316 | + * @param { number } height -高 | ||
| 317 | + * @param { string|object } [text] - 块里面可以填充文字,参考texts字段 | ||
| 318 | + * @param { number } [width=0] - 宽 如果内部有文字,由文字宽度和内边距决定 | ||
| 319 | + * @param { number } [paddingLeft=0] - 内左边距 | ||
| 320 | + * @param { number } [paddingRight=0] - 内右边距 | ||
| 321 | + * @param { number } [borderWidth] - 边框宽度 | ||
| 322 | + * @param { string } [backgroundColor] - 背景颜色 | ||
| 323 | + * @param { string } [borderColor] - 边框颜色 | ||
| 324 | + * @param { number } [borderRadius=0] - 圆角 | ||
| 325 | + * @param { array | null } [borderRadiusGroup= null] - 圆角数组 | ||
| 326 | + * @param { number } [opacity=1] - 透明度 | ||
| 327 | + * | ||
| 328 | + * @param { object } drawOptions - 绘制对象 | ||
| 329 | + * @param { object } drawOptions.ctx - ctx对象 | ||
| 330 | + */ | ||
| 331 | +export function drawBlock(data, drawOptions) { | ||
| 332 | + const { | ||
| 333 | + x, | ||
| 334 | + y, | ||
| 335 | + text, | ||
| 336 | + width = 0, | ||
| 337 | + height, | ||
| 338 | + opacity = 1, | ||
| 339 | + paddingLeft = 0, | ||
| 340 | + paddingRight = 0, | ||
| 341 | + borderWidth, | ||
| 342 | + backgroundColor, | ||
| 343 | + borderColor, | ||
| 344 | + borderRadius = 0, | ||
| 345 | + borderRadiusGroup = null | ||
| 346 | + } = data || {}; | ||
| 347 | + const { ctx } = drawOptions; | ||
| 348 | + ctx.save(); // 先保存画笔样式,等下恢复回来 | ||
| 349 | + ctx.globalAlpha = opacity; | ||
| 350 | + | ||
| 351 | + let blockWidth = 0; // 块的宽度 | ||
| 352 | + let textX = 0; | ||
| 353 | + let textY = 0; | ||
| 354 | + | ||
| 355 | + // 渲染块内文字 | ||
| 356 | + if (text) { | ||
| 357 | + // 如果文字宽度超出块宽度,则块的宽度为:文字的宽度 + 内边距 | ||
| 358 | + const textWidth = _getTextWidth( | ||
| 359 | + typeof text.text === 'string' ? text : text.text, | ||
| 360 | + drawOptions | ||
| 361 | + ); | ||
| 362 | + blockWidth = textWidth > width ? textWidth : width; | ||
| 363 | + blockWidth += paddingLeft + paddingLeft; | ||
| 364 | + | ||
| 365 | + const { textAlign = 'left' } = text; | ||
| 366 | + textY = y; // 文字默认定位在块的左上角 | ||
| 367 | + textX = x + paddingLeft; | ||
| 368 | + | ||
| 369 | + // 文字居中 | ||
| 370 | + if (textAlign === 'center') { | ||
| 371 | + textX = blockWidth / 2 + x; | ||
| 372 | + } else if (textAlign === 'right') { | ||
| 373 | + textX = x + blockWidth - paddingRight; | ||
| 374 | + } | ||
| 375 | + drawText(Object.assign(text, { x: textX, y: textY }), drawOptions); | ||
| 376 | + } else { | ||
| 377 | + blockWidth = width; | ||
| 378 | + } | ||
| 379 | + | ||
| 380 | + // 画矩形背景 | ||
| 381 | + if (backgroundColor) { | ||
| 382 | + const grd = getLinearColor(ctx, backgroundColor, x, y, blockWidth, height); | ||
| 383 | + ctx.fillStyle = grd; | ||
| 384 | + | ||
| 385 | + // 画圆角矩形 | ||
| 386 | + if (borderRadius > 0) { | ||
| 387 | + const drawData = { | ||
| 388 | + x, | ||
| 389 | + y, | ||
| 390 | + w: blockWidth, | ||
| 391 | + h: height, | ||
| 392 | + r: borderRadius | ||
| 393 | + }; | ||
| 394 | + _drawRadiusRect(drawData, drawOptions); | ||
| 395 | + ctx.fill(); // 填充路径 | ||
| 396 | + } else if (borderRadiusGroup) { | ||
| 397 | + const drawData = { | ||
| 398 | + x, | ||
| 399 | + y, | ||
| 400 | + w: blockWidth, | ||
| 401 | + h: height, | ||
| 402 | + g: borderRadiusGroup | ||
| 403 | + }; | ||
| 404 | + _drawRadiusGroupRect(drawData, drawOptions); | ||
| 405 | + ctx.fill(); // 填充路径 | ||
| 406 | + } else { | ||
| 407 | + ctx.fillRect(x, y, blockWidth, height); // 绘制矩形 | ||
| 408 | + } | ||
| 409 | + } | ||
| 410 | + | ||
| 411 | + // 画边框 | ||
| 412 | + if (borderWidth && borderRadius > 0) { | ||
| 413 | + ctx.strokeStyle = borderColor; | ||
| 414 | + ctx.lineWidth = borderWidth; | ||
| 415 | + if (borderRadius > 0) { | ||
| 416 | + // 画圆角矩形边框 | ||
| 417 | + const drawData = { | ||
| 418 | + x, | ||
| 419 | + y, | ||
| 420 | + w: blockWidth, | ||
| 421 | + h: height, | ||
| 422 | + r: borderRadius | ||
| 423 | + }; | ||
| 424 | + _drawRadiusRect(drawData, drawOptions); | ||
| 425 | + ctx.stroke(); | ||
| 426 | + } else { | ||
| 427 | + ctx.strokeRect(x, y, blockWidth, height); | ||
| 428 | + } | ||
| 429 | + } | ||
| 430 | + ctx.restore(); // 将 canvas 恢复到最近的保存状态的方法 | ||
| 431 | +} | ||
| 432 | + | ||
| 433 | +/** | ||
| 434 | + * @description 渲染图片 | ||
| 435 | + * @param { object } data | ||
| 436 | + * @param { number } sx - 源图像的矩形选择框的左上角 x 坐标 裁剪 | ||
| 437 | + * @param { number } sy - 源图像的矩形选择框的左上角 y 坐标 裁剪 | ||
| 438 | + * @param { number } sw - 源图像的矩形选择框的宽度 裁剪 | ||
| 439 | + * @param { number } sh - 源图像的矩形选择框的高度 裁剪 | ||
| 440 | + * @param { number } x - 图像的左上角在目标 canvas 上 x 轴的位置 定位 | ||
| 441 | + * @param { number } y - 图像的左上角在目标 canvas 上 y 轴的位置 定位 | ||
| 442 | + * @param { number } w - 在目标画布上绘制图像的宽度,允许对绘制的图像进行缩放 定位 | ||
| 443 | + * @param { number } h - 在目标画布上绘制图像的高度,允许对绘制的图像进行缩放 定位 | ||
| 444 | + * @param { number } [borderRadius=0] - 圆角 | ||
| 445 | + * @param { array | null } [borderRadiusGroup= null] - 圆角数组 | ||
| 446 | + * @param { number } [borderWidth=0] - 边框 | ||
| 447 | + * | ||
| 448 | + * @param { object } drawOptions - 绘制对象 | ||
| 449 | + * @param { object } drawOptions.ctx - ctx对象 | ||
| 450 | + */ | ||
| 451 | +export const drawImage = (data, drawOptions) => | ||
| 452 | + new Promise<void>((resolve) => { | ||
| 453 | + const { canvas, ctx } = drawOptions; | ||
| 454 | + const { | ||
| 455 | + x, | ||
| 456 | + y, | ||
| 457 | + w, | ||
| 458 | + h, | ||
| 459 | + sx, | ||
| 460 | + sy, | ||
| 461 | + sw, | ||
| 462 | + sh, | ||
| 463 | + imgPath, | ||
| 464 | + borderRadius = 0, | ||
| 465 | + borderWidth = 0, | ||
| 466 | + borderColor, | ||
| 467 | + borderRadiusGroup = null | ||
| 468 | + } = data; | ||
| 469 | + | ||
| 470 | + ctx.save(); | ||
| 471 | + if (borderRadius > 0) { | ||
| 472 | + _drawRadiusRect( | ||
| 473 | + { | ||
| 474 | + x, | ||
| 475 | + y, | ||
| 476 | + w, | ||
| 477 | + h, | ||
| 478 | + r: borderRadius | ||
| 479 | + }, | ||
| 480 | + drawOptions | ||
| 481 | + ); | ||
| 482 | + ctx.clip(); // 裁切,后续绘图限制在这个裁切范围内,保证图片圆角 | ||
| 483 | + ctx.fill(); | ||
| 484 | + const img = canvas.createImage(); // 创建图片对象 | ||
| 485 | + img.src = imgPath; | ||
| 486 | + img.onload = () => { | ||
| 487 | + ctx.drawImage(img, toPx(sx), toPx(sy), toPx(sw), toPx(sh), x, y, w, h); | ||
| 488 | + if (borderWidth > 0) { | ||
| 489 | + ctx.strokeStyle = borderColor; | ||
| 490 | + ctx.lineWidth = borderWidth; | ||
| 491 | + ctx.stroke(); | ||
| 492 | + } | ||
| 493 | + resolve(); | ||
| 494 | + ctx.restore(); | ||
| 495 | + }; | ||
| 496 | + } else if (borderRadiusGroup) { | ||
| 497 | + _drawRadiusGroupRect( | ||
| 498 | + { | ||
| 499 | + x, | ||
| 500 | + y, | ||
| 501 | + w, | ||
| 502 | + h, | ||
| 503 | + g: borderRadiusGroup | ||
| 504 | + }, | ||
| 505 | + drawOptions | ||
| 506 | + ); | ||
| 507 | + ctx.clip(); // 裁切,后续绘图限制在这个裁切范围内,保证图片圆角 | ||
| 508 | + ctx.fill(); | ||
| 509 | + const img = canvas.createImage(); // 创建图片对象 | ||
| 510 | + img.src = imgPath; | ||
| 511 | + img.onload = () => { | ||
| 512 | + ctx.drawImage(img, toPx(sx), toPx(sy), toPx(sw), toPx(sh), x, y, w, h); | ||
| 513 | + resolve(); | ||
| 514 | + ctx.restore(); | ||
| 515 | + }; | ||
| 516 | + } else { | ||
| 517 | + const img = canvas.createImage(); // 创建图片对象 | ||
| 518 | + img.src = imgPath; | ||
| 519 | + img.onload = () => { | ||
| 520 | + ctx.drawImage(img, toPx(sx), toPx(sy), toPx(sw), toPx(sh), x, y, w, h); | ||
| 521 | + resolve(); | ||
| 522 | + ctx.restore(); | ||
| 523 | + }; | ||
| 524 | + } | ||
| 525 | + }); |
src/components/PosterBuilder/utils/tools.ts
0 → 100644
| 1 | +/* eslint-disable prefer-destructuring */ | ||
| 2 | +import Taro, { CanvasContext, CanvasGradient } from '@tarojs/taro'; | ||
| 3 | + | ||
| 4 | +declare const wx: any; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * @description 生成随机字符串 | ||
| 8 | + * @param { number } length - 字符串长度 | ||
| 9 | + * @returns { string } | ||
| 10 | + */ | ||
| 11 | +export function randomString(length) { | ||
| 12 | + let str = Math.random().toString(36).substr(2); | ||
| 13 | + if (str.length >= length) { | ||
| 14 | + return str.substr(0, length); | ||
| 15 | + } | ||
| 16 | + str += randomString(length - str.length); | ||
| 17 | + return str; | ||
| 18 | +} | ||
| 19 | + | ||
| 20 | +/** | ||
| 21 | + * 随机创造一个id | ||
| 22 | + * @param { number } length - 字符串长度 | ||
| 23 | + * @returns { string } | ||
| 24 | + */ | ||
| 25 | +export function getRandomId(prefix = 'canvas', length = 10) { | ||
| 26 | + return prefix + randomString(length); | ||
| 27 | +} | ||
| 28 | + | ||
| 29 | +/** | ||
| 30 | + * @description 获取最大高度 | ||
| 31 | + * @param {} config | ||
| 32 | + * @returns { number } | ||
| 33 | + */ | ||
| 34 | +// export function getHeight (config) { | ||
| 35 | +// const getTextHeight = text => { | ||
| 36 | +// const fontHeight = text.lineHeight || text.fontSize | ||
| 37 | +// let height = 0 | ||
| 38 | +// if (text.baseLine === 'top') { | ||
| 39 | +// height = fontHeight | ||
| 40 | +// } else if (text.baseLine === 'middle') { | ||
| 41 | +// height = fontHeight / 2 | ||
| 42 | +// } else { | ||
| 43 | +// height = 0 | ||
| 44 | +// } | ||
| 45 | +// return height | ||
| 46 | +// } | ||
| 47 | +// const heightArr: number[] = []; | ||
| 48 | +// (config.blocks || []).forEach(item => { | ||
| 49 | +// heightArr.push(item.y + item.height) | ||
| 50 | +// }); | ||
| 51 | +// (config.texts || []).forEach(item => { | ||
| 52 | +// let height | ||
| 53 | +// if (Object.prototype.toString.call(item.text) === '[object Array]') { | ||
| 54 | +// item.text.forEach(i => { | ||
| 55 | +// height = getTextHeight({ ...i, baseLine: item.baseLine }) | ||
| 56 | +// heightArr.push(item.y + height) | ||
| 57 | +// }) | ||
| 58 | +// } else { | ||
| 59 | +// height = getTextHeight(item) | ||
| 60 | +// heightArr.push(item.y + height) | ||
| 61 | +// } | ||
| 62 | +// }); | ||
| 63 | +// (config.images || []).forEach(item => { | ||
| 64 | +// heightArr.push(item.y + item.height) | ||
| 65 | +// }); | ||
| 66 | +// (config.lines || []).forEach(item => { | ||
| 67 | +// heightArr.push(item.startY) | ||
| 68 | +// heightArr.push(item.endY) | ||
| 69 | +// }) | ||
| 70 | +// const sortRes = heightArr.sort((a, b) => b - a) | ||
| 71 | +// let canvasHeight = 0 | ||
| 72 | +// if (sortRes.length > 0) { | ||
| 73 | +// canvasHeight = sortRes[0] | ||
| 74 | +// } | ||
| 75 | +// if (config.height < canvasHeight || !config.height) { | ||
| 76 | +// return canvasHeight | ||
| 77 | +// } | ||
| 78 | +// return config.height | ||
| 79 | +// } | ||
| 80 | + | ||
| 81 | +/** | ||
| 82 | + * 将http转为https | ||
| 83 | + * @param {String}} rawUrl 图片资源url | ||
| 84 | + * @returns { string } | ||
| 85 | + */ | ||
| 86 | +export function mapHttpToHttps(rawUrl) { | ||
| 87 | + if (rawUrl.indexOf(':') < 0 || rawUrl.startsWith('http://tmp')) { | ||
| 88 | + return rawUrl; | ||
| 89 | + } | ||
| 90 | + const urlComponent = rawUrl.split(':'); | ||
| 91 | + if (urlComponent.length === 2) { | ||
| 92 | + if (urlComponent[0] === 'http') { | ||
| 93 | + urlComponent[0] = 'https'; | ||
| 94 | + return `${urlComponent[0]}:${urlComponent[1]}`; | ||
| 95 | + } | ||
| 96 | + } | ||
| 97 | + return rawUrl; | ||
| 98 | +} | ||
| 99 | + | ||
| 100 | +/** | ||
| 101 | + * 获取 rpx => px 的转换系数 | ||
| 102 | + * @returns { number } factor 单位转换系数 1rpx = factor * px | ||
| 103 | + */ | ||
| 104 | +export const getFactor = () => { | ||
| 105 | + const sysInfo = Taro.getSystemInfoSync(); | ||
| 106 | + const { screenWidth } = sysInfo; | ||
| 107 | + return screenWidth / 750; | ||
| 108 | +}; | ||
| 109 | + | ||
| 110 | +/** | ||
| 111 | + * rpx => px 单位转换 | ||
| 112 | + * @param { number } rpx - 需要转换的数值 | ||
| 113 | + * @param { number } factor - 转化因子 | ||
| 114 | + * @returns { number } | ||
| 115 | + */ | ||
| 116 | +export const toPx = (rpx, factor = getFactor()) => | ||
| 117 | + parseInt(String(rpx * factor), 10); | ||
| 118 | + | ||
| 119 | +/** | ||
| 120 | + * px => rpx 单位转换 | ||
| 121 | + * @param { number } px - 需要转换的数值 | ||
| 122 | + * @param { number } factor - 转化因子 | ||
| 123 | + * @returns { number } | ||
| 124 | + */ | ||
| 125 | +export const toRpx = (px, factor = getFactor()) => | ||
| 126 | + parseInt(String(px / factor), 10); | ||
| 127 | + | ||
| 128 | +/** | ||
| 129 | + * 下载图片资源 | ||
| 130 | + * @param { string } url | ||
| 131 | + * @returns { Promise } | ||
| 132 | + */ | ||
| 133 | +export function downImage(url) { | ||
| 134 | + return new Promise<string>((resolve, reject) => { | ||
| 135 | + // eslint-disable-next-line no-undef | ||
| 136 | + if (/^http/.test(url) && !new RegExp(wx.env.USER_DATA_PATH).test(url)) { | ||
| 137 | + // wx.env.USER_DATA_PATH 文件系统中的用户目录路径 | ||
| 138 | + Taro.downloadFile({ | ||
| 139 | + url: mapHttpToHttps(url), | ||
| 140 | + success: (res) => { | ||
| 141 | + if (res.statusCode === 200) { | ||
| 142 | + resolve(res.tempFilePath); | ||
| 143 | + } else { | ||
| 144 | + console.log('下载失败', res); | ||
| 145 | + reject(res); | ||
| 146 | + } | ||
| 147 | + }, | ||
| 148 | + fail(err) { | ||
| 149 | + console.log('下载失败了', err); | ||
| 150 | + reject(err); | ||
| 151 | + } | ||
| 152 | + }); | ||
| 153 | + } else { | ||
| 154 | + resolve(url); // 支持本地地址 | ||
| 155 | + } | ||
| 156 | + }); | ||
| 157 | +} | ||
| 158 | + | ||
| 159 | +/** | ||
| 160 | + * 下载图片并获取图片信息 | ||
| 161 | + * @param {} item 图片参数信息 | ||
| 162 | + * @param {} index 图片下标 | ||
| 163 | + * @returns { Promise } result 整理后的图片信息 | ||
| 164 | + */ | ||
| 165 | +export const getImageInfo = (item, index) => | ||
| 166 | + new Promise((resolve, reject) => { | ||
| 167 | + const { x, y, width, height, url, zIndex } = item; | ||
| 168 | + downImage(url).then((imgPath) => | ||
| 169 | + Taro.getImageInfo({ src: imgPath }) | ||
| 170 | + .then((imgInfo) => { | ||
| 171 | + // 获取图片信息 | ||
| 172 | + // 根据画布的宽高计算出图片绘制的大小,这里会保证图片绘制不变形, 即宽高比不变,截取再拉伸 | ||
| 173 | + let sx; // 截图的起点 x 坐标 | ||
| 174 | + let sy; // 截图的起点 y 坐标 | ||
| 175 | + const borderRadius = item.borderRadius || 0; | ||
| 176 | + const imgWidth = toRpx(imgInfo.width); // 图片真实宽度 单位 px | ||
| 177 | + const imgHeight = toRpx(imgInfo.height); // 图片真实高度 单位 px | ||
| 178 | + // 根据宽高比截取图片 | ||
| 179 | + if (imgWidth / imgHeight <= width / height) { | ||
| 180 | + sx = 0; | ||
| 181 | + sy = (imgHeight - (imgWidth / width) * height) / 2; | ||
| 182 | + } else { | ||
| 183 | + sy = 0; | ||
| 184 | + sx = (imgWidth - (imgHeight / height) * width) / 2; | ||
| 185 | + } | ||
| 186 | + // 给 canvas 画图准备参数,详见 ./draw.ts-drawImage | ||
| 187 | + const result = { | ||
| 188 | + type: 'image', | ||
| 189 | + borderRadius, | ||
| 190 | + borderWidth: item.borderWidth, | ||
| 191 | + borderColor: item.borderColor, | ||
| 192 | + borderRadiusGroup: item.borderRadiusGroup, | ||
| 193 | + zIndex: typeof zIndex !== 'undefined' ? zIndex : index, | ||
| 194 | + imgPath: url, | ||
| 195 | + sx, | ||
| 196 | + sy, | ||
| 197 | + sw: imgWidth - sx * 2, | ||
| 198 | + sh: imgHeight - sy * 2, | ||
| 199 | + x, | ||
| 200 | + y, | ||
| 201 | + w: width, | ||
| 202 | + h: height | ||
| 203 | + }; | ||
| 204 | + resolve(result); | ||
| 205 | + }) | ||
| 206 | + .catch((err) => { | ||
| 207 | + console.log('读取图片信息失败', err); | ||
| 208 | + reject(err); | ||
| 209 | + }) | ||
| 210 | + ); | ||
| 211 | + }); | ||
| 212 | + | ||
| 213 | +/** | ||
| 214 | + * 获取线性渐变色 | ||
| 215 | + * @param {CanvasContext} ctx canvas 实例对象 | ||
| 216 | + * @param {String} color 线性渐变色,如 'linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, #fff 100%)' | ||
| 217 | + * @param {Number} startX 起点 x 坐标 | ||
| 218 | + * @param {Number} startY 起点 y 坐标 | ||
| 219 | + * @param {Number} w 宽度 | ||
| 220 | + * @param {Number} h 高度 | ||
| 221 | + * @returns {} | ||
| 222 | + */ | ||
| 223 | +// TODO: 待优化, 支持所有角度,多个颜色的线性渐变 | ||
| 224 | +export function getLinearColor( | ||
| 225 | + ctx: CanvasContext, | ||
| 226 | + color, | ||
| 227 | + startX, | ||
| 228 | + startY, | ||
| 229 | + w, | ||
| 230 | + h | ||
| 231 | +) { | ||
| 232 | + if ( | ||
| 233 | + typeof startX !== 'number' || | ||
| 234 | + typeof startY !== 'number' || | ||
| 235 | + typeof w !== 'number' || | ||
| 236 | + typeof h !== 'number' | ||
| 237 | + ) { | ||
| 238 | + console.warn('坐标或者宽高只支持数字'); | ||
| 239 | + return color; | ||
| 240 | + } | ||
| 241 | + let grd: CanvasGradient | string = color; | ||
| 242 | + if (color.includes('linear-gradient')) { | ||
| 243 | + // fillStyle 不支持线性渐变色 | ||
| 244 | + const colorList = color.match(/\((\d+)deg,\s(.+)\s\d+%,\s(.+)\s\d+%/); | ||
| 245 | + const radian = colorList[1]; // 渐变弧度(角度) | ||
| 246 | + const color1 = colorList[2]; | ||
| 247 | + const color2 = colorList[3]; | ||
| 248 | + | ||
| 249 | + const L = Math.sqrt(w * w + h * h); | ||
| 250 | + const x = Math.ceil(Math.sin(180 - radian) * L); | ||
| 251 | + const y = Math.ceil(Math.cos(180 - radian) * L); | ||
| 252 | + | ||
| 253 | + // 根据弧度和宽高确定渐变色的两个点的坐标 | ||
| 254 | + if (Number(radian) === 180 || Number(radian) === 0) { | ||
| 255 | + if (Number(radian) === 180) { | ||
| 256 | + grd = ctx.createLinearGradient(startX, startY, startX, startY + h); | ||
| 257 | + } | ||
| 258 | + if (Number(radian) === 0) { | ||
| 259 | + grd = ctx.createLinearGradient(startX, startY + h, startX, startY); | ||
| 260 | + } | ||
| 261 | + } else if (radian > 0 && radian < 180) { | ||
| 262 | + grd = ctx.createLinearGradient(startX, startY, x + startX, y + startY); | ||
| 263 | + } else { | ||
| 264 | + throw new Error('只支持0 <= 颜色弧度 <= 180'); | ||
| 265 | + } | ||
| 266 | + (grd as CanvasGradient).addColorStop(0, color1); | ||
| 267 | + (grd as CanvasGradient).addColorStop(1, color2); | ||
| 268 | + } | ||
| 269 | + return grd; | ||
| 270 | +} | ||
| 271 | + | ||
| 272 | +/** | ||
| 273 | + * 根据文字对齐方式设置坐标 | ||
| 274 | + * @param {*} imgPath | ||
| 275 | + * @param {*} index | ||
| 276 | + * @returns { Promise } | ||
| 277 | + */ | ||
| 278 | +export function getTextX(textAlign, x, width) { | ||
| 279 | + let newX = x; | ||
| 280 | + if (textAlign === 'center') { | ||
| 281 | + newX = width / 2 + x; | ||
| 282 | + } else if (textAlign === 'right') { | ||
| 283 | + newX = width + x; | ||
| 284 | + } | ||
| 285 | + return newX; | ||
| 286 | +} |
src/components/navBar.vue
0 → 100644
| 1 | +<!-- | ||
| 2 | + * @Date: 2022-09-21 11:59:20 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2024-09-13 10:52:42 | ||
| 5 | + * @FilePath: /meihuaApp/src/components/navBar.vue | ||
| 6 | + * @Description: 底部导航栏 | ||
| 7 | +--> | ||
| 8 | +<template> | ||
| 9 | + <view id="navbar-page" class="navbar-page"> | ||
| 10 | + <view @tap="goTo('index')" class="home"> | ||
| 11 | + <view style="height: 1.5rem;"> | ||
| 12 | + <IconFont :name="icon_home" size="1.5rem" color="" /> | ||
| 13 | + </view> | ||
| 14 | + <view><text :style="homeStyle">首页</text></view> | ||
| 15 | + </view> | ||
| 16 | + <view @tap="goTo('book')" class="book"> | ||
| 17 | + <view style="height: 1.5rem;"> | ||
| 18 | + <IconFont :name="icon_book" size="1.5rem" color="" /> | ||
| 19 | + </view> | ||
| 20 | + <view><text :style="bookStyle">订房</text></view> | ||
| 21 | + </view> | ||
| 22 | + <view @tap="goTo('serverInfo')" class="server"> | ||
| 23 | + <view style="height: 1.5rem;"> | ||
| 24 | + <IconFont :name="icon_server" size="1.5rem" color="" /> | ||
| 25 | + </view> | ||
| 26 | + <view><text :style="serverStyle">服务</text></view> | ||
| 27 | + </view> | ||
| 28 | + <view @tap="goTo('my')" class="my"> | ||
| 29 | + <view style="height: 1.5rem;"> | ||
| 30 | + <IconFont :name="icon_my" size="1.5rem" color="" /> | ||
| 31 | + </view> | ||
| 32 | + <view><text :style="myStyle">我的</text></view> | ||
| 33 | + </view> | ||
| 34 | + </view> | ||
| 35 | +</template> | ||
| 36 | + | ||
| 37 | +<script setup> | ||
| 38 | +import Taro from '@tarojs/taro' | ||
| 39 | +import { ref, defineProps, computed, onMounted } from 'vue' | ||
| 40 | +import icon_home1 from '@/images/icon/icon_home1@2x.png' | ||
| 41 | +import icon_home2 from '@/images/icon/icon_home2@2x.png' | ||
| 42 | +import icon_my1 from '@/images/icon/icon_my1@2x.png' | ||
| 43 | +import icon_my2 from '@/images/icon/icon_my2@2x.png' | ||
| 44 | +import icon_book1 from '@/images/icon/icon_book1@2x.png' | ||
| 45 | +import icon_book2 from '@/images/icon/icon_book2@2x.png' | ||
| 46 | +import icon_server1 from '@/images/icon/icon_server1.png' | ||
| 47 | +import icon_server2 from '@/images/icon/icon_server2.png' | ||
| 48 | +// import { hostListAPI } from '@/api/Host/index' | ||
| 49 | +import { IconFont } from '@nutui/icons-vue-taro'; | ||
| 50 | + | ||
| 51 | +const goTo = (page) => { | ||
| 52 | + if (props.activated === page) { | ||
| 53 | + return; | ||
| 54 | + } | ||
| 55 | + wx.redirectTo({ | ||
| 56 | + url: `../${page}/index` | ||
| 57 | + }); | ||
| 58 | +} | ||
| 59 | + | ||
| 60 | +// const createActivity = async () => { | ||
| 61 | +// // 获取主办方列表信息 | ||
| 62 | +// const { code, data } = await hostListAPI(); | ||
| 63 | +// if (code) { | ||
| 64 | +// if (!data.my_hosts.length) { // 主办方为空 | ||
| 65 | +// Taro.showModal({ | ||
| 66 | +// title: '温馨提示', | ||
| 67 | +// content: '请先创建主办方后新建活动', | ||
| 68 | +// success: function (res) { | ||
| 69 | +// if (res.confirm) { | ||
| 70 | +// Taro.navigateTo({ | ||
| 71 | +// url: '../createProject/index' | ||
| 72 | +// }); | ||
| 73 | +// } | ||
| 74 | +// } | ||
| 75 | +// }); | ||
| 76 | +// } else { | ||
| 77 | +// Taro.navigateTo({ | ||
| 78 | +// url: '../createActivity/index' | ||
| 79 | +// }) | ||
| 80 | +// } | ||
| 81 | +// } | ||
| 82 | +// } | ||
| 83 | + | ||
| 84 | +const currentPage = ref(''); | ||
| 85 | + | ||
| 86 | +onMounted(() => { | ||
| 87 | + let pages = getCurrentPages(); | ||
| 88 | + let current_page = pages[pages.length - 1]; | ||
| 89 | + let url = current_page.route; | ||
| 90 | + if (url == 'pages/index/index') { | ||
| 91 | + currentPage.value = 'index' | ||
| 92 | + } else { | ||
| 93 | + currentPage.value = 'my' | ||
| 94 | + } | ||
| 95 | +}) | ||
| 96 | + | ||
| 97 | +const props = defineProps({ | ||
| 98 | + activated: String, | ||
| 99 | +}) | ||
| 100 | + | ||
| 101 | +const homeStyle = ref({}) | ||
| 102 | +const myStyle = ref({}) | ||
| 103 | +const bookStyle = ref({}) | ||
| 104 | +const serverStyle = ref({}) | ||
| 105 | + | ||
| 106 | +const icon_home = computed(() => { | ||
| 107 | + if (props.activated === 'index') { | ||
| 108 | + return icon_home1 | ||
| 109 | + } else { | ||
| 110 | + return icon_home2 | ||
| 111 | + } | ||
| 112 | +}) | ||
| 113 | +const icon_my = computed(() => { | ||
| 114 | + if (props.activated === 'my') { | ||
| 115 | + return icon_my1 | ||
| 116 | + } else { | ||
| 117 | + return icon_my2 | ||
| 118 | + } | ||
| 119 | +}) | ||
| 120 | +const icon_book = computed(() => { | ||
| 121 | + if (props.activated === 'book') { | ||
| 122 | + return icon_book1 | ||
| 123 | + } else { | ||
| 124 | + return icon_book2 | ||
| 125 | + } | ||
| 126 | +}) | ||
| 127 | +const icon_server= computed(() => { | ||
| 128 | + if (props.activated === 'serverInfo') { | ||
| 129 | + return icon_server1 | ||
| 130 | + } else { | ||
| 131 | + return icon_server2 | ||
| 132 | + } | ||
| 133 | +}) | ||
| 134 | + | ||
| 135 | +if (props.activated === 'index') { | ||
| 136 | + homeStyle.value = { | ||
| 137 | + color: '#6A4925', | ||
| 138 | + fontSize: '0.9rem' | ||
| 139 | + } | ||
| 140 | + myStyle.value = { | ||
| 141 | + color: '#999999', | ||
| 142 | + fontSize: '0.9rem' | ||
| 143 | + } | ||
| 144 | + bookStyle.value = { | ||
| 145 | + color: '#999999', | ||
| 146 | + fontSize: '0.9rem' | ||
| 147 | + } | ||
| 148 | + serverStyle.value = { | ||
| 149 | + color: '#999999', | ||
| 150 | + fontSize: '0.9rem' | ||
| 151 | + } | ||
| 152 | +} else if (props.activated === 'my') { | ||
| 153 | + homeStyle.value = { | ||
| 154 | + color: '#999999', | ||
| 155 | + fontSize: '0.9rem' | ||
| 156 | + } | ||
| 157 | + myStyle.value = { | ||
| 158 | + color: '#6A4925', | ||
| 159 | + fontSize: '0.9rem' | ||
| 160 | + } | ||
| 161 | + bookStyle.value = { | ||
| 162 | + color: '#999999', | ||
| 163 | + fontSize: '0.9rem' | ||
| 164 | + } | ||
| 165 | + serverStyle.value = { | ||
| 166 | + color: '#999999', | ||
| 167 | + fontSize: '0.9rem' | ||
| 168 | + } | ||
| 169 | +} else if (props.activated === 'book') { | ||
| 170 | + homeStyle.value = { | ||
| 171 | + color: '#999999', | ||
| 172 | + fontSize: '0.9rem' | ||
| 173 | + } | ||
| 174 | + myStyle.value = { | ||
| 175 | + color: '#999999', | ||
| 176 | + fontSize: '0.9rem' | ||
| 177 | + } | ||
| 178 | + bookStyle.value = { | ||
| 179 | + color: '#6A4925', | ||
| 180 | + fontSize: '0.9rem' | ||
| 181 | + } | ||
| 182 | + serverStyle.value = { | ||
| 183 | + color: '#999999', | ||
| 184 | + fontSize: '0.9rem' | ||
| 185 | + } | ||
| 186 | +} else if (props.activated === 'serverInfo') { | ||
| 187 | + homeStyle.value = { | ||
| 188 | + color: '#999999', | ||
| 189 | + fontSize: '0.9rem' | ||
| 190 | + } | ||
| 191 | + myStyle.value = { | ||
| 192 | + color: '#999999', | ||
| 193 | + fontSize: '0.9rem' | ||
| 194 | + } | ||
| 195 | + bookStyle.value = { | ||
| 196 | + color: '#999999', | ||
| 197 | + fontSize: '0.9rem' | ||
| 198 | + } | ||
| 199 | + serverStyle.value = { | ||
| 200 | + color: '#6A4925', | ||
| 201 | + fontSize: '0.9rem' | ||
| 202 | + } | ||
| 203 | +} | ||
| 204 | + | ||
| 205 | +</script> | ||
| 206 | + | ||
| 207 | +<style lang="less"> | ||
| 208 | +.navbar-page { | ||
| 209 | + position: fixed; | ||
| 210 | + bottom: 0; | ||
| 211 | + background-color: #FFFFFF; | ||
| 212 | + padding-top: 0.5rem; | ||
| 213 | + height: 5rem; | ||
| 214 | + width: 100%; | ||
| 215 | + | ||
| 216 | + .home { | ||
| 217 | + position: absolute; | ||
| 218 | + left: 10%; | ||
| 219 | + transform: translateX(-15%); | ||
| 220 | + text-align: center; | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | + .book { | ||
| 224 | + position: absolute; | ||
| 225 | + left: 35%; | ||
| 226 | + transform: translateX(-50%); | ||
| 227 | + text-align: center; | ||
| 228 | + } | ||
| 229 | + | ||
| 230 | + .server { | ||
| 231 | + position: absolute; | ||
| 232 | + left: 60%; | ||
| 233 | + transform: translateX(-50%); | ||
| 234 | + text-align: center; | ||
| 235 | + } | ||
| 236 | + | ||
| 237 | + .my { | ||
| 238 | + position: absolute; | ||
| 239 | + left: 85%; | ||
| 240 | + transform: translateX(-85%); | ||
| 241 | + text-align: center; | ||
| 242 | + } | ||
| 243 | +} | ||
| 244 | +</style> |
| 1 | +var getDaysInOneMonth = function (year, month) { | ||
| 2 | + let _month = parseInt(month, 10); | ||
| 3 | + let d = new Date(year, _month, 0); | ||
| 4 | + return d.getDate(); | ||
| 5 | +} | ||
| 6 | +var dateDate = function (date) { | ||
| 7 | + let year = date && date.getFullYear(); | ||
| 8 | + let month = date && date.getMonth() + 1; | ||
| 9 | + let day = date && date.getDate(); | ||
| 10 | + let hours = date && date.getHours(); | ||
| 11 | + let minutes = date && date.getMinutes(); | ||
| 12 | + return { | ||
| 13 | + year, month, day, hours, minutes | ||
| 14 | + } | ||
| 15 | +} | ||
| 16 | +var dateTimePicker = function (startyear, endyear) { | ||
| 17 | + // 获取date time 年份,月份,天数,小时,分钟推后30分 | ||
| 18 | + const years = []; | ||
| 19 | + const months = []; | ||
| 20 | + const hours = []; | ||
| 21 | + const minutes = []; | ||
| 22 | + for (let i = startyear; i <= endyear; i++) { | ||
| 23 | + years.push({ | ||
| 24 | + name: i + '年', | ||
| 25 | + id: i | ||
| 26 | + }); | ||
| 27 | + } | ||
| 28 | + //获取月份 | ||
| 29 | + for (let i = 1; i <= 12; i++) { | ||
| 30 | + if (i < 10) { | ||
| 31 | + i = "0" + i; | ||
| 32 | + } | ||
| 33 | + months.push({ | ||
| 34 | + name: i + '月', | ||
| 35 | + id: i | ||
| 36 | + }); | ||
| 37 | + } | ||
| 38 | + //获取小时 | ||
| 39 | + for (let i = 0; i < 24; i++) { | ||
| 40 | + if (i < 10) { | ||
| 41 | + i = "0" + i; | ||
| 42 | + } | ||
| 43 | + hours.push({ | ||
| 44 | + name: i + '时', | ||
| 45 | + id: i | ||
| 46 | + }); | ||
| 47 | + } | ||
| 48 | + //获取分钟 | ||
| 49 | + for (let i = 0; i < 60; i++) { | ||
| 50 | + if (i < 10) { | ||
| 51 | + i = "0" + i; | ||
| 52 | + } | ||
| 53 | + minutes.push({ | ||
| 54 | + name: i + '分', | ||
| 55 | + id: i | ||
| 56 | + }); | ||
| 57 | + } | ||
| 58 | + return function (_year, _month) { | ||
| 59 | + const days = []; | ||
| 60 | + _year = parseInt(_year); | ||
| 61 | + _month = parseInt(_month); | ||
| 62 | + //获取日期 | ||
| 63 | + for (let i = 1; i <= getDaysInOneMonth(_year, _month); i++) { | ||
| 64 | + if (i < 10) { | ||
| 65 | + i = "0" + i; | ||
| 66 | + } | ||
| 67 | + days.push({ | ||
| 68 | + name: i + '日', | ||
| 69 | + id: i | ||
| 70 | + }); | ||
| 71 | + } | ||
| 72 | + return [years, months, days, hours, minutes]; | ||
| 73 | + } | ||
| 74 | +} | ||
| 75 | +export { | ||
| 76 | + dateTimePicker, | ||
| 77 | + getDaysInOneMonth, | ||
| 78 | + dateDate | ||
| 79 | +} |
src/components/time-picker-data/picker.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <picker mode="multiSelector" :range-key="'name'" :value="timeIndex" :range="activityArray" :disabled="disabled" | ||
| 3 | + @change="bindMultiPickerChange" @columnChange="bindMultiPickerColumnChange"> | ||
| 4 | + <slot /> | ||
| 5 | + </picker> | ||
| 6 | +</template> | ||
| 7 | +<script> | ||
| 8 | +import { dateTimePicker, dateDate } from "./dateTimePicker.js"; | ||
| 9 | +export default { | ||
| 10 | + props: { | ||
| 11 | + startTime: { | ||
| 12 | + type: [Object, Date], | ||
| 13 | + default: new Date(), | ||
| 14 | + }, | ||
| 15 | + endTime: { | ||
| 16 | + type: [Object, Date], | ||
| 17 | + default: new Date(), | ||
| 18 | + }, | ||
| 19 | + defaultTime: { | ||
| 20 | + type: [Object, Date], | ||
| 21 | + default: new Date(), | ||
| 22 | + }, | ||
| 23 | + disabled: { | ||
| 24 | + type: Boolean, | ||
| 25 | + default: false, | ||
| 26 | + }, | ||
| 27 | + }, | ||
| 28 | + data() { | ||
| 29 | + return { | ||
| 30 | + timeIndex: [0, 0, 0, 0, 0], | ||
| 31 | + activityArray: [], | ||
| 32 | + year: 0, | ||
| 33 | + month: 1, | ||
| 34 | + day: 1, | ||
| 35 | + hour: 0, | ||
| 36 | + minute: 0, | ||
| 37 | + datePicker: "", | ||
| 38 | + defaultIndex: [0, 0, 0, 0, 0], | ||
| 39 | + startIndex: [0, 0, 0, 0, 0], | ||
| 40 | + endIndex: [0, 0, 0, 0, 0], | ||
| 41 | + }; | ||
| 42 | + }, | ||
| 43 | + computed: { | ||
| 44 | + timeDate() { | ||
| 45 | + const { startTime, endTime } = this; | ||
| 46 | + return { startTime, endTime }; | ||
| 47 | + }, | ||
| 48 | + }, | ||
| 49 | + watch: { | ||
| 50 | + timeDate() { | ||
| 51 | + this.initData(); | ||
| 52 | + }, | ||
| 53 | + defaultTime () { | ||
| 54 | + this.initData(); | ||
| 55 | + } | ||
| 56 | + }, | ||
| 57 | + created() { | ||
| 58 | + this.initData(); | ||
| 59 | + }, | ||
| 60 | + methods: { | ||
| 61 | + initData() { | ||
| 62 | + let startTime = this.startTime; | ||
| 63 | + let endTime = this.endTime; | ||
| 64 | + this.datePicker = dateTimePicker( | ||
| 65 | + startTime.getFullYear(), | ||
| 66 | + endTime.getFullYear() | ||
| 67 | + ); | ||
| 68 | + this.setDateData(this.defaultTime); | ||
| 69 | + this.getKeyIndex(this.startTime, "startIndex"); | ||
| 70 | + // 截止时间索引 | ||
| 71 | + this.getKeyIndex(this.endTime, "endIndex"); | ||
| 72 | + // 默认索引 | ||
| 73 | + this.getKeyIndex(this.defaultTime, "defaultIndex"); | ||
| 74 | + this.timeIndex = this.defaultIndex; | ||
| 75 | + // 初始时间 | ||
| 76 | + this.initTime(); | ||
| 77 | + }, | ||
| 78 | + getKeyIndex(time, key) { | ||
| 79 | + let Arr = dateDate(time); | ||
| 80 | + let _index = this.getIndex(Arr); | ||
| 81 | + this[key] = _index; | ||
| 82 | + }, | ||
| 83 | + getIndex(arr) { | ||
| 84 | + let timeIndex = []; | ||
| 85 | + let indexKey = ["year", "month", "day", "hours", "minutes"]; | ||
| 86 | + this.activityArray.forEach((element, index) => { | ||
| 87 | + let _index = element.findIndex( | ||
| 88 | + (item) => parseInt(item.id) === parseInt(arr[indexKey[index]]) | ||
| 89 | + ); | ||
| 90 | + timeIndex[index] = _index >= 0 ? _index : 0; | ||
| 91 | + }); | ||
| 92 | + return timeIndex; | ||
| 93 | + }, | ||
| 94 | + initTime() { | ||
| 95 | + let _index = this.timeIndex; | ||
| 96 | + this.year = this.activityArray[0][_index[0]].id; | ||
| 97 | + this.month = this.activityArray[1].length && this.activityArray[1][_index[1]].id; | ||
| 98 | + this.day = this.activityArray[2].length && this.activityArray[2][_index[2]].id; | ||
| 99 | + this.hour = this.activityArray[3].length && this.activityArray[3][_index[3]].id; | ||
| 100 | + this.minute = this.activityArray[4].length && this.activityArray[4][_index[4]].id; | ||
| 101 | + }, | ||
| 102 | + setDateData(_date) { | ||
| 103 | + let _data = dateDate(_date); | ||
| 104 | + this.activityArray = this.datePicker(_data.year, _data.month); | ||
| 105 | + }, | ||
| 106 | + bindMultiPickerChange(e) { | ||
| 107 | + console.log("picker发送选择改变,携带值为", e.detail.value); | ||
| 108 | + let activityArray = JSON.parse(JSON.stringify(this.activityArray)), | ||
| 109 | + { value } = e.detail, | ||
| 110 | + _result = []; | ||
| 111 | + for (let i = 0; i < value.length; i++) { | ||
| 112 | + _result[i] = activityArray[i][value[i]].id; | ||
| 113 | + } | ||
| 114 | + this.$emit("result", _result); | ||
| 115 | + }, | ||
| 116 | + bindMultiPickerColumnChange(e) { | ||
| 117 | + console.log("修改的列为", e.detail.column, ",值为", e.detail.value); | ||
| 118 | + let _data = JSON.parse(JSON.stringify(this.activityArray)), | ||
| 119 | + timeIndex = JSON.parse(JSON.stringify(this.timeIndex)), | ||
| 120 | + { startIndex, endIndex } = this, | ||
| 121 | + { column, value } = e.detail, | ||
| 122 | + _value = _data[column][value].id, | ||
| 123 | + _start = dateDate(this.startTime), | ||
| 124 | + _end = dateDate(this.endTime); | ||
| 125 | + switch (e.detail.column) { | ||
| 126 | + case 0: | ||
| 127 | + if (_value <= _start.year) { | ||
| 128 | + timeIndex = startIndex; | ||
| 129 | + this.year = _start.year; | ||
| 130 | + this.setDateData(this.startTime); | ||
| 131 | + } else if (_value >= _end.year) { | ||
| 132 | + this.year = _end.year; | ||
| 133 | + timeIndex = [endIndex[0], 0, 0, 0, 0]; | ||
| 134 | + this.setDateData(this.endTime); | ||
| 135 | + } else { | ||
| 136 | + this.year = _value; | ||
| 137 | + timeIndex = [value, 0, 0, 0, 0]; | ||
| 138 | + this.activityArray = this.datePicker(_value, 1); | ||
| 139 | + } | ||
| 140 | + timeIndex = this.timeIndex = JSON.parse(JSON.stringify(timeIndex)); | ||
| 141 | + this.timeIndex = timeIndex; | ||
| 142 | + break; | ||
| 143 | + case 1: | ||
| 144 | + if (this.year == _start.year && value <= startIndex[1]) { | ||
| 145 | + timeIndex = startIndex; | ||
| 146 | + this.month = _start.month; | ||
| 147 | + this.setDateData(this.startTime); | ||
| 148 | + } else if (this.year == _end.year && value >= endIndex[1]) { | ||
| 149 | + timeIndex = endIndex; | ||
| 150 | + this.month = _end.month; | ||
| 151 | + this.setDateData(this.endTime); | ||
| 152 | + } else { | ||
| 153 | + this.month = _value; | ||
| 154 | + _data[2] = this.datePicker(this.year, this.month)[2]; | ||
| 155 | + timeIndex = [timeIndex[0], value, 0, 0, 0]; | ||
| 156 | + this.activityArray = _data; | ||
| 157 | + } | ||
| 158 | + this.timeIndex = JSON.parse(JSON.stringify(timeIndex)); | ||
| 159 | + break; | ||
| 160 | + case 2: | ||
| 161 | + if ( | ||
| 162 | + this.year == _start.year && | ||
| 163 | + this.month == _start.month && | ||
| 164 | + value <= startIndex[2] | ||
| 165 | + ) { | ||
| 166 | + this.day = _start.day; | ||
| 167 | + timeIndex = startIndex; | ||
| 168 | + } else if ( | ||
| 169 | + this.year == _end.year && | ||
| 170 | + this.month == _end.month && | ||
| 171 | + value >= endIndex[2] | ||
| 172 | + ) { | ||
| 173 | + this.day = _end.day; | ||
| 174 | + timeIndex = endIndex; | ||
| 175 | + } else { | ||
| 176 | + this.day = _value; | ||
| 177 | + timeIndex = [timeIndex[0], timeIndex[1], value, 0, 0]; | ||
| 178 | + } | ||
| 179 | + this.timeIndex = JSON.parse(JSON.stringify(timeIndex)); | ||
| 180 | + break; | ||
| 181 | + case 3: | ||
| 182 | + if ( | ||
| 183 | + this.year == _start.year && | ||
| 184 | + this.month == _start.month && | ||
| 185 | + this.day == _start.day && | ||
| 186 | + value <= startIndex[3] | ||
| 187 | + ) { | ||
| 188 | + this.hour = _start.hours; | ||
| 189 | + timeIndex = startIndex; | ||
| 190 | + } else if ( | ||
| 191 | + this.year == _end.year && | ||
| 192 | + this.month == _end.month && | ||
| 193 | + this.day == _end.day && | ||
| 194 | + value >= endIndex[3] | ||
| 195 | + ) { | ||
| 196 | + this.hour = _end.hours; | ||
| 197 | + timeIndex = endIndex; | ||
| 198 | + } else { | ||
| 199 | + this.hour = _value; | ||
| 200 | + timeIndex[3] = value; | ||
| 201 | + timeIndex[4] = 0; | ||
| 202 | + } | ||
| 203 | + this.timeIndex = JSON.parse(JSON.stringify(timeIndex)); | ||
| 204 | + break; | ||
| 205 | + case 4: | ||
| 206 | + timeIndex[4] = value; | ||
| 207 | + if ( | ||
| 208 | + this.year == _start.year && | ||
| 209 | + this.month == _start.month && | ||
| 210 | + this.day == _start.day && | ||
| 211 | + this.hour == _start.hours && | ||
| 212 | + value <= startIndex[4] | ||
| 213 | + ) { | ||
| 214 | + timeIndex = startIndex; | ||
| 215 | + } else if ( | ||
| 216 | + this.year == _end.year && | ||
| 217 | + this.month == _end.month && | ||
| 218 | + this.day == _end.day && | ||
| 219 | + this.hour == _end.hours && | ||
| 220 | + value >= endIndex[4] | ||
| 221 | + ) { | ||
| 222 | + timeIndex = endIndex; | ||
| 223 | + } | ||
| 224 | + this.timeIndex = JSON.parse(JSON.stringify(timeIndex)); | ||
| 225 | + break; | ||
| 226 | + } | ||
| 227 | + }, | ||
| 228 | + }, | ||
| 229 | +}; | ||
| 230 | +</script> |
src/index.html
0 → 100644
| 1 | +<!DOCTYPE html> | ||
| 2 | +<html> | ||
| 3 | +<head> | ||
| 4 | + <meta content="text/html; charset=utf-8" http-equiv="Content-Type"> | ||
| 5 | + <meta content="width=device-width,initial-scale=1,user-scalable=no" name="viewport"> | ||
| 6 | + <meta name="apple-mobile-web-app-capable" content="yes"> | ||
| 7 | + <meta name="apple-touch-fullscreen" content="yes"> | ||
| 8 | + <meta name="format-detection" content="telephone=no,address=no"> | ||
| 9 | + <meta name="apple-mobile-web-app-status-bar-style" content="white"> | ||
| 10 | + <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" > | ||
| 11 | + <title>myApp</title> | ||
| 12 | + <script><%= htmlWebpackPlugin.options.script %></script> | ||
| 13 | +</head> | ||
| 14 | +<body> | ||
| 15 | + <div id="app"></div> | ||
| 16 | +</body> | ||
| 17 | +</html> |
src/pages/auth/index.config.js
0 → 100755
src/pages/auth/index.less
0 → 100644
src/pages/auth/index.vue
0 → 100644
| 1 | +<!-- | ||
| 2 | + * @Date: 2022-09-19 14:11:06 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2024-05-26 10:17:04 | ||
| 5 | + * @FilePath: /meihuaApp/src/pages/auth/index.vue | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | +--> | ||
| 8 | +<template> | ||
| 9 | + <div> | ||
| 10 | + <!-- <button wx:if="{{canIUse}}" open-type="getUserInfo" @getuserinfo="bindGetUserInfo">授权登录</button> | ||
| 11 | + <view @tap="auth">授权登陆</view> --> | ||
| 12 | + </div> | ||
| 13 | +</template> | ||
| 14 | + | ||
| 15 | +<script setup> | ||
| 16 | +import Taro from '@tarojs/taro' | ||
| 17 | +import { ref } from "vue"; | ||
| 18 | +import request from '@/utils/request'; | ||
| 19 | + | ||
| 20 | +</script> | ||
| 21 | + | ||
| 22 | +<script> | ||
| 23 | +import "./index.less"; | ||
| 24 | +import { getCurrentPageParam } from "@/utils/weapp"; | ||
| 25 | + | ||
| 26 | +export default { | ||
| 27 | + name: "authPage", | ||
| 28 | + mounted () { | ||
| 29 | + // 授权登陆 | ||
| 30 | + Taro.login({ | ||
| 31 | + success: function (res) { | ||
| 32 | + if (res.code) { | ||
| 33 | + //发起网络请求 | ||
| 34 | + Taro.showLoading({ | ||
| 35 | + title: '授权中', | ||
| 36 | + }) | ||
| 37 | + request.post('/srv/?a=openid', { | ||
| 38 | + code: res.code | ||
| 39 | + // openid: '0002' | ||
| 40 | + // openid: 'o5NFZ5cFQtLRy3aVHaZMLkjHFusI' | ||
| 41 | + // openid: 'o5NFZ5TpgG4FwYursGCLjcUJH2ak' | ||
| 42 | + // openid: 'o5NFZ5cqroPYwawCp8FEOxewtgnw' | ||
| 43 | + }) | ||
| 44 | + .then(res => { | ||
| 45 | + if (res.data.code) { | ||
| 46 | + var cookie = res.cookies[0]; | ||
| 47 | + if (cookie != null) { | ||
| 48 | + wx.setStorageSync("sessionid", res.cookies[0]);//服务器返回的 Set-Cookie,保存到本地 | ||
| 49 | + //TAG 小程序绑定cookie | ||
| 50 | + // 修改请求头 | ||
| 51 | + request.defaults.headers.cookie = res.cookies[0]; | ||
| 52 | + // if (res.data.data.avatar) { | ||
| 53 | + // Taro.reLaunch({ | ||
| 54 | + // url: '../../' + getCurrentPageParam().url | ||
| 55 | + // }) | ||
| 56 | + // } else { // 头像没有设置跳转完善信息页面 | ||
| 57 | + // Taro.redirectTo({ | ||
| 58 | + // url: '../apxUserInfo/index' | ||
| 59 | + // }) | ||
| 60 | + // } | ||
| 61 | + // TAG:处理分享跳转问题 | ||
| 62 | + const params = getCurrentPageParam(); | ||
| 63 | + if (getCurrentPageParam().url === 'pages/detail/index') { // 详情页的分享跳转处理 | ||
| 64 | + Taro.reLaunch({ | ||
| 65 | + url: `../../${params.url}?id=${params.id}&start_date=${params.start_date}&end_date=${params.end_date}` | ||
| 66 | + }) | ||
| 67 | + } else { // 其他页面分享跳首页 | ||
| 68 | + Taro.reLaunch({ | ||
| 69 | + url: `/pages/index/index?first_in=${wx.getStorageSync("first_in")}` | ||
| 70 | + }) | ||
| 71 | + } | ||
| 72 | + Taro.hideLoading(); | ||
| 73 | + } | ||
| 74 | + } else { | ||
| 75 | + console.warn(res.data.msg); | ||
| 76 | + Taro.hideLoading(); | ||
| 77 | + } | ||
| 78 | + }) | ||
| 79 | + .catch(err => { | ||
| 80 | + console.error(err); | ||
| 81 | + Taro.hideLoading(); | ||
| 82 | + }); | ||
| 83 | + } else { | ||
| 84 | + console.log('登录失败!' + res.errMsg) | ||
| 85 | + } | ||
| 86 | + } | ||
| 87 | + }) | ||
| 88 | + }, | ||
| 89 | + data () { | ||
| 90 | + return { | ||
| 91 | + canIUse: wx.canIUse('button.open-type.getUserInfo') | ||
| 92 | + } | ||
| 93 | + }, | ||
| 94 | + onLoad: function() { | ||
| 95 | + // 查看是否授权 | ||
| 96 | + // wx.getSetting({ | ||
| 97 | + // success (res){ | ||
| 98 | + // if (res.authSetting['scope.userInfo']) { | ||
| 99 | + // // 已经授权,可以直接调用 getUserInfo 获取头像昵称 | ||
| 100 | + // wx.getUserInfo({ | ||
| 101 | + // success: function(res) { | ||
| 102 | + // console.warn(res.userInfo) | ||
| 103 | + // } | ||
| 104 | + // }) | ||
| 105 | + // } | ||
| 106 | + // } | ||
| 107 | + // }) | ||
| 108 | + }, | ||
| 109 | + methods: { | ||
| 110 | + bindGetUserInfo (e) { | ||
| 111 | + console.warn(e.detail.userInfo) | ||
| 112 | + }, | ||
| 113 | + // auth () { | ||
| 114 | + // Taro.getSetting({ | ||
| 115 | + // success: function (res) { | ||
| 116 | + // if (!res.authSetting['scope.userInfo']) { | ||
| 117 | + // console.warn(0); | ||
| 118 | + // Taro.authorize({ | ||
| 119 | + // scope: 'scope.userInfo', | ||
| 120 | + // success: function () { | ||
| 121 | + // Taro.getUserInfo({ | ||
| 122 | + // success: function(res) { | ||
| 123 | + // var userInfo = res.userInfo | ||
| 124 | + // console.warn(userInfo); | ||
| 125 | + // } | ||
| 126 | + // }) | ||
| 127 | + // }, | ||
| 128 | + // fail: function (error) { | ||
| 129 | + // console.error(error) | ||
| 130 | + // } | ||
| 131 | + // }) | ||
| 132 | + // } | ||
| 133 | + // } | ||
| 134 | + // }) | ||
| 135 | + // } | ||
| 136 | + auth () { | ||
| 137 | + // wx.getSetting({ | ||
| 138 | + // success (res){ | ||
| 139 | + // if (res.authSetting['scope.userInfo']) { | ||
| 140 | + // // 已经授权,可以直接调用 getUserInfo 获取头像昵称 | ||
| 141 | + // wx.getUserInfo({ | ||
| 142 | + // success: function(res) { | ||
| 143 | + // console.warn(res.userInfo) | ||
| 144 | + // } | ||
| 145 | + // }) | ||
| 146 | + // } | ||
| 147 | + // } | ||
| 148 | + // }) | ||
| 149 | + wx.getSetting({ | ||
| 150 | + success(res) { | ||
| 151 | + if (!res.authSetting['scope.userInfo']) { | ||
| 152 | + wx.authorize({ | ||
| 153 | + scope: 'scope.userInfo', | ||
| 154 | + success () { | ||
| 155 | + // 已经授权,可以直接调用 getUserInfo 获取头像昵称 | ||
| 156 | + wx.getUserInfo({ | ||
| 157 | + success: function(res) { | ||
| 158 | + console.warn(res.userInfo) | ||
| 159 | + } | ||
| 160 | + }) | ||
| 161 | + } | ||
| 162 | + }) | ||
| 163 | + } | ||
| 164 | + } | ||
| 165 | + }) | ||
| 166 | + } | ||
| 167 | + } | ||
| 168 | +}; | ||
| 169 | +</script> |
src/pages/demo/index.config.js
0 → 100755
src/pages/demo/index.less
0 → 100644
src/pages/demo/index.vue
0 → 100644
| 1 | +<!-- | ||
| 2 | + * @Date: 2022-09-19 14:11:06 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-07-01 10:56:38 | ||
| 5 | + * @FilePath: /myApp/src/pages/demo/index.vue | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | +--> | ||
| 8 | +<template> | ||
| 9 | + <div class="red">{{ str }}</div> | ||
| 10 | +</template> | ||
| 11 | + | ||
| 12 | +<script setup> | ||
| 13 | +import '@tarojs/taro/html.css' | ||
| 14 | +import { ref } from "vue"; | ||
| 15 | +import "./index.less"; | ||
| 16 | + | ||
| 17 | +// 定义响应式数据 | ||
| 18 | +const str = ref('Demo页面') | ||
| 19 | +</script> | ||
| 20 | + | ||
| 21 | +<script> | ||
| 22 | +export default { | ||
| 23 | + name: "demoPage", | ||
| 24 | +}; | ||
| 25 | +</script> |
src/pages/index/index.config.js
0 → 100644
src/pages/index/index.less
0 → 100644
src/pages/index/index.vue
0 → 100644
| 1 | +<!-- | ||
| 2 | + * @Date: 2025-06-28 10:33:00 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-07-01 11:13:13 | ||
| 5 | + * @FilePath: /myApp/src/pages/index/index.vue | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | +--> | ||
| 8 | +<template> | ||
| 9 | + <view class="index"> | ||
| 10 | + <nut-button type="primary" @click="onClick">按钮</nut-button> | ||
| 11 | + <nut-toast v-model:visible="show" msg="你成功了" /> | ||
| 12 | + <View className="text-[#acc855] text-[100px]">Hello world!</View> | ||
| 13 | + </view> | ||
| 14 | +</template> | ||
| 15 | + | ||
| 16 | +<script setup> | ||
| 17 | +import Taro from '@tarojs/taro' | ||
| 18 | +import '@tarojs/taro/html.css' | ||
| 19 | +import { ref, onMounted } from 'vue' | ||
| 20 | +import { useDidShow, useReady } from '@tarojs/taro' | ||
| 21 | +import "./index.less"; | ||
| 22 | + | ||
| 23 | +const show = ref(false) | ||
| 24 | +const onClick = () => { | ||
| 25 | + show.value = true | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | +// 生命周期钩子 | ||
| 29 | +useDidShow(() => { | ||
| 30 | + console.warn('index onShow') | ||
| 31 | +}) | ||
| 32 | + | ||
| 33 | +useReady(async () => { | ||
| 34 | + console.warn('index onReady') | ||
| 35 | + // 版本更新检查 | ||
| 36 | + if (!Taro.canIUse("getUpdateManager")) { | ||
| 37 | + Taro.showModal({ | ||
| 38 | + title: "提示", | ||
| 39 | + content: "当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试", | ||
| 40 | + showCancel: false, | ||
| 41 | + }); | ||
| 42 | + return; | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + // https://developers.weixin.qq.com/miniprogram/dev/api/base/update/UpdateManager.html | ||
| 46 | + const updateManager = Taro.getUpdateManager(); | ||
| 47 | + | ||
| 48 | + updateManager.onCheckForUpdate((res) => { | ||
| 49 | + // 请求完新版本信息的回调 | ||
| 50 | + if (res.hasUpdate) { | ||
| 51 | + updateManager.onUpdateReady(function () { | ||
| 52 | + Taro.showModal({ | ||
| 53 | + title: "更新提示", | ||
| 54 | + content: "新版本已经准备好,是否重启应用?", | ||
| 55 | + success: function (res) { | ||
| 56 | + if (res.confirm) { | ||
| 57 | + // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启 | ||
| 58 | + updateManager.applyUpdate(); | ||
| 59 | + } | ||
| 60 | + }, | ||
| 61 | + }); | ||
| 62 | + }); | ||
| 63 | + | ||
| 64 | + updateManager.onUpdateFailed(function () { | ||
| 65 | + // 新版本下载失败 | ||
| 66 | + Taro.showModal({ | ||
| 67 | + title: "更新提示", | ||
| 68 | + content: "新版本已上线,请删除当前小程序,重新搜索打开", | ||
| 69 | + }); | ||
| 70 | + }); | ||
| 71 | + } | ||
| 72 | + }); | ||
| 73 | +}) | ||
| 74 | + | ||
| 75 | +onMounted(() => { | ||
| 76 | + console.warn('index mounted') | ||
| 77 | +}) | ||
| 78 | + | ||
| 79 | +// 分享功能 | ||
| 80 | +wx.showShareMenu({ | ||
| 81 | + withShareTicket: true, | ||
| 82 | + menus: ['shareAppMessage', 'shareTimeline'] | ||
| 83 | +}) | ||
| 84 | +</script> | ||
| 85 | + | ||
| 86 | +<script> | ||
| 87 | +import { getCurrentPageParam } from "@/utils/weapp"; | ||
| 88 | + | ||
| 89 | +export default { | ||
| 90 | + name: "indexPage", | ||
| 91 | + onHide () { | ||
| 92 | + console.warn('index onHide') | ||
| 93 | + }, | ||
| 94 | + onShareAppMessage() { | ||
| 95 | + let params = getCurrentPageParam(); | ||
| 96 | + // 设置菜单中的转发按钮触发转发事件时的转发内容 | ||
| 97 | + var shareObj = { | ||
| 98 | + title: "xxx", // 默认是小程序的名称(可以写slogan等) | ||
| 99 | + path: `pages/detail/index?id=${params.id}&start_date=${params.start_date}&end_date=${params.end_date}&room_type=${params.room_type}`, // 默认是当前页面,必须是以'/'开头的完整路径 | ||
| 100 | + imageUrl: '', //自定义图片路径,可以是本地文件路径、代码包文件路径或者网络图片路径,支持PNG及JPG,不传入 imageUrl 则使用默认截图。显示图片长宽比是 5:4 | ||
| 101 | + success: function (res) { | ||
| 102 | + // 转发成功之后的回调 | ||
| 103 | + if (res.errMsg == 'shareAppMessage:ok') { | ||
| 104 | + // | ||
| 105 | + } | ||
| 106 | + }, | ||
| 107 | + fail: function () { | ||
| 108 | + // 转发失败之后的回调 | ||
| 109 | + if (res.errMsg == 'shareAppMessage:fail cancel') { | ||
| 110 | + // 用户取消转发 | ||
| 111 | + } else if (res.errMsg == 'shareAppMessage:fail') { | ||
| 112 | + // 转发失败,其中 detail message 为详细失败信息 | ||
| 113 | + } | ||
| 114 | + }, | ||
| 115 | + complete: function () { | ||
| 116 | + // 转发结束之后的回调(转发成不成功都会执行) | ||
| 117 | + } | ||
| 118 | + } | ||
| 119 | + // 来自页面内的按钮的转发 | ||
| 120 | + // if (options.from == 'button') { | ||
| 121 | + // var eData = options.target.dataset; | ||
| 122 | + // // 此处可以修改 shareObj 中的内容 | ||
| 123 | + // shareObj.path = '/pages/goods/goods?goodId=' + eData.id; | ||
| 124 | + // } | ||
| 125 | + // 返回shareObj | ||
| 126 | + return shareObj; | ||
| 127 | + } | ||
| 128 | +}; | ||
| 129 | +</script> |
src/stores/counter.js
0 → 100755
| 1 | +// https://pinia.esm.dev/introduction.html | ||
| 2 | +import { defineStore } from 'pinia' | ||
| 3 | + | ||
| 4 | +export const useCounterStore = defineStore('counter', { | ||
| 5 | + state: () => { | ||
| 6 | + return { count: 0 } | ||
| 7 | + }, | ||
| 8 | + // could also be defined as | ||
| 9 | + // state: () => ({ count: 0 }) | ||
| 10 | + actions: { | ||
| 11 | + increment() { | ||
| 12 | + this.count++ | ||
| 13 | + }, | ||
| 14 | + }, | ||
| 15 | +}) | ||
| 16 | + | ||
| 17 | +// You can even use a function (similar to a component setup()) to define a Store for more advanced use cases: | ||
| 18 | +// export const useCounterStore = defineStore('counter', () => { | ||
| 19 | +// const count = ref(0) | ||
| 20 | +// | ||
| 21 | +// function increment() { | ||
| 22 | +// count.value++ | ||
| 23 | +// } | ||
| 24 | +// | ||
| 25 | +// return {count, increment} | ||
| 26 | +// }) | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/stores/host.js
0 → 100755
| 1 | +/* | ||
| 2 | + * @Date: 2022-10-28 14:34:22 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2022-10-28 15:12:55 | ||
| 5 | + * @FilePath: /swx/src/stores/host.js | ||
| 6 | + * @Description: 缓存主办方ID | ||
| 7 | + */ | ||
| 8 | +import { defineStore } from 'pinia' | ||
| 9 | + | ||
| 10 | +export const hostStore = defineStore('host', { | ||
| 11 | + state: () => { | ||
| 12 | + return { | ||
| 13 | + id: '', | ||
| 14 | + join_id: '' | ||
| 15 | + } | ||
| 16 | + }, | ||
| 17 | + actions: { | ||
| 18 | + add (id) { | ||
| 19 | + this.id = id | ||
| 20 | + }, | ||
| 21 | + addJoin (id) { | ||
| 22 | + this.join_id = id | ||
| 23 | + }, | ||
| 24 | + }, | ||
| 25 | +}) |
src/stores/router.js
0 → 100755
| 1 | +/* | ||
| 2 | + * @Date: 2022-10-28 14:34:22 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2022-11-01 13:27:09 | ||
| 5 | + * @FilePath: /swx/src/stores/router.js | ||
| 6 | + * @Description: 缓存路由信息 | ||
| 7 | + */ | ||
| 8 | +import { defineStore } from 'pinia' | ||
| 9 | + | ||
| 10 | +export const routerStore = defineStore('router', { | ||
| 11 | + state: () => { | ||
| 12 | + return { | ||
| 13 | + url: '', | ||
| 14 | + } | ||
| 15 | + }, | ||
| 16 | + actions: { | ||
| 17 | + add (path) { | ||
| 18 | + this.url = path | ||
| 19 | + }, | ||
| 20 | + remove () { | ||
| 21 | + this.url = '' | ||
| 22 | + }, | ||
| 23 | + }, | ||
| 24 | +}) |
src/utils/config.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2022-09-19 14:11:06 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2024-01-15 17:07:14 | ||
| 5 | + * @FilePath: /meihuaApp/src/utils/config.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +// TAG:服务器环境配置 | ||
| 9 | +// const BASE_URL = "https://oa-dev.onwall.cn"; // 测试服务器 | ||
| 10 | +const BASE_URL = "https://oa.onwall.cn"; // 正式服务器 | ||
| 11 | + | ||
| 12 | +export default BASE_URL |
src/utils/mixin.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2022-10-13 22:36:08 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2023-12-22 18:23:08 | ||
| 5 | + * @FilePath: /meihuaApp/src/utils/mixin.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +import { getSessionId, setSessionId, clearSessionId } from './request'; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 全局mixin,提供sessionid管理功能 | ||
| 12 | + * 注意:sessionid现在由request.js自动管理,无需手动设置 | ||
| 13 | + */ | ||
| 14 | +export default { | ||
| 15 | + // 初始化设置 | ||
| 16 | + init: { | ||
| 17 | + created () { | ||
| 18 | + // sessionid现在由request.js的拦截器自动管理 | ||
| 19 | + // 这里可以添加其他初始化逻辑 | ||
| 20 | + } | ||
| 21 | + } | ||
| 22 | +}; | ||
| 23 | + | ||
| 24 | +// 导出sessionid管理工具函数,供其他组件使用 | ||
| 25 | +export { getSessionId, setSessionId, clearSessionId } |
src/utils/moment.min.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2022-10-31 16:39:25 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2022-10-31 16:48:38 | ||
| 5 | + * @FilePath: /swx/src/utils/moment.min.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +!function (e, t) { "object" == typeof exports && "undefined" != typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : e.moment = t() }(this, function () { "use strict"; var e, i; function c() { return e.apply(null, arguments) } function o(e) { return e instanceof Array || "[object Array]" === Object.prototype.toString.call(e) } function u(e) { return null != e && "[object Object]" === Object.prototype.toString.call(e) } function l(e) { return void 0 === e } function h(e) { return "number" == typeof e || "[object Number]" === Object.prototype.toString.call(e) } function d(e) { return e instanceof Date || "[object Date]" === Object.prototype.toString.call(e) } function f(e, t) { var n, s = []; for (n = 0; n < e.length; ++n)s.push(t(e[n], n)); return s } function m(e, t) { return Object.prototype.hasOwnProperty.call(e, t) } function _(e, t) { for (var n in t) m(t, n) && (e[n] = t[n]); return m(t, "toString") && (e.toString = t.toString), m(t, "valueOf") && (e.valueOf = t.valueOf), e } function y(e, t, n, s) { return Tt(e, t, n, s, !0).utc() } function g(e) { return null == e._pf && (e._pf = { empty: !1, unusedTokens: [], unusedInput: [], overflow: -2, charsLeftOver: 0, nullInput: !1, invalidMonth: null, invalidFormat: !1, userInvalidated: !1, iso: !1, parsedDateParts: [], meridiem: null, rfc2822: !1, weekdayMismatch: !1 }), e._pf } function v(e) { if (null == e._isValid) { var t = g(e), n = i.call(t.parsedDateParts, function (e) { return null != e }), s = !isNaN(e._d.getTime()) && t.overflow < 0 && !t.empty && !t.invalidMonth && !t.invalidWeekday && !t.weekdayMismatch && !t.nullInput && !t.invalidFormat && !t.userInvalidated && (!t.meridiem || t.meridiem && n); if (e._strict && (s = s && 0 === t.charsLeftOver && 0 === t.unusedTokens.length && void 0 === t.bigHour), null != Object.isFrozen && Object.isFrozen(e)) return s; e._isValid = s } return e._isValid } function p(e) { var t = y(NaN); return null != e ? _(g(t), e) : g(t).userInvalidated = !0, t } i = Array.prototype.some ? Array.prototype.some : function (e) { for (var t = Object(this), n = t.length >>> 0, s = 0; s < n; s++)if (s in t && e.call(this, t[s], s, t)) return !0; return !1 }; var r = c.momentProperties = []; function w(e, t) { var n, s, i; if (l(t._isAMomentObject) || (e._isAMomentObject = t._isAMomentObject), l(t._i) || (e._i = t._i), l(t._f) || (e._f = t._f), l(t._l) || (e._l = t._l), l(t._strict) || (e._strict = t._strict), l(t._tzm) || (e._tzm = t._tzm), l(t._isUTC) || (e._isUTC = t._isUTC), l(t._offset) || (e._offset = t._offset), l(t._pf) || (e._pf = g(t)), l(t._locale) || (e._locale = t._locale), 0 < r.length) for (n = 0; n < r.length; n++)l(i = t[s = r[n]]) || (e[s] = i); return e } var t = !1; function M(e) { w(this, e), this._d = new Date(null != e._d ? e._d.getTime() : NaN), this.isValid() || (this._d = new Date(NaN)), !1 === t && (t = !0, c.updateOffset(this), t = !1) } function k(e) { return e instanceof M || null != e && null != e._isAMomentObject } function S(e) { return e < 0 ? Math.ceil(e) || 0 : Math.floor(e) } function D(e) { var t = +e, n = 0; return 0 !== t && isFinite(t) && (n = S(t)), n } function a(e, t, n) { var s, i = Math.min(e.length, t.length), r = Math.abs(e.length - t.length), a = 0; for (s = 0; s < i; s++)(n && e[s] !== t[s] || !n && D(e[s]) !== D(t[s])) && a++; return a + r } function Y(e) { !1 === c.suppressDeprecationWarnings && "undefined" != typeof console && console.warn && console.warn("Deprecation warning: " + e) } function n(i, r) { var a = !0; return _(function () { if (null != c.deprecationHandler && c.deprecationHandler(null, i), a) { for (var e, t = [], n = 0; n < arguments.length; n++) { if (e = "", "object" == typeof arguments[n]) { for (var s in e += "\n[" + n + "] ", arguments[0]) e += s + ": " + arguments[0][s] + ", "; e = e.slice(0, -2) } else e = arguments[n]; t.push(e) } Y(i + "\nArguments: " + Array.prototype.slice.call(t).join("") + "\n" + (new Error).stack), a = !1 } return r.apply(this, arguments) }, r) } var s, O = {}; function T(e, t) { null != c.deprecationHandler && c.deprecationHandler(e, t), O[e] || (Y(t), O[e] = !0) } function b(e) { return e instanceof Function || "[object Function]" === Object.prototype.toString.call(e) } function x(e, t) { var n, s = _({}, e); for (n in t) m(t, n) && (u(e[n]) && u(t[n]) ? (s[n] = {}, _(s[n], e[n]), _(s[n], t[n])) : null != t[n] ? s[n] = t[n] : delete s[n]); for (n in e) m(e, n) && !m(t, n) && u(e[n]) && (s[n] = _({}, s[n])); return s } function P(e) { null != e && this.set(e) } c.suppressDeprecationWarnings = !1, c.deprecationHandler = null, s = Object.keys ? Object.keys : function (e) { var t, n = []; for (t in e) m(e, t) && n.push(t); return n }; var W = {}; function C(e, t) { var n = e.toLowerCase(); W[n] = W[n + "s"] = W[t] = e } function H(e) { return "string" == typeof e ? W[e] || W[e.toLowerCase()] : void 0 } function R(e) { var t, n, s = {}; for (n in e) m(e, n) && (t = H(n)) && (s[t] = e[n]); return s } var U = {}; function F(e, t) { U[e] = t } function L(e, t, n) { var s = "" + Math.abs(e), i = t - s.length; return (0 <= e ? n ? "+" : "" : "-") + Math.pow(10, Math.max(0, i)).toString().substr(1) + s } var N = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g, G = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, V = {}, E = {}; function I(e, t, n, s) { var i = s; "string" == typeof s && (i = function () { return this[s]() }), e && (E[e] = i), t && (E[t[0]] = function () { return L(i.apply(this, arguments), t[1], t[2]) }), n && (E[n] = function () { return this.localeData().ordinal(i.apply(this, arguments), e) }) } function A(e, t) { return e.isValid() ? (t = j(t, e.localeData()), V[t] = V[t] || function (s) { var e, i, t, r = s.match(N); for (e = 0, i = r.length; e < i; e++)E[r[e]] ? r[e] = E[r[e]] : r[e] = (t = r[e]).match(/\[[\s\S]/) ? t.replace(/^\[|\]$/g, "") : t.replace(/\\/g, ""); return function (e) { var t, n = ""; for (t = 0; t < i; t++)n += b(r[t]) ? r[t].call(e, s) : r[t]; return n } }(t), V[t](e)) : e.localeData().invalidDate() } function j(e, t) { var n = 5; function s(e) { return t.longDateFormat(e) || e } for (G.lastIndex = 0; 0 <= n && G.test(e);)e = e.replace(G, s), G.lastIndex = 0, n -= 1; return e } var Z = /\d/, z = /\d\d/, $ = /\d{3}/, q = /\d{4}/, J = /[+-]?\d{6}/, B = /\d\d?/, Q = /\d\d\d\d?/, X = /\d\d\d\d\d\d?/, K = /\d{1,3}/, ee = /\d{1,4}/, te = /[+-]?\d{1,6}/, ne = /\d+/, se = /[+-]?\d+/, ie = /Z|[+-]\d\d:?\d\d/gi, re = /Z|[+-]\d\d(?::?\d\d)?/gi, ae = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i, oe = {}; function ue(e, n, s) { oe[e] = b(n) ? n : function (e, t) { return e && s ? s : n } } function le(e, t) { return m(oe, e) ? oe[e](t._strict, t._locale) : new RegExp(he(e.replace("\\", "").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (e, t, n, s, i) { return t || n || s || i }))) } function he(e) { return e.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") } var de = {}; function ce(e, n) { var t, s = n; for ("string" == typeof e && (e = [e]), h(n) && (s = function (e, t) { t[n] = D(e) }), t = 0; t < e.length; t++)de[e[t]] = s } function fe(e, i) { ce(e, function (e, t, n, s) { n._w = n._w || {}, i(e, n._w, n, s) }) } var me = 0, _e = 1, ye = 2, ge = 3, ve = 4, pe = 5, we = 6, Me = 7, ke = 8; function Se(e) { return De(e) ? 366 : 365 } function De(e) { return e % 4 == 0 && e % 100 != 0 || e % 400 == 0 } I("Y", 0, 0, function () { var e = this.year(); return e <= 9999 ? "" + e : "+" + e }), I(0, ["YY", 2], 0, function () { return this.year() % 100 }), I(0, ["YYYY", 4], 0, "year"), I(0, ["YYYYY", 5], 0, "year"), I(0, ["YYYYYY", 6, !0], 0, "year"), C("year", "y"), F("year", 1), ue("Y", se), ue("YY", B, z), ue("YYYY", ee, q), ue("YYYYY", te, J), ue("YYYYYY", te, J), ce(["YYYYY", "YYYYYY"], me), ce("YYYY", function (e, t) { t[me] = 2 === e.length ? c.parseTwoDigitYear(e) : D(e) }), ce("YY", function (e, t) { t[me] = c.parseTwoDigitYear(e) }), ce("Y", function (e, t) { t[me] = parseInt(e, 10) }), c.parseTwoDigitYear = function (e) { return D(e) + (68 < D(e) ? 1900 : 2e3) }; var Ye, Oe = Te("FullYear", !0); function Te(t, n) { return function (e) { return null != e ? (xe(this, t, e), c.updateOffset(this, n), this) : be(this, t) } } function be(e, t) { return e.isValid() ? e._d["get" + (e._isUTC ? "UTC" : "") + t]() : NaN } function xe(e, t, n) { e.isValid() && !isNaN(n) && ("FullYear" === t && De(e.year()) && 1 === e.month() && 29 === e.date() ? e._d["set" + (e._isUTC ? "UTC" : "") + t](n, e.month(), Pe(n, e.month())) : e._d["set" + (e._isUTC ? "UTC" : "") + t](n)) } function Pe(e, t) { if (isNaN(e) || isNaN(t)) return NaN; var n, s = (t % (n = 12) + n) % n; return e += (t - s) / 12, 1 === s ? De(e) ? 29 : 28 : 31 - s % 7 % 2 } Ye = Array.prototype.indexOf ? Array.prototype.indexOf : function (e) { var t; for (t = 0; t < this.length; ++t)if (this[t] === e) return t; return -1 }, I("M", ["MM", 2], "Mo", function () { return this.month() + 1 }), I("MMM", 0, 0, function (e) { return this.localeData().monthsShort(this, e) }), I("MMMM", 0, 0, function (e) { return this.localeData().months(this, e) }), C("month", "M"), F("month", 8), ue("M", B), ue("MM", B, z), ue("MMM", function (e, t) { return t.monthsShortRegex(e) }), ue("MMMM", function (e, t) { return t.monthsRegex(e) }), ce(["M", "MM"], function (e, t) { t[_e] = D(e) - 1 }), ce(["MMM", "MMMM"], function (e, t, n, s) { var i = n._locale.monthsParse(e, s, n._strict); null != i ? t[_e] = i : g(n).invalidMonth = e }); var We = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/, Ce = "January_February_March_April_May_June_July_August_September_October_November_December".split("_"); var He = "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"); function Re(e, t) { var n; if (!e.isValid()) return e; if ("string" == typeof t) if (/^\d+$/.test(t)) t = D(t); else if (!h(t = e.localeData().monthsParse(t))) return e; return n = Math.min(e.date(), Pe(e.year(), t)), e._d["set" + (e._isUTC ? "UTC" : "") + "Month"](t, n), e } function Ue(e) { return null != e ? (Re(this, e), c.updateOffset(this, !0), this) : be(this, "Month") } var Fe = ae; var Le = ae; function Ne() { function e(e, t) { return t.length - e.length } var t, n, s = [], i = [], r = []; for (t = 0; t < 12; t++)n = y([2e3, t]), s.push(this.monthsShort(n, "")), i.push(this.months(n, "")), r.push(this.months(n, "")), r.push(this.monthsShort(n, "")); for (s.sort(e), i.sort(e), r.sort(e), t = 0; t < 12; t++)s[t] = he(s[t]), i[t] = he(i[t]); for (t = 0; t < 24; t++)r[t] = he(r[t]); this._monthsRegex = new RegExp("^(" + r.join("|") + ")", "i"), this._monthsShortRegex = this._monthsRegex, this._monthsStrictRegex = new RegExp("^(" + i.join("|") + ")", "i"), this._monthsShortStrictRegex = new RegExp("^(" + s.join("|") + ")", "i") } function Ge(e) { var t; if (e < 100 && 0 <= e) { var n = Array.prototype.slice.call(arguments); n[0] = e + 400, t = new Date(Date.UTC.apply(null, n)), isFinite(t.getUTCFullYear()) && t.setUTCFullYear(e) } else t = new Date(Date.UTC.apply(null, arguments)); return t } function Ve(e, t, n) { var s = 7 + t - n; return -((7 + Ge(e, 0, s).getUTCDay() - t) % 7) + s - 1 } function Ee(e, t, n, s, i) { var r, a, o = 1 + 7 * (t - 1) + (7 + n - s) % 7 + Ve(e, s, i); return a = o <= 0 ? Se(r = e - 1) + o : o > Se(e) ? (r = e + 1, o - Se(e)) : (r = e, o), { year: r, dayOfYear: a } } function Ie(e, t, n) { var s, i, r = Ve(e.year(), t, n), a = Math.floor((e.dayOfYear() - r - 1) / 7) + 1; return a < 1 ? s = a + Ae(i = e.year() - 1, t, n) : a > Ae(e.year(), t, n) ? (s = a - Ae(e.year(), t, n), i = e.year() + 1) : (i = e.year(), s = a), { week: s, year: i } } function Ae(e, t, n) { var s = Ve(e, t, n), i = Ve(e + 1, t, n); return (Se(e) - s + i) / 7 } I("w", ["ww", 2], "wo", "week"), I("W", ["WW", 2], "Wo", "isoWeek"), C("week", "w"), C("isoWeek", "W"), F("week", 5), F("isoWeek", 5), ue("w", B), ue("ww", B, z), ue("W", B), ue("WW", B, z), fe(["w", "ww", "W", "WW"], function (e, t, n, s) { t[s.substr(0, 1)] = D(e) }); function je(e, t) { return e.slice(t, 7).concat(e.slice(0, t)) } I("d", 0, "do", "day"), I("dd", 0, 0, function (e) { return this.localeData().weekdaysMin(this, e) }), I("ddd", 0, 0, function (e) { return this.localeData().weekdaysShort(this, e) }), I("dddd", 0, 0, function (e) { return this.localeData().weekdays(this, e) }), I("e", 0, 0, "weekday"), I("E", 0, 0, "isoWeekday"), C("day", "d"), C("weekday", "e"), C("isoWeekday", "E"), F("day", 11), F("weekday", 11), F("isoWeekday", 11), ue("d", B), ue("e", B), ue("E", B), ue("dd", function (e, t) { return t.weekdaysMinRegex(e) }), ue("ddd", function (e, t) { return t.weekdaysShortRegex(e) }), ue("dddd", function (e, t) { return t.weekdaysRegex(e) }), fe(["dd", "ddd", "dddd"], function (e, t, n, s) { var i = n._locale.weekdaysParse(e, s, n._strict); null != i ? t.d = i : g(n).invalidWeekday = e }), fe(["d", "e", "E"], function (e, t, n, s) { t[s] = D(e) }); var Ze = "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"); var ze = "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"); var $e = "Su_Mo_Tu_We_Th_Fr_Sa".split("_"); var qe = ae; var Je = ae; var Be = ae; function Qe() { function e(e, t) { return t.length - e.length } var t, n, s, i, r, a = [], o = [], u = [], l = []; for (t = 0; t < 7; t++)n = y([2e3, 1]).day(t), s = this.weekdaysMin(n, ""), i = this.weekdaysShort(n, ""), r = this.weekdays(n, ""), a.push(s), o.push(i), u.push(r), l.push(s), l.push(i), l.push(r); for (a.sort(e), o.sort(e), u.sort(e), l.sort(e), t = 0; t < 7; t++)o[t] = he(o[t]), u[t] = he(u[t]), l[t] = he(l[t]); this._weekdaysRegex = new RegExp("^(" + l.join("|") + ")", "i"), this._weekdaysShortRegex = this._weekdaysRegex, this._weekdaysMinRegex = this._weekdaysRegex, this._weekdaysStrictRegex = new RegExp("^(" + u.join("|") + ")", "i"), this._weekdaysShortStrictRegex = new RegExp("^(" + o.join("|") + ")", "i"), this._weekdaysMinStrictRegex = new RegExp("^(" + a.join("|") + ")", "i") } function Xe() { return this.hours() % 12 || 12 } function Ke(e, t) { I(e, 0, 0, function () { return this.localeData().meridiem(this.hours(), this.minutes(), t) }) } function et(e, t) { return t._meridiemParse } I("H", ["HH", 2], 0, "hour"), I("h", ["hh", 2], 0, Xe), I("k", ["kk", 2], 0, function () { return this.hours() || 24 }), I("hmm", 0, 0, function () { return "" + Xe.apply(this) + L(this.minutes(), 2) }), I("hmmss", 0, 0, function () { return "" + Xe.apply(this) + L(this.minutes(), 2) + L(this.seconds(), 2) }), I("Hmm", 0, 0, function () { return "" + this.hours() + L(this.minutes(), 2) }), I("Hmmss", 0, 0, function () { return "" + this.hours() + L(this.minutes(), 2) + L(this.seconds(), 2) }), Ke("a", !0), Ke("A", !1), C("hour", "h"), F("hour", 13), ue("a", et), ue("A", et), ue("H", B), ue("h", B), ue("k", B), ue("HH", B, z), ue("hh", B, z), ue("kk", B, z), ue("hmm", Q), ue("hmmss", X), ue("Hmm", Q), ue("Hmmss", X), ce(["H", "HH"], ge), ce(["k", "kk"], function (e, t, n) { var s = D(e); t[ge] = 24 === s ? 0 : s }), ce(["a", "A"], function (e, t, n) { n._isPm = n._locale.isPM(e), n._meridiem = e }), ce(["h", "hh"], function (e, t, n) { t[ge] = D(e), g(n).bigHour = !0 }), ce("hmm", function (e, t, n) { var s = e.length - 2; t[ge] = D(e.substr(0, s)), t[ve] = D(e.substr(s)), g(n).bigHour = !0 }), ce("hmmss", function (e, t, n) { var s = e.length - 4, i = e.length - 2; t[ge] = D(e.substr(0, s)), t[ve] = D(e.substr(s, 2)), t[pe] = D(e.substr(i)), g(n).bigHour = !0 }), ce("Hmm", function (e, t, n) { var s = e.length - 2; t[ge] = D(e.substr(0, s)), t[ve] = D(e.substr(s)) }), ce("Hmmss", function (e, t, n) { var s = e.length - 4, i = e.length - 2; t[ge] = D(e.substr(0, s)), t[ve] = D(e.substr(s, 2)), t[pe] = D(e.substr(i)) }); var tt, nt = Te("Hours", !0), st = { calendar: { sameDay: "[Today at] LT", nextDay: "[Tomorrow at] LT", nextWeek: "dddd [at] LT", lastDay: "[Yesterday at] LT", lastWeek: "[Last] dddd [at] LT", sameElse: "L" }, longDateFormat: { LTS: "h:mm:ss A", LT: "h:mm A", L: "MM/DD/YYYY", LL: "MMMM D, YYYY", LLL: "MMMM D, YYYY h:mm A", LLLL: "dddd, MMMM D, YYYY h:mm A" }, invalidDate: "Invalid date", ordinal: "%d", dayOfMonthOrdinalParse: /\d{1,2}/, relativeTime: { future: "in %s", past: "%s ago", s: "a few seconds", ss: "%d seconds", m: "a minute", mm: "%d minutes", h: "an hour", hh: "%d hours", d: "a day", dd: "%d days", M: "a month", MM: "%d months", y: "a year", yy: "%d years" }, months: Ce, monthsShort: He, week: { dow: 0, doy: 6 }, weekdays: Ze, weekdaysMin: $e, weekdaysShort: ze, meridiemParse: /[ap]\.?m?\.?/i }, it = {}, rt = {}; function at(e) { return e ? e.toLowerCase().replace("_", "-") : e } function ot(e) { var t = null; if (!it[e] && "undefined" != typeof module && module && module.exports) try { t = tt._abbr, require("./" + e), ut(t) } catch (e) { } return it[e] } function ut(e, t) { var n; return e && ((n = l(t) ? ht(e) : lt(e, t)) ? tt = n : "undefined" != typeof console && console.warn && console.warn("Locale " + e + " not found. Did you forget to load it?")), tt._abbr } function lt(e, t) { if (null === t) return delete it[e], null; var n, s = st; if (t.abbr = e, null != it[e]) T("defineLocaleOverride", "use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."), s = it[e]._config; else if (null != t.parentLocale) if (null != it[t.parentLocale]) s = it[t.parentLocale]._config; else { if (null == (n = ot(t.parentLocale))) return rt[t.parentLocale] || (rt[t.parentLocale] = []), rt[t.parentLocale].push({ name: e, config: t }), null; s = n._config } return it[e] = new P(x(s, t)), rt[e] && rt[e].forEach(function (e) { lt(e.name, e.config) }), ut(e), it[e] } function ht(e) { var t; if (e && e._locale && e._locale._abbr && (e = e._locale._abbr), !e) return tt; if (!o(e)) { if (t = ot(e)) return t; e = [e] } return function (e) { for (var t, n, s, i, r = 0; r < e.length;) { for (t = (i = at(e[r]).split("-")).length, n = (n = at(e[r + 1])) ? n.split("-") : null; 0 < t;) { if (s = ot(i.slice(0, t).join("-"))) return s; if (n && n.length >= t && a(i, n, !0) >= t - 1) break; t-- } r++ } return tt }(e) } function dt(e) { var t, n = e._a; return n && -2 === g(e).overflow && (t = n[_e] < 0 || 11 < n[_e] ? _e : n[ye] < 1 || n[ye] > Pe(n[me], n[_e]) ? ye : n[ge] < 0 || 24 < n[ge] || 24 === n[ge] && (0 !== n[ve] || 0 !== n[pe] || 0 !== n[we]) ? ge : n[ve] < 0 || 59 < n[ve] ? ve : n[pe] < 0 || 59 < n[pe] ? pe : n[we] < 0 || 999 < n[we] ? we : -1, g(e)._overflowDayOfYear && (t < me || ye < t) && (t = ye), g(e)._overflowWeeks && -1 === t && (t = Me), g(e)._overflowWeekday && -1 === t && (t = ke), g(e).overflow = t), e } function ct(e, t, n) { return null != e ? e : null != t ? t : n } function ft(e) { var t, n, s, i, r, a = []; if (!e._d) { var o, u; for (o = e, u = new Date(c.now()), s = o._useUTC ? [u.getUTCFullYear(), u.getUTCMonth(), u.getUTCDate()] : [u.getFullYear(), u.getMonth(), u.getDate()], e._w && null == e._a[ye] && null == e._a[_e] && function (e) { var t, n, s, i, r, a, o, u; if (null != (t = e._w).GG || null != t.W || null != t.E) r = 1, a = 4, n = ct(t.GG, e._a[me], Ie(bt(), 1, 4).year), s = ct(t.W, 1), ((i = ct(t.E, 1)) < 1 || 7 < i) && (u = !0); else { r = e._locale._week.dow, a = e._locale._week.doy; var l = Ie(bt(), r, a); n = ct(t.gg, e._a[me], l.year), s = ct(t.w, l.week), null != t.d ? ((i = t.d) < 0 || 6 < i) && (u = !0) : null != t.e ? (i = t.e + r, (t.e < 0 || 6 < t.e) && (u = !0)) : i = r } s < 1 || s > Ae(n, r, a) ? g(e)._overflowWeeks = !0 : null != u ? g(e)._overflowWeekday = !0 : (o = Ee(n, s, i, r, a), e._a[me] = o.year, e._dayOfYear = o.dayOfYear) }(e), null != e._dayOfYear && (r = ct(e._a[me], s[me]), (e._dayOfYear > Se(r) || 0 === e._dayOfYear) && (g(e)._overflowDayOfYear = !0), n = Ge(r, 0, e._dayOfYear), e._a[_e] = n.getUTCMonth(), e._a[ye] = n.getUTCDate()), t = 0; t < 3 && null == e._a[t]; ++t)e._a[t] = a[t] = s[t]; for (; t < 7; t++)e._a[t] = a[t] = null == e._a[t] ? 2 === t ? 1 : 0 : e._a[t]; 24 === e._a[ge] && 0 === e._a[ve] && 0 === e._a[pe] && 0 === e._a[we] && (e._nextDay = !0, e._a[ge] = 0), e._d = (e._useUTC ? Ge : function (e, t, n, s, i, r, a) { var o; return e < 100 && 0 <= e ? (o = new Date(e + 400, t, n, s, i, r, a), isFinite(o.getFullYear()) && o.setFullYear(e)) : o = new Date(e, t, n, s, i, r, a), o }).apply(null, a), i = e._useUTC ? e._d.getUTCDay() : e._d.getDay(), null != e._tzm && e._d.setUTCMinutes(e._d.getUTCMinutes() - e._tzm), e._nextDay && (e._a[ge] = 24), e._w && void 0 !== e._w.d && e._w.d !== i && (g(e).weekdayMismatch = !0) } } var mt = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/, _t = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/, yt = /Z|[+-]\d\d(?::?\d\d)?/, gt = [["YYYYYY-MM-DD", /[+-]\d{6}-\d\d-\d\d/], ["YYYY-MM-DD", /\d{4}-\d\d-\d\d/], ["GGGG-[W]WW-E", /\d{4}-W\d\d-\d/], ["GGGG-[W]WW", /\d{4}-W\d\d/, !1], ["YYYY-DDD", /\d{4}-\d{3}/], ["YYYY-MM", /\d{4}-\d\d/, !1], ["YYYYYYMMDD", /[+-]\d{10}/], ["YYYYMMDD", /\d{8}/], ["GGGG[W]WWE", /\d{4}W\d{3}/], ["GGGG[W]WW", /\d{4}W\d{2}/, !1], ["YYYYDDD", /\d{7}/]], vt = [["HH:mm:ss.SSSS", /\d\d:\d\d:\d\d\.\d+/], ["HH:mm:ss,SSSS", /\d\d:\d\d:\d\d,\d+/], ["HH:mm:ss", /\d\d:\d\d:\d\d/], ["HH:mm", /\d\d:\d\d/], ["HHmmss.SSSS", /\d\d\d\d\d\d\.\d+/], ["HHmmss,SSSS", /\d\d\d\d\d\d,\d+/], ["HHmmss", /\d\d\d\d\d\d/], ["HHmm", /\d\d\d\d/], ["HH", /\d\d/]], pt = /^\/?Date\((\-?\d+)/i; function wt(e) { var t, n, s, i, r, a, o = e._i, u = mt.exec(o) || _t.exec(o); if (u) { for (g(e).iso = !0, t = 0, n = gt.length; t < n; t++)if (gt[t][1].exec(u[1])) { i = gt[t][0], s = !1 !== gt[t][2]; break } if (null == i) return void (e._isValid = !1); if (u[3]) { for (t = 0, n = vt.length; t < n; t++)if (vt[t][1].exec(u[3])) { r = (u[2] || " ") + vt[t][0]; break } if (null == r) return void (e._isValid = !1) } if (!s && null != r) return void (e._isValid = !1); if (u[4]) { if (!yt.exec(u[4])) return void (e._isValid = !1); a = "Z" } e._f = i + (r || "") + (a || ""), Yt(e) } else e._isValid = !1 } var Mt = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/; function kt(e, t, n, s, i, r) { var a = [function (e) { var t = parseInt(e, 10); { if (t <= 49) return 2e3 + t; if (t <= 999) return 1900 + t } return t }(e), He.indexOf(t), parseInt(n, 10), parseInt(s, 10), parseInt(i, 10)]; return r && a.push(parseInt(r, 10)), a } var St = { UT: 0, GMT: 0, EDT: -240, EST: -300, CDT: -300, CST: -360, MDT: -360, MST: -420, PDT: -420, PST: -480 }; function Dt(e) { var t, n, s, i = Mt.exec(e._i.replace(/\([^)]*\)|[\n\t]/g, " ").replace(/(\s\s+)/g, " ").replace(/^\s\s*/, "").replace(/\s\s*$/, "")); if (i) { var r = kt(i[4], i[3], i[2], i[5], i[6], i[7]); if (t = i[1], n = r, s = e, t && ze.indexOf(t) !== new Date(n[0], n[1], n[2]).getDay() && (g(s).weekdayMismatch = !0, !(s._isValid = !1))) return; e._a = r, e._tzm = function (e, t, n) { if (e) return St[e]; if (t) return 0; var s = parseInt(n, 10), i = s % 100; return (s - i) / 100 * 60 + i }(i[8], i[9], i[10]), e._d = Ge.apply(null, e._a), e._d.setUTCMinutes(e._d.getUTCMinutes() - e._tzm), g(e).rfc2822 = !0 } else e._isValid = !1 } function Yt(e) { if (e._f !== c.ISO_8601) if (e._f !== c.RFC_2822) { e._a = [], g(e).empty = !0; var t, n, s, i, r, a, o, u, l = "" + e._i, h = l.length, d = 0; for (s = j(e._f, e._locale).match(N) || [], t = 0; t < s.length; t++)i = s[t], (n = (l.match(le(i, e)) || [])[0]) && (0 < (r = l.substr(0, l.indexOf(n))).length && g(e).unusedInput.push(r), l = l.slice(l.indexOf(n) + n.length), d += n.length), E[i] ? (n ? g(e).empty = !1 : g(e).unusedTokens.push(i), a = i, u = e, null != (o = n) && m(de, a) && de[a](o, u._a, u, a)) : e._strict && !n && g(e).unusedTokens.push(i); g(e).charsLeftOver = h - d, 0 < l.length && g(e).unusedInput.push(l), e._a[ge] <= 12 && !0 === g(e).bigHour && 0 < e._a[ge] && (g(e).bigHour = void 0), g(e).parsedDateParts = e._a.slice(0), g(e).meridiem = e._meridiem, e._a[ge] = function (e, t, n) { var s; if (null == n) return t; return null != e.meridiemHour ? e.meridiemHour(t, n) : (null != e.isPM && ((s = e.isPM(n)) && t < 12 && (t += 12), s || 12 !== t || (t = 0)), t) }(e._locale, e._a[ge], e._meridiem), ft(e), dt(e) } else Dt(e); else wt(e) } function Ot(e) { var t, n, s, i, r = e._i, a = e._f; return e._locale = e._locale || ht(e._l), null === r || void 0 === a && "" === r ? p({ nullInput: !0 }) : ("string" == typeof r && (e._i = r = e._locale.preparse(r)), k(r) ? new M(dt(r)) : (d(r) ? e._d = r : o(a) ? function (e) { var t, n, s, i, r; if (0 === e._f.length) return g(e).invalidFormat = !0, e._d = new Date(NaN); for (i = 0; i < e._f.length; i++)r = 0, t = w({}, e), null != e._useUTC && (t._useUTC = e._useUTC), t._f = e._f[i], Yt(t), v(t) && (r += g(t).charsLeftOver, r += 10 * g(t).unusedTokens.length, g(t).score = r, (null == s || r < s) && (s = r, n = t)); _(e, n || t) }(e) : a ? Yt(e) : l(n = (t = e)._i) ? t._d = new Date(c.now()) : d(n) ? t._d = new Date(n.valueOf()) : "string" == typeof n ? (s = t, null === (i = pt.exec(s._i)) ? (wt(s), !1 === s._isValid && (delete s._isValid, Dt(s), !1 === s._isValid && (delete s._isValid, c.createFromInputFallback(s)))) : s._d = new Date(+i[1])) : o(n) ? (t._a = f(n.slice(0), function (e) { return parseInt(e, 10) }), ft(t)) : u(n) ? function (e) { if (!e._d) { var t = R(e._i); e._a = f([t.year, t.month, t.day || t.date, t.hour, t.minute, t.second, t.millisecond], function (e) { return e && parseInt(e, 10) }), ft(e) } }(t) : h(n) ? t._d = new Date(n) : c.createFromInputFallback(t), v(e) || (e._d = null), e)) } function Tt(e, t, n, s, i) { var r, a = {}; return !0 !== n && !1 !== n || (s = n, n = void 0), (u(e) && function (e) { if (Object.getOwnPropertyNames) return 0 === Object.getOwnPropertyNames(e).length; var t; for (t in e) if (e.hasOwnProperty(t)) return !1; return !0 }(e) || o(e) && 0 === e.length) && (e = void 0), a._isAMomentObject = !0, a._useUTC = a._isUTC = i, a._l = n, a._i = e, a._f = t, a._strict = s, (r = new M(dt(Ot(a))))._nextDay && (r.add(1, "d"), r._nextDay = void 0), r } function bt(e, t, n, s) { return Tt(e, t, n, s, !1) } c.createFromInputFallback = n("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.", function (e) { e._d = new Date(e._i + (e._useUTC ? " UTC" : "")) }), c.ISO_8601 = function () { }, c.RFC_2822 = function () { }; var xt = n("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/", function () { var e = bt.apply(null, arguments); return this.isValid() && e.isValid() ? e < this ? this : e : p() }), Pt = n("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/", function () { var e = bt.apply(null, arguments); return this.isValid() && e.isValid() ? this < e ? this : e : p() }); function Wt(e, t) { var n, s; if (1 === t.length && o(t[0]) && (t = t[0]), !t.length) return bt(); for (n = t[0], s = 1; s < t.length; ++s)t[s].isValid() && !t[s][e](n) || (n = t[s]); return n } var Ct = ["year", "quarter", "month", "week", "day", "hour", "minute", "second", "millisecond"]; function Ht(e) { var t = R(e), n = t.year || 0, s = t.quarter || 0, i = t.month || 0, r = t.week || t.isoWeek || 0, a = t.day || 0, o = t.hour || 0, u = t.minute || 0, l = t.second || 0, h = t.millisecond || 0; this._isValid = function (e) { for (var t in e) if (-1 === Ye.call(Ct, t) || null != e[t] && isNaN(e[t])) return !1; for (var n = !1, s = 0; s < Ct.length; ++s)if (e[Ct[s]]) { if (n) return !1; parseFloat(e[Ct[s]]) !== D(e[Ct[s]]) && (n = !0) } return !0 }(t), this._milliseconds = +h + 1e3 * l + 6e4 * u + 1e3 * o * 60 * 60, this._days = +a + 7 * r, this._months = +i + 3 * s + 12 * n, this._data = {}, this._locale = ht(), this._bubble() } function Rt(e) { return e instanceof Ht } function Ut(e) { return e < 0 ? -1 * Math.round(-1 * e) : Math.round(e) } function Ft(e, n) { I(e, 0, 0, function () { var e = this.utcOffset(), t = "+"; return e < 0 && (e = -e, t = "-"), t + L(~~(e / 60), 2) + n + L(~~e % 60, 2) }) } Ft("Z", ":"), Ft("ZZ", ""), ue("Z", re), ue("ZZ", re), ce(["Z", "ZZ"], function (e, t, n) { n._useUTC = !0, n._tzm = Nt(re, e) }); var Lt = /([\+\-]|\d\d)/gi; function Nt(e, t) { var n = (t || "").match(e); if (null === n) return null; var s = ((n[n.length - 1] || []) + "").match(Lt) || ["-", 0, 0], i = 60 * s[1] + D(s[2]); return 0 === i ? 0 : "+" === s[0] ? i : -i } function Gt(e, t) { var n, s; return t._isUTC ? (n = t.clone(), s = (k(e) || d(e) ? e.valueOf() : bt(e).valueOf()) - n.valueOf(), n._d.setTime(n._d.valueOf() + s), c.updateOffset(n, !1), n) : bt(e).local() } function Vt(e) { return 15 * -Math.round(e._d.getTimezoneOffset() / 15) } function Et() { return !!this.isValid() && (this._isUTC && 0 === this._offset) } c.updateOffset = function () { }; var It = /^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/, At = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; function jt(e, t) { var n, s, i, r = e, a = null; return Rt(e) ? r = { ms: e._milliseconds, d: e._days, M: e._months } : h(e) ? (r = {}, t ? r[t] = e : r.milliseconds = e) : (a = It.exec(e)) ? (n = "-" === a[1] ? -1 : 1, r = { y: 0, d: D(a[ye]) * n, h: D(a[ge]) * n, m: D(a[ve]) * n, s: D(a[pe]) * n, ms: D(Ut(1e3 * a[we])) * n }) : (a = At.exec(e)) ? (n = "-" === a[1] ? -1 : 1, r = { y: Zt(a[2], n), M: Zt(a[3], n), w: Zt(a[4], n), d: Zt(a[5], n), h: Zt(a[6], n), m: Zt(a[7], n), s: Zt(a[8], n) }) : null == r ? r = {} : "object" == typeof r && ("from" in r || "to" in r) && (i = function (e, t) { var n; if (!e.isValid() || !t.isValid()) return { milliseconds: 0, months: 0 }; t = Gt(t, e), e.isBefore(t) ? n = zt(e, t) : ((n = zt(t, e)).milliseconds = -n.milliseconds, n.months = -n.months); return n }(bt(r.from), bt(r.to)), (r = {}).ms = i.milliseconds, r.M = i.months), s = new Ht(r), Rt(e) && m(e, "_locale") && (s._locale = e._locale), s } function Zt(e, t) { var n = e && parseFloat(e.replace(",", ".")); return (isNaN(n) ? 0 : n) * t } function zt(e, t) { var n = {}; return n.months = t.month() - e.month() + 12 * (t.year() - e.year()), e.clone().add(n.months, "M").isAfter(t) && --n.months, n.milliseconds = +t - +e.clone().add(n.months, "M"), n } function $t(s, i) { return function (e, t) { var n; return null === t || isNaN(+t) || (T(i, "moment()." + i + "(period, number) is deprecated. Please use moment()." + i + "(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."), n = e, e = t, t = n), qt(this, jt(e = "string" == typeof e ? +e : e, t), s), this } } function qt(e, t, n, s) { var i = t._milliseconds, r = Ut(t._days), a = Ut(t._months); e.isValid() && (s = null == s || s, a && Re(e, be(e, "Month") + a * n), r && xe(e, "Date", be(e, "Date") + r * n), i && e._d.setTime(e._d.valueOf() + i * n), s && c.updateOffset(e, r || a)) } jt.fn = Ht.prototype, jt.invalid = function () { return jt(NaN) }; var Jt = $t(1, "add"), Bt = $t(-1, "subtract"); function Qt(e, t) { var n = 12 * (t.year() - e.year()) + (t.month() - e.month()), s = e.clone().add(n, "months"); return -(n + (t - s < 0 ? (t - s) / (s - e.clone().add(n - 1, "months")) : (t - s) / (e.clone().add(n + 1, "months") - s))) || 0 } function Xt(e) { var t; return void 0 === e ? this._locale._abbr : (null != (t = ht(e)) && (this._locale = t), this) } c.defaultFormat = "YYYY-MM-DDTHH:mm:ssZ", c.defaultFormatUtc = "YYYY-MM-DDTHH:mm:ss[Z]"; var Kt = n("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.", function (e) { return void 0 === e ? this.localeData() : this.locale(e) }); function en() { return this._locale } var tn = 126227808e5; function nn(e, t) { return (e % t + t) % t } function sn(e, t, n) { return e < 100 && 0 <= e ? new Date(e + 400, t, n) - tn : new Date(e, t, n).valueOf() } function rn(e, t, n) { return e < 100 && 0 <= e ? Date.UTC(e + 400, t, n) - tn : Date.UTC(e, t, n) } function an(e, t) { I(0, [e, e.length], 0, t) } function on(e, t, n, s, i) { var r; return null == e ? Ie(this, s, i).year : ((r = Ae(e, s, i)) < t && (t = r), function (e, t, n, s, i) { var r = Ee(e, t, n, s, i), a = Ge(r.year, 0, r.dayOfYear); return this.year(a.getUTCFullYear()), this.month(a.getUTCMonth()), this.date(a.getUTCDate()), this }.call(this, e, t, n, s, i)) } I(0, ["gg", 2], 0, function () { return this.weekYear() % 100 }), I(0, ["GG", 2], 0, function () { return this.isoWeekYear() % 100 }), an("gggg", "weekYear"), an("ggggg", "weekYear"), an("GGGG", "isoWeekYear"), an("GGGGG", "isoWeekYear"), C("weekYear", "gg"), C("isoWeekYear", "GG"), F("weekYear", 1), F("isoWeekYear", 1), ue("G", se), ue("g", se), ue("GG", B, z), ue("gg", B, z), ue("GGGG", ee, q), ue("gggg", ee, q), ue("GGGGG", te, J), ue("ggggg", te, J), fe(["gggg", "ggggg", "GGGG", "GGGGG"], function (e, t, n, s) { t[s.substr(0, 2)] = D(e) }), fe(["gg", "GG"], function (e, t, n, s) { t[s] = c.parseTwoDigitYear(e) }), I("Q", 0, "Qo", "quarter"), C("quarter", "Q"), F("quarter", 7), ue("Q", Z), ce("Q", function (e, t) { t[_e] = 3 * (D(e) - 1) }), I("D", ["DD", 2], "Do", "date"), C("date", "D"), F("date", 9), ue("D", B), ue("DD", B, z), ue("Do", function (e, t) { return e ? t._dayOfMonthOrdinalParse || t._ordinalParse : t._dayOfMonthOrdinalParseLenient }), ce(["D", "DD"], ye), ce("Do", function (e, t) { t[ye] = D(e.match(B)[0]) }); var un = Te("Date", !0); I("DDD", ["DDDD", 3], "DDDo", "dayOfYear"), C("dayOfYear", "DDD"), F("dayOfYear", 4), ue("DDD", K), ue("DDDD", $), ce(["DDD", "DDDD"], function (e, t, n) { n._dayOfYear = D(e) }), I("m", ["mm", 2], 0, "minute"), C("minute", "m"), F("minute", 14), ue("m", B), ue("mm", B, z), ce(["m", "mm"], ve); var ln = Te("Minutes", !1); I("s", ["ss", 2], 0, "second"), C("second", "s"), F("second", 15), ue("s", B), ue("ss", B, z), ce(["s", "ss"], pe); var hn, dn = Te("Seconds", !1); for (I("S", 0, 0, function () { return ~~(this.millisecond() / 100) }), I(0, ["SS", 2], 0, function () { return ~~(this.millisecond() / 10) }), I(0, ["SSS", 3], 0, "millisecond"), I(0, ["SSSS", 4], 0, function () { return 10 * this.millisecond() }), I(0, ["SSSSS", 5], 0, function () { return 100 * this.millisecond() }), I(0, ["SSSSSS", 6], 0, function () { return 1e3 * this.millisecond() }), I(0, ["SSSSSSS", 7], 0, function () { return 1e4 * this.millisecond() }), I(0, ["SSSSSSSS", 8], 0, function () { return 1e5 * this.millisecond() }), I(0, ["SSSSSSSSS", 9], 0, function () { return 1e6 * this.millisecond() }), C("millisecond", "ms"), F("millisecond", 16), ue("S", K, Z), ue("SS", K, z), ue("SSS", K, $), hn = "SSSS"; hn.length <= 9; hn += "S")ue(hn, ne); function cn(e, t) { t[we] = D(1e3 * ("0." + e)) } for (hn = "S"; hn.length <= 9; hn += "S")ce(hn, cn); var fn = Te("Milliseconds", !1); I("z", 0, 0, "zoneAbbr"), I("zz", 0, 0, "zoneName"); var mn = M.prototype; function _n(e) { return e } mn.add = Jt, mn.calendar = function (e, t) { var n = e || bt(), s = Gt(n, this).startOf("day"), i = c.calendarFormat(this, s) || "sameElse", r = t && (b(t[i]) ? t[i].call(this, n) : t[i]); return this.format(r || this.localeData().calendar(i, this, bt(n))) }, mn.clone = function () { return new M(this) }, mn.diff = function (e, t, n) { var s, i, r; if (!this.isValid()) return NaN; if (!(s = Gt(e, this)).isValid()) return NaN; switch (i = 6e4 * (s.utcOffset() - this.utcOffset()), t = H(t)) { case "year": r = Qt(this, s) / 12; break; case "month": r = Qt(this, s); break; case "quarter": r = Qt(this, s) / 3; break; case "second": r = (this - s) / 1e3; break; case "minute": r = (this - s) / 6e4; break; case "hour": r = (this - s) / 36e5; break; case "day": r = (this - s - i) / 864e5; break; case "week": r = (this - s - i) / 6048e5; break; default: r = this - s }return n ? r : S(r) }, mn.endOf = function (e) { var t; if (void 0 === (e = H(e)) || "millisecond" === e || !this.isValid()) return this; var n = this._isUTC ? rn : sn; switch (e) { case "year": t = n(this.year() + 1, 0, 1) - 1; break; case "quarter": t = n(this.year(), this.month() - this.month() % 3 + 3, 1) - 1; break; case "month": t = n(this.year(), this.month() + 1, 1) - 1; break; case "week": t = n(this.year(), this.month(), this.date() - this.weekday() + 7) - 1; break; case "isoWeek": t = n(this.year(), this.month(), this.date() - (this.isoWeekday() - 1) + 7) - 1; break; case "day": case "date": t = n(this.year(), this.month(), this.date() + 1) - 1; break; case "hour": t = this._d.valueOf(), t += 36e5 - nn(t + (this._isUTC ? 0 : 6e4 * this.utcOffset()), 36e5) - 1; break; case "minute": t = this._d.valueOf(), t += 6e4 - nn(t, 6e4) - 1; break; case "second": t = this._d.valueOf(), t += 1e3 - nn(t, 1e3) - 1; break }return this._d.setTime(t), c.updateOffset(this, !0), this }, mn.format = function (e) { e || (e = this.isUtc() ? c.defaultFormatUtc : c.defaultFormat); var t = A(this, e); return this.localeData().postformat(t) }, mn.from = function (e, t) { return this.isValid() && (k(e) && e.isValid() || bt(e).isValid()) ? jt({ to: this, from: e }).locale(this.locale()).humanize(!t) : this.localeData().invalidDate() }, mn.fromNow = function (e) { return this.from(bt(), e) }, mn.to = function (e, t) { return this.isValid() && (k(e) && e.isValid() || bt(e).isValid()) ? jt({ from: this, to: e }).locale(this.locale()).humanize(!t) : this.localeData().invalidDate() }, mn.toNow = function (e) { return this.to(bt(), e) }, mn.get = function (e) { return b(this[e = H(e)]) ? this[e]() : this }, mn.invalidAt = function () { return g(this).overflow }, mn.isAfter = function (e, t) { var n = k(e) ? e : bt(e); return !(!this.isValid() || !n.isValid()) && ("millisecond" === (t = H(t) || "millisecond") ? this.valueOf() > n.valueOf() : n.valueOf() < this.clone().startOf(t).valueOf()) }, mn.isBefore = function (e, t) { var n = k(e) ? e : bt(e); return !(!this.isValid() || !n.isValid()) && ("millisecond" === (t = H(t) || "millisecond") ? this.valueOf() < n.valueOf() : this.clone().endOf(t).valueOf() < n.valueOf()) }, mn.isBetween = function (e, t, n, s) { var i = k(e) ? e : bt(e), r = k(t) ? t : bt(t); return !!(this.isValid() && i.isValid() && r.isValid()) && ("(" === (s = s || "()")[0] ? this.isAfter(i, n) : !this.isBefore(i, n)) && (")" === s[1] ? this.isBefore(r, n) : !this.isAfter(r, n)) }, mn.isSame = function (e, t) { var n, s = k(e) ? e : bt(e); return !(!this.isValid() || !s.isValid()) && ("millisecond" === (t = H(t) || "millisecond") ? this.valueOf() === s.valueOf() : (n = s.valueOf(), this.clone().startOf(t).valueOf() <= n && n <= this.clone().endOf(t).valueOf())) }, mn.isSameOrAfter = function (e, t) { return this.isSame(e, t) || this.isAfter(e, t) }, mn.isSameOrBefore = function (e, t) { return this.isSame(e, t) || this.isBefore(e, t) }, mn.isValid = function () { return v(this) }, mn.lang = Kt, mn.locale = Xt, mn.localeData = en, mn.max = Pt, mn.min = xt, mn.parsingFlags = function () { return _({}, g(this)) }, mn.set = function (e, t) { if ("object" == typeof e) for (var n = function (e) { var t = []; for (var n in e) t.push({ unit: n, priority: U[n] }); return t.sort(function (e, t) { return e.priority - t.priority }), t }(e = R(e)), s = 0; s < n.length; s++)this[n[s].unit](e[n[s].unit]); else if (b(this[e = H(e)])) return this[e](t); return this }, mn.startOf = function (e) { var t; if (void 0 === (e = H(e)) || "millisecond" === e || !this.isValid()) return this; var n = this._isUTC ? rn : sn; switch (e) { case "year": t = n(this.year(), 0, 1); break; case "quarter": t = n(this.year(), this.month() - this.month() % 3, 1); break; case "month": t = n(this.year(), this.month(), 1); break; case "week": t = n(this.year(), this.month(), this.date() - this.weekday()); break; case "isoWeek": t = n(this.year(), this.month(), this.date() - (this.isoWeekday() - 1)); break; case "day": case "date": t = n(this.year(), this.month(), this.date()); break; case "hour": t = this._d.valueOf(), t -= nn(t + (this._isUTC ? 0 : 6e4 * this.utcOffset()), 36e5); break; case "minute": t = this._d.valueOf(), t -= nn(t, 6e4); break; case "second": t = this._d.valueOf(), t -= nn(t, 1e3); break }return this._d.setTime(t), c.updateOffset(this, !0), this }, mn.subtract = Bt, mn.toArray = function () { var e = this; return [e.year(), e.month(), e.date(), e.hour(), e.minute(), e.second(), e.millisecond()] }, mn.toObject = function () { var e = this; return { years: e.year(), months: e.month(), date: e.date(), hours: e.hours(), minutes: e.minutes(), seconds: e.seconds(), milliseconds: e.milliseconds() } }, mn.toDate = function () { return new Date(this.valueOf()) }, mn.toISOString = function (e) { if (!this.isValid()) return null; var t = !0 !== e, n = t ? this.clone().utc() : this; return n.year() < 0 || 9999 < n.year() ? A(n, t ? "YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]" : "YYYYYY-MM-DD[T]HH:mm:ss.SSSZ") : b(Date.prototype.toISOString) ? t ? this.toDate().toISOString() : new Date(this.valueOf() + 60 * this.utcOffset() * 1e3).toISOString().replace("Z", A(n, "Z")) : A(n, t ? "YYYY-MM-DD[T]HH:mm:ss.SSS[Z]" : "YYYY-MM-DD[T]HH:mm:ss.SSSZ") }, mn.inspect = function () { if (!this.isValid()) return "moment.invalid(/* " + this._i + " */)"; var e = "moment", t = ""; this.isLocal() || (e = 0 === this.utcOffset() ? "moment.utc" : "moment.parseZone", t = "Z"); var n = "[" + e + '("]', s = 0 <= this.year() && this.year() <= 9999 ? "YYYY" : "YYYYYY", i = t + '[")]'; return this.format(n + s + "-MM-DD[T]HH:mm:ss.SSS" + i) }, mn.toJSON = function () { return this.isValid() ? this.toISOString() : null }, mn.toString = function () { return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ") }, mn.unix = function () { return Math.floor(this.valueOf() / 1e3) }, mn.valueOf = function () { return this._d.valueOf() - 6e4 * (this._offset || 0) }, mn.creationData = function () { return { input: this._i, format: this._f, locale: this._locale, isUTC: this._isUTC, strict: this._strict } }, mn.year = Oe, mn.isLeapYear = function () { return De(this.year()) }, mn.weekYear = function (e) { return on.call(this, e, this.week(), this.weekday(), this.localeData()._week.dow, this.localeData()._week.doy) }, mn.isoWeekYear = function (e) { return on.call(this, e, this.isoWeek(), this.isoWeekday(), 1, 4) }, mn.quarter = mn.quarters = function (e) { return null == e ? Math.ceil((this.month() + 1) / 3) : this.month(3 * (e - 1) + this.month() % 3) }, mn.month = Ue, mn.daysInMonth = function () { return Pe(this.year(), this.month()) }, mn.week = mn.weeks = function (e) { var t = this.localeData().week(this); return null == e ? t : this.add(7 * (e - t), "d") }, mn.isoWeek = mn.isoWeeks = function (e) { var t = Ie(this, 1, 4).week; return null == e ? t : this.add(7 * (e - t), "d") }, mn.weeksInYear = function () { var e = this.localeData()._week; return Ae(this.year(), e.dow, e.doy) }, mn.isoWeeksInYear = function () { return Ae(this.year(), 1, 4) }, mn.date = un, mn.day = mn.days = function (e) { if (!this.isValid()) return null != e ? this : NaN; var t, n, s = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); return null != e ? (t = e, n = this.localeData(), e = "string" != typeof t ? t : isNaN(t) ? "number" == typeof (t = n.weekdaysParse(t)) ? t : null : parseInt(t, 10), this.add(e - s, "d")) : s }, mn.weekday = function (e) { if (!this.isValid()) return null != e ? this : NaN; var t = (this.day() + 7 - this.localeData()._week.dow) % 7; return null == e ? t : this.add(e - t, "d") }, mn.isoWeekday = function (e) { if (!this.isValid()) return null != e ? this : NaN; if (null == e) return this.day() || 7; var t, n, s = (t = e, n = this.localeData(), "string" == typeof t ? n.weekdaysParse(t) % 7 || 7 : isNaN(t) ? null : t); return this.day(this.day() % 7 ? s : s - 7) }, mn.dayOfYear = function (e) { var t = Math.round((this.clone().startOf("day") - this.clone().startOf("year")) / 864e5) + 1; return null == e ? t : this.add(e - t, "d") }, mn.hour = mn.hours = nt, mn.minute = mn.minutes = ln, mn.second = mn.seconds = dn, mn.millisecond = mn.milliseconds = fn, mn.utcOffset = function (e, t, n) { var s, i = this._offset || 0; if (!this.isValid()) return null != e ? this : NaN; if (null == e) return this._isUTC ? i : Vt(this); if ("string" == typeof e) { if (null === (e = Nt(re, e))) return this } else Math.abs(e) < 16 && !n && (e *= 60); return !this._isUTC && t && (s = Vt(this)), this._offset = e, this._isUTC = !0, null != s && this.add(s, "m"), i !== e && (!t || this._changeInProgress ? qt(this, jt(e - i, "m"), 1, !1) : this._changeInProgress || (this._changeInProgress = !0, c.updateOffset(this, !0), this._changeInProgress = null)), this }, mn.utc = function (e) { return this.utcOffset(0, e) }, mn.local = function (e) { return this._isUTC && (this.utcOffset(0, e), this._isUTC = !1, e && this.subtract(Vt(this), "m")), this }, mn.parseZone = function () { if (null != this._tzm) this.utcOffset(this._tzm, !1, !0); else if ("string" == typeof this._i) { var e = Nt(ie, this._i); null != e ? this.utcOffset(e) : this.utcOffset(0, !0) } return this }, mn.hasAlignedHourOffset = function (e) { return !!this.isValid() && (e = e ? bt(e).utcOffset() : 0, (this.utcOffset() - e) % 60 == 0) }, mn.isDST = function () { return this.utcOffset() > this.clone().month(0).utcOffset() || this.utcOffset() > this.clone().month(5).utcOffset() }, mn.isLocal = function () { return !!this.isValid() && !this._isUTC }, mn.isUtcOffset = function () { return !!this.isValid() && this._isUTC }, mn.isUtc = Et, mn.isUTC = Et, mn.zoneAbbr = function () { return this._isUTC ? "UTC" : "" }, mn.zoneName = function () { return this._isUTC ? "Coordinated Universal Time" : "" }, mn.dates = n("dates accessor is deprecated. Use date instead.", un), mn.months = n("months accessor is deprecated. Use month instead", Ue), mn.years = n("years accessor is deprecated. Use year instead", Oe), mn.zone = n("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/", function (e, t) { return null != e ? ("string" != typeof e && (e = -e), this.utcOffset(e, t), this) : -this.utcOffset() }), mn.isDSTShifted = n("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information", function () { if (!l(this._isDSTShifted)) return this._isDSTShifted; var e = {}; if (w(e, this), (e = Ot(e))._a) { var t = e._isUTC ? y(e._a) : bt(e._a); this._isDSTShifted = this.isValid() && 0 < a(e._a, t.toArray()) } else this._isDSTShifted = !1; return this._isDSTShifted }); var yn = P.prototype; function gn(e, t, n, s) { var i = ht(), r = y().set(s, t); return i[n](r, e) } function vn(e, t, n) { if (h(e) && (t = e, e = void 0), e = e || "", null != t) return gn(e, t, n, "month"); var s, i = []; for (s = 0; s < 12; s++)i[s] = gn(e, s, n, "month"); return i } function pn(e, t, n, s) { t = ("boolean" == typeof e ? h(t) && (n = t, t = void 0) : (t = e, e = !1, h(n = t) && (n = t, t = void 0)), t || ""); var i, r = ht(), a = e ? r._week.dow : 0; if (null != n) return gn(t, (n + a) % 7, s, "day"); var o = []; for (i = 0; i < 7; i++)o[i] = gn(t, (i + a) % 7, s, "day"); return o } yn.calendar = function (e, t, n) { var s = this._calendar[e] || this._calendar.sameElse; return b(s) ? s.call(t, n) : s }, yn.longDateFormat = function (e) { var t = this._longDateFormat[e], n = this._longDateFormat[e.toUpperCase()]; return t || !n ? t : (this._longDateFormat[e] = n.replace(/MMMM|MM|DD|dddd/g, function (e) { return e.slice(1) }), this._longDateFormat[e]) }, yn.invalidDate = function () { return this._invalidDate }, yn.ordinal = function (e) { return this._ordinal.replace("%d", e) }, yn.preparse = _n, yn.postformat = _n, yn.relativeTime = function (e, t, n, s) { var i = this._relativeTime[n]; return b(i) ? i(e, t, n, s) : i.replace(/%d/i, e) }, yn.pastFuture = function (e, t) { var n = this._relativeTime[0 < e ? "future" : "past"]; return b(n) ? n(t) : n.replace(/%s/i, t) }, yn.set = function (e) { var t, n; for (n in e) b(t = e[n]) ? this[n] = t : this["_" + n] = t; this._config = e, this._dayOfMonthOrdinalParseLenient = new RegExp((this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + "|" + /\d{1,2}/.source) }, yn.months = function (e, t) { return e ? o(this._months) ? this._months[e.month()] : this._months[(this._months.isFormat || We).test(t) ? "format" : "standalone"][e.month()] : o(this._months) ? this._months : this._months.standalone }, yn.monthsShort = function (e, t) { return e ? o(this._monthsShort) ? this._monthsShort[e.month()] : this._monthsShort[We.test(t) ? "format" : "standalone"][e.month()] : o(this._monthsShort) ? this._monthsShort : this._monthsShort.standalone }, yn.monthsParse = function (e, t, n) { var s, i, r; if (this._monthsParseExact) return function (e, t, n) { var s, i, r, a = e.toLocaleLowerCase(); if (!this._monthsParse) for (this._monthsParse = [], this._longMonthsParse = [], this._shortMonthsParse = [], s = 0; s < 12; ++s)r = y([2e3, s]), this._shortMonthsParse[s] = this.monthsShort(r, "").toLocaleLowerCase(), this._longMonthsParse[s] = this.months(r, "").toLocaleLowerCase(); return n ? "MMM" === t ? -1 !== (i = Ye.call(this._shortMonthsParse, a)) ? i : null : -1 !== (i = Ye.call(this._longMonthsParse, a)) ? i : null : "MMM" === t ? -1 !== (i = Ye.call(this._shortMonthsParse, a)) ? i : -1 !== (i = Ye.call(this._longMonthsParse, a)) ? i : null : -1 !== (i = Ye.call(this._longMonthsParse, a)) ? i : -1 !== (i = Ye.call(this._shortMonthsParse, a)) ? i : null }.call(this, e, t, n); for (this._monthsParse || (this._monthsParse = [], this._longMonthsParse = [], this._shortMonthsParse = []), s = 0; s < 12; s++) { if (i = y([2e3, s]), n && !this._longMonthsParse[s] && (this._longMonthsParse[s] = new RegExp("^" + this.months(i, "").replace(".", "") + "$", "i"), this._shortMonthsParse[s] = new RegExp("^" + this.monthsShort(i, "").replace(".", "") + "$", "i")), n || this._monthsParse[s] || (r = "^" + this.months(i, "") + "|^" + this.monthsShort(i, ""), this._monthsParse[s] = new RegExp(r.replace(".", ""), "i")), n && "MMMM" === t && this._longMonthsParse[s].test(e)) return s; if (n && "MMM" === t && this._shortMonthsParse[s].test(e)) return s; if (!n && this._monthsParse[s].test(e)) return s } }, yn.monthsRegex = function (e) { return this._monthsParseExact ? (m(this, "_monthsRegex") || Ne.call(this), e ? this._monthsStrictRegex : this._monthsRegex) : (m(this, "_monthsRegex") || (this._monthsRegex = Le), this._monthsStrictRegex && e ? this._monthsStrictRegex : this._monthsRegex) }, yn.monthsShortRegex = function (e) { return this._monthsParseExact ? (m(this, "_monthsRegex") || Ne.call(this), e ? this._monthsShortStrictRegex : this._monthsShortRegex) : (m(this, "_monthsShortRegex") || (this._monthsShortRegex = Fe), this._monthsShortStrictRegex && e ? this._monthsShortStrictRegex : this._monthsShortRegex) }, yn.week = function (e) { return Ie(e, this._week.dow, this._week.doy).week }, yn.firstDayOfYear = function () { return this._week.doy }, yn.firstDayOfWeek = function () { return this._week.dow }, yn.weekdays = function (e, t) { var n = o(this._weekdays) ? this._weekdays : this._weekdays[e && !0 !== e && this._weekdays.isFormat.test(t) ? "format" : "standalone"]; return !0 === e ? je(n, this._week.dow) : e ? n[e.day()] : n }, yn.weekdaysMin = function (e) { return !0 === e ? je(this._weekdaysMin, this._week.dow) : e ? this._weekdaysMin[e.day()] : this._weekdaysMin }, yn.weekdaysShort = function (e) { return !0 === e ? je(this._weekdaysShort, this._week.dow) : e ? this._weekdaysShort[e.day()] : this._weekdaysShort }, yn.weekdaysParse = function (e, t, n) { var s, i, r; if (this._weekdaysParseExact) return function (e, t, n) { var s, i, r, a = e.toLocaleLowerCase(); if (!this._weekdaysParse) for (this._weekdaysParse = [], this._shortWeekdaysParse = [], this._minWeekdaysParse = [], s = 0; s < 7; ++s)r = y([2e3, 1]).day(s), this._minWeekdaysParse[s] = this.weekdaysMin(r, "").toLocaleLowerCase(), this._shortWeekdaysParse[s] = this.weekdaysShort(r, "").toLocaleLowerCase(), this._weekdaysParse[s] = this.weekdays(r, "").toLocaleLowerCase(); return n ? "dddd" === t ? -1 !== (i = Ye.call(this._weekdaysParse, a)) ? i : null : "ddd" === t ? -1 !== (i = Ye.call(this._shortWeekdaysParse, a)) ? i : null : -1 !== (i = Ye.call(this._minWeekdaysParse, a)) ? i : null : "dddd" === t ? -1 !== (i = Ye.call(this._weekdaysParse, a)) ? i : -1 !== (i = Ye.call(this._shortWeekdaysParse, a)) ? i : -1 !== (i = Ye.call(this._minWeekdaysParse, a)) ? i : null : "ddd" === t ? -1 !== (i = Ye.call(this._shortWeekdaysParse, a)) ? i : -1 !== (i = Ye.call(this._weekdaysParse, a)) ? i : -1 !== (i = Ye.call(this._minWeekdaysParse, a)) ? i : null : -1 !== (i = Ye.call(this._minWeekdaysParse, a)) ? i : -1 !== (i = Ye.call(this._weekdaysParse, a)) ? i : -1 !== (i = Ye.call(this._shortWeekdaysParse, a)) ? i : null }.call(this, e, t, n); for (this._weekdaysParse || (this._weekdaysParse = [], this._minWeekdaysParse = [], this._shortWeekdaysParse = [], this._fullWeekdaysParse = []), s = 0; s < 7; s++) { if (i = y([2e3, 1]).day(s), n && !this._fullWeekdaysParse[s] && (this._fullWeekdaysParse[s] = new RegExp("^" + this.weekdays(i, "").replace(".", "\\.?") + "$", "i"), this._shortWeekdaysParse[s] = new RegExp("^" + this.weekdaysShort(i, "").replace(".", "\\.?") + "$", "i"), this._minWeekdaysParse[s] = new RegExp("^" + this.weekdaysMin(i, "").replace(".", "\\.?") + "$", "i")), this._weekdaysParse[s] || (r = "^" + this.weekdays(i, "") + "|^" + this.weekdaysShort(i, "") + "|^" + this.weekdaysMin(i, ""), this._weekdaysParse[s] = new RegExp(r.replace(".", ""), "i")), n && "dddd" === t && this._fullWeekdaysParse[s].test(e)) return s; if (n && "ddd" === t && this._shortWeekdaysParse[s].test(e)) return s; if (n && "dd" === t && this._minWeekdaysParse[s].test(e)) return s; if (!n && this._weekdaysParse[s].test(e)) return s } }, yn.weekdaysRegex = function (e) { return this._weekdaysParseExact ? (m(this, "_weekdaysRegex") || Qe.call(this), e ? this._weekdaysStrictRegex : this._weekdaysRegex) : (m(this, "_weekdaysRegex") || (this._weekdaysRegex = qe), this._weekdaysStrictRegex && e ? this._weekdaysStrictRegex : this._weekdaysRegex) }, yn.weekdaysShortRegex = function (e) { return this._weekdaysParseExact ? (m(this, "_weekdaysRegex") || Qe.call(this), e ? this._weekdaysShortStrictRegex : this._weekdaysShortRegex) : (m(this, "_weekdaysShortRegex") || (this._weekdaysShortRegex = Je), this._weekdaysShortStrictRegex && e ? this._weekdaysShortStrictRegex : this._weekdaysShortRegex) }, yn.weekdaysMinRegex = function (e) { return this._weekdaysParseExact ? (m(this, "_weekdaysRegex") || Qe.call(this), e ? this._weekdaysMinStrictRegex : this._weekdaysMinRegex) : (m(this, "_weekdaysMinRegex") || (this._weekdaysMinRegex = Be), this._weekdaysMinStrictRegex && e ? this._weekdaysMinStrictRegex : this._weekdaysMinRegex) }, yn.isPM = function (e) { return "p" === (e + "").toLowerCase().charAt(0) }, yn.meridiem = function (e, t, n) { return 11 < e ? n ? "pm" : "PM" : n ? "am" : "AM" }, ut("en", { dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, ordinal: function (e) { var t = e % 10; return e + (1 === D(e % 100 / 10) ? "th" : 1 === t ? "st" : 2 === t ? "nd" : 3 === t ? "rd" : "th") } }), c.lang = n("moment.lang is deprecated. Use moment.locale instead.", ut), c.langData = n("moment.langData is deprecated. Use moment.localeData instead.", ht); var wn = Math.abs; function Mn(e, t, n, s) { var i = jt(t, n); return e._milliseconds += s * i._milliseconds, e._days += s * i._days, e._months += s * i._months, e._bubble() } function kn(e) { return e < 0 ? Math.floor(e) : Math.ceil(e) } function Sn(e) { return 4800 * e / 146097 } function Dn(e) { return 146097 * e / 4800 } function Yn(e) { return function () { return this.as(e) } } var On = Yn("ms"), Tn = Yn("s"), bn = Yn("m"), xn = Yn("h"), Pn = Yn("d"), Wn = Yn("w"), Cn = Yn("M"), Hn = Yn("Q"), Rn = Yn("y"); function Un(e) { return function () { return this.isValid() ? this._data[e] : NaN } } var Fn = Un("milliseconds"), Ln = Un("seconds"), Nn = Un("minutes"), Gn = Un("hours"), Vn = Un("days"), En = Un("months"), In = Un("years"); var An = Math.round, jn = { ss: 44, s: 45, m: 45, h: 22, d: 26, M: 11 }; var Zn = Math.abs; function zn(e) { return (0 < e) - (e < 0) || +e } function $n() { if (!this.isValid()) return this.localeData().invalidDate(); var e, t, n = Zn(this._milliseconds) / 1e3, s = Zn(this._days), i = Zn(this._months); t = S((e = S(n / 60)) / 60), n %= 60, e %= 60; var r = S(i / 12), a = i %= 12, o = s, u = t, l = e, h = n ? n.toFixed(3).replace(/\.?0+$/, "") : "", d = this.asSeconds(); if (!d) return "P0D"; var c = d < 0 ? "-" : "", f = zn(this._months) !== zn(d) ? "-" : "", m = zn(this._days) !== zn(d) ? "-" : "", _ = zn(this._milliseconds) !== zn(d) ? "-" : ""; return c + "P" + (r ? f + r + "Y" : "") + (a ? f + a + "M" : "") + (o ? m + o + "D" : "") + (u || l || h ? "T" : "") + (u ? _ + u + "H" : "") + (l ? _ + l + "M" : "") + (h ? _ + h + "S" : "") } var qn = Ht.prototype; return qn.isValid = function () { return this._isValid }, qn.abs = function () { var e = this._data; return this._milliseconds = wn(this._milliseconds), this._days = wn(this._days), this._months = wn(this._months), e.milliseconds = wn(e.milliseconds), e.seconds = wn(e.seconds), e.minutes = wn(e.minutes), e.hours = wn(e.hours), e.months = wn(e.months), e.years = wn(e.years), this }, qn.add = function (e, t) { return Mn(this, e, t, 1) }, qn.subtract = function (e, t) { return Mn(this, e, t, -1) }, qn.as = function (e) { if (!this.isValid()) return NaN; var t, n, s = this._milliseconds; if ("month" === (e = H(e)) || "quarter" === e || "year" === e) switch (t = this._days + s / 864e5, n = this._months + Sn(t), e) { case "month": return n; case "quarter": return n / 3; case "year": return n / 12 } else switch (t = this._days + Math.round(Dn(this._months)), e) { case "week": return t / 7 + s / 6048e5; case "day": return t + s / 864e5; case "hour": return 24 * t + s / 36e5; case "minute": return 1440 * t + s / 6e4; case "second": return 86400 * t + s / 1e3; case "millisecond": return Math.floor(864e5 * t) + s; default: throw new Error("Unknown unit " + e) } }, qn.asMilliseconds = On, qn.asSeconds = Tn, qn.asMinutes = bn, qn.asHours = xn, qn.asDays = Pn, qn.asWeeks = Wn, qn.asMonths = Cn, qn.asQuarters = Hn, qn.asYears = Rn, qn.valueOf = function () { return this.isValid() ? this._milliseconds + 864e5 * this._days + this._months % 12 * 2592e6 + 31536e6 * D(this._months / 12) : NaN }, qn._bubble = function () { var e, t, n, s, i, r = this._milliseconds, a = this._days, o = this._months, u = this._data; return 0 <= r && 0 <= a && 0 <= o || r <= 0 && a <= 0 && o <= 0 || (r += 864e5 * kn(Dn(o) + a), o = a = 0), u.milliseconds = r % 1e3, e = S(r / 1e3), u.seconds = e % 60, t = S(e / 60), u.minutes = t % 60, n = S(t / 60), u.hours = n % 24, o += i = S(Sn(a += S(n / 24))), a -= kn(Dn(i)), s = S(o / 12), o %= 12, u.days = a, u.months = o, u.years = s, this }, qn.clone = function () { return jt(this) }, qn.get = function (e) { return e = H(e), this.isValid() ? this[e + "s"]() : NaN }, qn.milliseconds = Fn, qn.seconds = Ln, qn.minutes = Nn, qn.hours = Gn, qn.days = Vn, qn.weeks = function () { return S(this.days() / 7) }, qn.months = En, qn.years = In, qn.humanize = function (e) { if (!this.isValid()) return this.localeData().invalidDate(); var t, n, s, i, r, a, o, u, l, h, d, c = this.localeData(), f = (n = !e, s = c, i = jt(t = this).abs(), r = An(i.as("s")), a = An(i.as("m")), o = An(i.as("h")), u = An(i.as("d")), l = An(i.as("M")), h = An(i.as("y")), (d = r <= jn.ss && ["s", r] || r < jn.s && ["ss", r] || a <= 1 && ["m"] || a < jn.m && ["mm", a] || o <= 1 && ["h"] || o < jn.h && ["hh", o] || u <= 1 && ["d"] || u < jn.d && ["dd", u] || l <= 1 && ["M"] || l < jn.M && ["MM", l] || h <= 1 && ["y"] || ["yy", h])[2] = n, d[3] = 0 < +t, d[4] = s, function (e, t, n, s, i) { return i.relativeTime(t || 1, !!n, e, s) }.apply(null, d)); return e && (f = c.pastFuture(+this, f)), c.postformat(f) }, qn.toISOString = $n, qn.toString = $n, qn.toJSON = $n, qn.locale = Xt, qn.localeData = en, qn.toIsoString = n("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)", $n), qn.lang = Kt, I("X", 0, 0, "unix"), I("x", 0, 0, "valueOf"), ue("x", se), ue("X", /[+-]?\d+(\.\d{1,3})?/), ce("X", function (e, t, n) { n._d = new Date(1e3 * parseFloat(e, 10)) }), ce("x", function (e, t, n) { n._d = new Date(D(e)) }), c.version = "2.24.0", e = bt, c.fn = mn, c.min = function () { return Wt("isBefore", [].slice.call(arguments, 0)) }, c.max = function () { return Wt("isAfter", [].slice.call(arguments, 0)) }, c.now = function () { return Date.now ? Date.now() : +new Date }, c.utc = y, c.unix = function (e) { return bt(1e3 * e) }, c.months = function (e, t) { return vn(e, t, "months") }, c.isDate = d, c.locale = ut, c.invalid = p, c.duration = jt, c.isMoment = k, c.weekdays = function (e, t, n) { return pn(e, t, n, "weekdays") }, c.parseZone = function () { return bt.apply(null, arguments).parseZone() }, c.localeData = ht, c.isDuration = Rt, c.monthsShort = function (e, t) { return vn(e, t, "monthsShort") }, c.weekdaysMin = function (e, t, n) { return pn(e, t, n, "weekdaysMin") }, c.defineLocale = lt, c.updateLocale = function (e, t) { if (null != t) { var n, s, i = st; null != (s = ot(e)) && (i = s._config), (n = new P(t = x(i, t))).parentLocale = it[e], it[e] = n, ut(e) } else null != it[e] && (null != it[e].parentLocale ? it[e] = it[e].parentLocale : null != it[e] && delete it[e]); return it[e] }, c.locales = function () { return s(it) }, c.weekdaysShort = function (e, t, n) { return pn(e, t, n, "weekdaysShort") }, c.normalizeUnits = H, c.relativeTimeRounding = function (e) { return void 0 === e ? An : "function" == typeof e && (An = e, !0) }, c.relativeTimeThreshold = function (e, t) { return void 0 !== jn[e] && (void 0 === t ? jn[e] : (jn[e] = t, "s" === e && (jn.ss = t - 1), !0)) }, c.calendarFormat = function (e, t) { var n = e.diff(t, "days", !0); return n < -6 ? "sameElse" : n < -1 ? "lastWeek" : n < 0 ? "lastDay" : n < 1 ? "sameDay" : n < 2 ? "nextDay" : n < 7 ? "nextWeek" : "sameElse" }, c.prototype = mn, c.HTML5_FMT = { DATETIME_LOCAL: "YYYY-MM-DDTHH:mm", DATETIME_LOCAL_SECONDS: "YYYY-MM-DDTHH:mm:ss", DATETIME_LOCAL_MS: "YYYY-MM-DDTHH:mm:ss.SSS", DATE: "YYYY-MM-DD", TIME: "HH:mm", TIME_SECONDS: "HH:mm:ss", TIME_MS: "HH:mm:ss.SSS", WEEK: "GGGG-[W]WW", MONTH: "YYYY-MM" }, c }); |
src/utils/request.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2022-09-19 14:11:06 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-07-01 11:17:49 | ||
| 5 | + * @FilePath: /myApp/src/utils/request.js | ||
| 6 | + * @Description: 简单axios封装,后续按实际处理 | ||
| 7 | + */ | ||
| 8 | +// import axios from 'axios' | ||
| 9 | +import axios from 'axios-miniprogram'; | ||
| 10 | +import Taro from '@tarojs/taro' | ||
| 11 | +// import { strExist } from './tools' | ||
| 12 | +// import qs from 'Qs' | ||
| 13 | +import { routerStore } from '@/stores/router' | ||
| 14 | + | ||
| 15 | +// import { ProgressStart, ProgressEnd } from '@/components/axios-progress/progress'; | ||
| 16 | +// import store from '@/store' | ||
| 17 | +// import { getToken } from '@/utils/auth' | ||
| 18 | +import BASE_URL from './config'; | ||
| 19 | + | ||
| 20 | +/** | ||
| 21 | + * 获取sessionid的工具函数 | ||
| 22 | + * @returns {string|null} sessionid或null | ||
| 23 | + */ | ||
| 24 | +const getSessionId = () => { | ||
| 25 | + try { | ||
| 26 | + return wx.getStorageSync("sessionid") || null; | ||
| 27 | + } catch (error) { | ||
| 28 | + console.error('获取sessionid失败:', error); | ||
| 29 | + return null; | ||
| 30 | + } | ||
| 31 | +}; | ||
| 32 | + | ||
| 33 | +/** | ||
| 34 | + * 设置sessionid的工具函数 | ||
| 35 | + * @param {string} sessionid - 要设置的sessionid | ||
| 36 | + */ | ||
| 37 | +const setSessionId = (sessionid) => { | ||
| 38 | + try { | ||
| 39 | + wx.setStorageSync("sessionid", sessionid); | ||
| 40 | + } catch (error) { | ||
| 41 | + console.error('设置sessionid失败:', error); | ||
| 42 | + } | ||
| 43 | +}; | ||
| 44 | + | ||
| 45 | +/** | ||
| 46 | + * 清除sessionid的工具函数 | ||
| 47 | + */ | ||
| 48 | +const clearSessionId = () => { | ||
| 49 | + try { | ||
| 50 | + wx.removeStorageSync("sessionid"); | ||
| 51 | + } catch (error) { | ||
| 52 | + console.error('清除sessionid失败:', error); | ||
| 53 | + } | ||
| 54 | +}; | ||
| 55 | +// create an axios instance | ||
| 56 | +const service = axios.create({ | ||
| 57 | + baseURL: BASE_URL, // url = base url + request url | ||
| 58 | + // withCredentials: true, // send cookies when cross-domain requests | ||
| 59 | + timeout: 5000, // request timeout | ||
| 60 | +}) | ||
| 61 | + | ||
| 62 | +service.defaults.params = { | ||
| 63 | + f: 'room', | ||
| 64 | + client_id: '772428', | ||
| 65 | +}; | ||
| 66 | + | ||
| 67 | +// request interceptor | ||
| 68 | +service.interceptors.request.use( | ||
| 69 | + config => { | ||
| 70 | + // console.warn(config) | ||
| 71 | + // console.warn(store) | ||
| 72 | + | ||
| 73 | + /** | ||
| 74 | + * 动态获取sessionid并设置到请求头 | ||
| 75 | + * 确保每个请求都带上最新的sessionid | ||
| 76 | + */ | ||
| 77 | + const sessionid = getSessionId(); | ||
| 78 | + if (sessionid) { | ||
| 79 | + config.headers.cookie = sessionid; | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + /** | ||
| 83 | + * POST PHP需要修改数据格式 | ||
| 84 | + * 序列化POST请求时需要屏蔽上传相关接口,上传相关接口序列化后报错 | ||
| 85 | + */ | ||
| 86 | + // config.data = config.method === 'post' && !strExist(['a=upload', 'upload.qiniup.com'], config.url) ? qs.stringify(config.data) : config.data; | ||
| 87 | + return config | ||
| 88 | + }, | ||
| 89 | + error => { | ||
| 90 | + // do something with request error | ||
| 91 | + console.error(error, 'err') // for debug | ||
| 92 | + return Promise.reject(error) | ||
| 93 | + } | ||
| 94 | +) | ||
| 95 | + | ||
| 96 | +// response interceptor | ||
| 97 | +service.interceptors.response.use( | ||
| 98 | + /** | ||
| 99 | + * If you want to get http information such as headers or status | ||
| 100 | + * Please return response => response | ||
| 101 | + */ | ||
| 102 | + | ||
| 103 | + /** | ||
| 104 | + * Determine the request status by custom code | ||
| 105 | + * Here is just an example | ||
| 106 | + * You can also judge the status by HTTP Status Code | ||
| 107 | + */ | ||
| 108 | + response => { | ||
| 109 | + /** | ||
| 110 | + * 检查响应头中是否有新的sessionid | ||
| 111 | + * 如果有,则更新本地存储 | ||
| 112 | + */ | ||
| 113 | + const setCookieHeader = response.headers['set-cookie']; | ||
| 114 | + if (setCookieHeader) { | ||
| 115 | + // 解析set-cookie头,提取sessionid | ||
| 116 | + const sessionidMatch = setCookieHeader.match(/sessionid=([^;]+)/); | ||
| 117 | + if (sessionidMatch && sessionidMatch[1]) { | ||
| 118 | + setSessionId(sessionidMatch[1]); | ||
| 119 | + } | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + // wx.hideLoading(); | ||
| 123 | + // const res = response.data | ||
| 124 | + // // Toast.clear(); | ||
| 125 | + // // if the custom code is not 20000, it is judged as an error. | ||
| 126 | + // if (res.code !== 100000) { | ||
| 127 | + // // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired; | ||
| 128 | + // if (res.code === 50008 || res.code === 50012 || res.code === 50014) { | ||
| 129 | + // // to re-login | ||
| 130 | + // // Toast.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', { | ||
| 131 | + // // confirmButtonText: 'Re-Login', | ||
| 132 | + // // cancelButtonText: 'Cancel', | ||
| 133 | + // // type: 'warning' | ||
| 134 | + // // }).then(() => { | ||
| 135 | + // // // store.dispatch('user/resetToken').then(() => { | ||
| 136 | + // // // location.reload() | ||
| 137 | + // // // }) | ||
| 138 | + // // }) | ||
| 139 | + // } else { | ||
| 140 | + // // Toast.fail({ | ||
| 141 | + // // message: res.message, | ||
| 142 | + // // duration: 1.5 * 1000 | ||
| 143 | + // // }) | ||
| 144 | + // // Tips.error(res.message, false) | ||
| 145 | + // } | ||
| 146 | + // return Promise.reject(new Error(res.message || 'Error')) | ||
| 147 | + // } else { | ||
| 148 | + // return res | ||
| 149 | + // } | ||
| 150 | + | ||
| 151 | + /** | ||
| 152 | + * 处理401未授权状态 | ||
| 153 | + * 清除本地sessionid并跳转到登录页 | ||
| 154 | + */ | ||
| 155 | + if (response.data.code === 401) { | ||
| 156 | + // 清除无效的sessionid | ||
| 157 | + clearSessionId(); | ||
| 158 | + /** | ||
| 159 | + * 未授权跳转登录页 | ||
| 160 | + * 授权完成后 返回当前页面 | ||
| 161 | + */ | ||
| 162 | + setTimeout(() => { | ||
| 163 | + Taro.navigateTo({ | ||
| 164 | + url: '../../pages/auth/index?url=' + routerStore().url | ||
| 165 | + }); | ||
| 166 | + }, 1000); | ||
| 167 | + } | ||
| 168 | + return response | ||
| 169 | + }, | ||
| 170 | + error => { | ||
| 171 | + // Toast.clear(); | ||
| 172 | + console.error('err' + error) // for debug | ||
| 173 | + // Toast.fail({ | ||
| 174 | + // message: error.message, | ||
| 175 | + // duration: 1.5 * 1000 | ||
| 176 | + // }) | ||
| 177 | + return Promise.reject(error) | ||
| 178 | + } | ||
| 179 | +) | ||
| 180 | + | ||
| 181 | +// 导出sessionid管理工具函数 | ||
| 182 | +export { getSessionId, setSessionId, clearSessionId }; | ||
| 183 | + | ||
| 184 | +export default service |
src/utils/sysData.js
0 → 100644
src/utils/tools.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2022-04-18 15:59:42 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2022-12-07 22:09:30 | ||
| 5 | + * @FilePath: /swx/src/utils/tools.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +import Taro from '@tarojs/taro' | ||
| 9 | +import moment from '@/utils/moment.min.js' | ||
| 10 | + | ||
| 11 | +// 格式化时间 | ||
| 12 | +const formatDate = (date) => { | ||
| 13 | + return moment(date).format('YYYY-MM-DD HH:mm') | ||
| 14 | +}; | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * @description 判断浏览器属于平台 | ||
| 18 | + * @returns | ||
| 19 | + */ | ||
| 20 | +const wxInfo = () => { | ||
| 21 | + let u = navigator.userAgent; | ||
| 22 | + let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; //android终端或者uc浏览器 | ||
| 23 | + let isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端 | ||
| 24 | + let uAgent = navigator.userAgent.toLowerCase(); | ||
| 25 | + let isTable = (uAgent.match(/MicroMessenger/i) == 'micromessenger') ? true : false; | ||
| 26 | + let isMobile = window.navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i); // 是否手机端 | ||
| 27 | + let isWx = /micromessenger/i.test(navigator.userAgent); // 是否微信 | ||
| 28 | + let isWxPc = isWx && !isMobile; // PC端微信 | ||
| 29 | + return { | ||
| 30 | + isAndroid, | ||
| 31 | + isiOS, | ||
| 32 | + isTable, | ||
| 33 | + isWxPc | ||
| 34 | + }; | ||
| 35 | +}; | ||
| 36 | + | ||
| 37 | +/** | ||
| 38 | + * @description 判断多行省略文本 | ||
| 39 | + * @param {*} id 目标dom标签 | ||
| 40 | + * @returns | ||
| 41 | + */ | ||
| 42 | +const hasEllipsis = (id) => { | ||
| 43 | + let oDiv = document.getElementById(id); | ||
| 44 | + let flag = false; | ||
| 45 | + if (oDiv.scrollHeight > oDiv.clientHeight) { | ||
| 46 | + flag = true | ||
| 47 | + } | ||
| 48 | + return flag | ||
| 49 | +} | ||
| 50 | + | ||
| 51 | +/** | ||
| 52 | + * @description 解析URL参数 | ||
| 53 | + * @param {*} url | ||
| 54 | + * @returns | ||
| 55 | + */ | ||
| 56 | +const parseQueryString = url => { | ||
| 57 | + var json = {}; | ||
| 58 | + var arr = url.indexOf('?') >= 0 ? url.substr(url.indexOf('?') + 1).split('&') : []; | ||
| 59 | + arr.forEach(item => { | ||
| 60 | + var tmp = item.split('='); | ||
| 61 | + json[tmp[0]] = tmp[1]; | ||
| 62 | + }); | ||
| 63 | + return json; | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +/** | ||
| 67 | + * 字符串包含字符数组中字符的状态 | ||
| 68 | + * @param {*} array 字符数组 | ||
| 69 | + * @param {*} str 字符串 | ||
| 70 | + * @returns 包含状态 | ||
| 71 | + */ | ||
| 72 | +const strExist = (array, str) => { | ||
| 73 | + const exist = array.filter(arr => { | ||
| 74 | + if (str.indexOf(arr) >= 0) return str; | ||
| 75 | + }) | ||
| 76 | + return exist.length > 0 | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | +const randomId = (n) => { | ||
| 80 | + const charts = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; | ||
| 81 | + var res = ''; | ||
| 82 | + for (var i = 0; i < n; i++) { | ||
| 83 | + undefined | ||
| 84 | + var id = Math.ceil(Math.random() * 35); | ||
| 85 | + res += charts[id]; | ||
| 86 | + } | ||
| 87 | + return res; | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +/** | ||
| 91 | + * 获取页面query参数 | ||
| 92 | + */ | ||
| 93 | +const pageQuery = () => { | ||
| 94 | + const instance = Taro.getCurrentInstance(); | ||
| 95 | + let $query = ''; | ||
| 96 | + $query = JSON.stringify(instance.router.params); | ||
| 97 | + return JSON.parse($query) | ||
| 98 | +} | ||
| 99 | + | ||
| 100 | +export { formatDate, wxInfo, hasEllipsis, parseQueryString, strExist, randomId, pageQuery }; |
src/utils/weapp.js
0 → 100644
| 1 | +/*获取当前页url*/ | ||
| 2 | +const getCurrentPageUrl = () => { | ||
| 3 | + let pages = getCurrentPages() //获取加载的页面 | ||
| 4 | + let currentPage = pages[pages.length - 1] //获取当前页面的对象 | ||
| 5 | + let url = currentPage.route //当前页面url | ||
| 6 | + return url | ||
| 7 | +} | ||
| 8 | +/*获取当前页参数*/ | ||
| 9 | +const getCurrentPageParam = () => { | ||
| 10 | + let pages = getCurrentPages() //获取加载的页面 | ||
| 11 | + let currentPage = pages[pages.length - 1] //获取当前页面的对象 | ||
| 12 | + let options = currentPage.options //如果要获取url中所带的参数可以查看options | ||
| 13 | + return options | ||
| 14 | +} | ||
| 15 | + | ||
| 16 | +export { | ||
| 17 | + getCurrentPageUrl, | ||
| 18 | + getCurrentPageParam | ||
| 19 | +} |
tailwind.config.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2025-06-30 13:27:50 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-06-30 13:27:56 | ||
| 5 | + * @FilePath: /myApp/tailwind.config.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +/** @type {import('tailwindcss').Config} */ | ||
| 9 | +module.exports = { | ||
| 10 | + // 这里给出了一份 taro 通用示例,具体要根据你自己项目的目录结构进行配置 | ||
| 11 | + // 比如你使用 vue3 项目,你就需要把 vue 这个格式也包括进来 | ||
| 12 | + // 不在 content glob 表达式中包括的文件,在里面编写 tailwindcss class,是不会生成对应的 css 工具类的 | ||
| 13 | + content: ['./public/index.html', './src/**/*.{html,js,ts,jsx,tsx,vue}'], | ||
| 14 | + theme: { | ||
| 15 | + extend: {}, | ||
| 16 | + }, | ||
| 17 | + plugins: [], | ||
| 18 | + corePlugins: { | ||
| 19 | + // 小程序不需要 preflight,因为这主要是给 h5 的,如果你要同时开发多端,你应该使用 process.env.TARO_ENV 环境变量来控制它 | ||
| 20 | + preflight: false, | ||
| 21 | + }, | ||
| 22 | +} |
yarn.lock
0 → 100644
This diff could not be displayed because it is too large.
-
Please register or login to post a comment