diff --git a/Cunkebao/.gitignore b/Cunkebao/.gitignore new file mode 100644 index 00000000..b512c09d --- /dev/null +++ b/Cunkebao/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/Cunkebao/App.vue b/Cunkebao/App.vue new file mode 100644 index 00000000..b0333295 --- /dev/null +++ b/Cunkebao/App.vue @@ -0,0 +1,69 @@ + + + \ No newline at end of file diff --git a/Cunkebao/README.md b/Cunkebao/README.md new file mode 100644 index 00000000..1d881a83 --- /dev/null +++ b/Cunkebao/README.md @@ -0,0 +1,86 @@ +# 村客宝 UniApp + +基于uni-app框架开发的村客宝移动端应用,支持H5、微信小程序、App等多端部署。 + +## 项目结构 + +``` +├── api # API接口目录 +├── components # 自定义组件 +├── pages # 页面文件目录 +│ ├── index # 首页 +│ ├── login # 登录页面 +│ └── agreement # 协议页面 +├── static # 静态资源 +│ ├── images # 图片 +│ └── icons # 图标 +├── store # Vuex状态管理 +├── utils # 工具函数 +│ ├── auth.js # 认证相关工具函数 +│ ├── common.js # 通用工具函数 +│ └── request.js # 请求工具函数 +├── App.vue # 应用配置,用来配置App全局样式以及监听应用生命周期 +├── main.js # Vue初始化入口文件 +├── manifest.json # 配置应用名称、appid、logo、版本等打包信息 +├── pages.json # 配置页面路由、导航条、选项卡等页面类信息 +└── uni.scss # 全局样式变量 +``` + +## 功能特性 + +- **支持多种登录方式**:手机号验证码登录、密码登录、微信授权登录、Apple登录 +- **完整的token认证机制**:JWT令牌管理,自动刷新token,过期处理 +- **统一的网络请求封装**:请求拦截器,响应拦截器,错误处理 +- **多端适配**:一套代码,同时支持H5、微信小程序、App +- **UI框架集成**:基于uView 2.x UI框架,提供丰富的组件和样式 +- **主题定制**:全局样式变量,支持自定义主题 + +## 开发环境 + +- **Node.js**: v14.x及以上 +- **HBuilderX**: 3.x及以上版本 + +### 安装依赖 + +1. 使用HBuilderX打开项目 +2. 点击菜单栏 "工具 -> 插件安装",安装"scss/sass编译"插件 +3. 点击菜单栏 "工具 -> 插件安装",安装"uView-UI"插件 + +## 运行和发布 + +### 运行到浏览器 + +1. 在HBuilderX中,点击"运行 -> 运行到浏览器" +2. 选择浏览器,如Chrome + +### 运行到微信小程序 + +1. 在HBuilderX中,点击"运行 -> 运行到小程序模拟器 -> 微信开发者工具" +2. 确保已安装并配置了微信开发者工具 + +### 发布为H5 + +1. 在HBuilderX中,点击"发行 -> 网站H5发布" +2. 配置发布信息,点击发布 + +### 发布为微信小程序 + +1. 在HBuilderX中,点击"发行 -> 小程序发布 -> 微信小程序" +2. 配置小程序AppID等信息,点击发布 + +### 发布为App + +1. 在HBuilderX中,点击"发行 -> 原生App-云打包" +2. 配置证书等信息,选择打包平台(Android/iOS),点击发布 + +## 技术栈 + +- **uni-app**:跨平台前端框架 +- **Vue.js**:前端框架 +- **Vuex**:状态管理 +- **uView UI**:UI组件库 +- **SCSS**:CSS预处理器 + +## License + +MIT \ No newline at end of file diff --git a/Cunkebao/api/user.js b/Cunkebao/api/user.js new file mode 100644 index 00000000..ae7c147b --- /dev/null +++ b/Cunkebao/api/user.js @@ -0,0 +1,107 @@ +import request from '@/utils/request' + +/** + * 用户登录 + * @param {Object} data 登录数据 + * @param {string} data.username 用户名 + * @param {string} data.password 密码 + * @param {boolean} data.is_encrypted 密码是否已加密 + * @returns {Promise} 登录结果 + */ +export function login(data) { + return request({ + url: '/api/auth/login', + method: 'POST', + data + }) +} + +/** + * 手机号验证码登录 + * @param {Object} data 登录数据 + * @param {string} data.mobile 手机号 + * @param {string} data.code 验证码 + * @returns {Promise} 登录结果 + */ +export function mobileLogin(data) { + return request({ + url: '/api/auth/mobile-login', + method: 'POST', + data + }) +} + +/** + * 发送验证码 + * @param {Object} data 数据 + * @param {string} data.mobile 手机号 + * @param {string} data.type 验证码类型(login:登录,register:注册) + * @returns {Promise} 发送结果 + */ +export function sendCode(data) { + return request({ + url: '/api/auth/code', + method: 'POST', + data + }) +} + +/** + * 获取用户信息 + * @returns {Promise} 用户信息 + */ +export function getUserInfo() { + return request({ + url: '/api/auth/info', + method: 'GET' + }) +} + +/** + * 刷新token + * @returns {Promise} 刷新结果 + */ +export function refreshToken() { + return request({ + url: '/api/auth/refresh', + method: 'POST' + }) +} + +/** + * 退出登录 + * @returns {Promise} 退出结果 + */ +export function logout() { + return new Promise(resolve => { + resolve({ code: 200, msg: '退出成功' }) + }) +} + +/** + * 微信登录 + * @param {Object} data 登录数据 + * @param {string} data.code 微信授权码 + * @returns {Promise} 登录结果 + */ +export function wechatLogin(data) { + return request({ + url: '/api/auth/wechat-login', + method: 'POST', + data + }) +} + +/** + * Apple登录 + * @param {Object} data 登录数据 + * @param {string} data.identityToken Apple身份令牌 + * @returns {Promise} 登录结果 + */ +export function appleLogin(data) { + return request({ + url: '/api/auth/apple-login', + method: 'POST', + data + }) +} \ No newline at end of file diff --git a/Cunkebao/components/CustomTabBar.vue b/Cunkebao/components/CustomTabBar.vue new file mode 100644 index 00000000..02ff322d --- /dev/null +++ b/Cunkebao/components/CustomTabBar.vue @@ -0,0 +1,94 @@ + + + + + \ No newline at end of file diff --git a/Cunkebao/components/LineChart.vue b/Cunkebao/components/LineChart.vue new file mode 100644 index 00000000..af7a2789 --- /dev/null +++ b/Cunkebao/components/LineChart.vue @@ -0,0 +1,253 @@ + + + + + \ No newline at end of file diff --git a/Cunkebao/components/TabBar.vue b/Cunkebao/components/TabBar.vue new file mode 100644 index 00000000..d17718de --- /dev/null +++ b/Cunkebao/components/TabBar.vue @@ -0,0 +1,96 @@ + + + + + \ No newline at end of file diff --git a/Cunkebao/main.js b/Cunkebao/main.js new file mode 100644 index 00000000..0be225d2 --- /dev/null +++ b/Cunkebao/main.js @@ -0,0 +1,49 @@ +import Vue from 'vue' +import App from './App' + +// 引入uView UI +import uView from 'uview-ui' +Vue.use(uView) + +// 设置为 false 以阻止 Vue 在启动时生成生产提示 +Vue.config.productionTip = false + +// 导入全局样式 +import './uni.scss' + +// 导入请求拦截和封装 +import Request from './utils/request' +Vue.prototype.$request = Request + +// 导入API +import * as UserApi from './api/user' +Vue.prototype.$userApi = UserApi + +// 导入工具函数 +import Utils from './utils/common' +Vue.prototype.$utils = Utils + +// 导入权限检查 +import Auth from './utils/auth' +Vue.prototype.$auth = Auth + +App.mpType = 'app' + +// #ifdef MP +// 引入uView对小程序分享的mixin封装 +const mpShare = require('uview-ui/libs/mixin/mpShare.js') +Vue.mixin(mpShare) +// #endif + +const app = new Vue({ + ...App +}) + +// 挂载uView到Vue原型,使用时可以使用this.$u访问 +Vue.prototype.$u = Vue.prototype.$u || {} + +// 如果采用了自定义主题,必须加入这个 +import uviewTheme from './uni.scss' +Vue.prototype.$u.config.unit = 'rpx' + +app.$mount() \ No newline at end of file diff --git a/Cunkebao/manifest.json b/Cunkebao/manifest.json new file mode 100644 index 00000000..4743c167 --- /dev/null +++ b/Cunkebao/manifest.json @@ -0,0 +1,74 @@ +{ + "name" : "村客宝", + "appid" : "", + "description" : "村客宝应用", + "versionName" : "1.0.0", + "versionCode" : "100", + "transformPx" : false, + "app-plus" : { + "usingComponents" : true, + "nvueStyleCompiler" : "uni-app", + "compilerVersion" : 3, + "splashscreen" : { + "alwaysShowBeforeRender" : true, + "waiting" : true, + "autoclose" : true, + "delay" : 0 + }, + "modules" : {}, + "distribute" : { + "android" : { + "permissions" : [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ] + }, + "ios" : { + "dSYMs" : false + }, + "sdkConfigs" : { + "ad" : {} + } + } + }, + "quickapp" : {}, + "mp-weixin" : { + "appid" : "", + "setting" : { + "urlCheck" : false + }, + "usingComponents" : true + }, + "mp-alipay" : { + "usingComponents" : true + }, + "mp-baidu" : { + "usingComponents" : true + }, + "mp-toutiao" : { + "usingComponents" : true + }, + "h5" : { + "router" : { + "base" : "/" + }, + "template" : "index.html", + "optimization" : { + "treeShaking" : { + "enable" : true + } + }, + "title" : "村客宝" + } +} \ No newline at end of file diff --git a/Cunkebao/package-lock.json b/Cunkebao/package-lock.json new file mode 100644 index 00000000..8ec954d5 --- /dev/null +++ b/Cunkebao/package-lock.json @@ -0,0 +1,1762 @@ +{ + "name": "cunkebao", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cunkebao", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "uview-ui": "^2.0.31" + }, + "devDependencies": { + "autoprefixer": "^10.4.21", + "postcss-comment": "^2.0.0", + "postcss-import": "^16.1.0", + "sass": "^1.32.13", + "sass-loader": "^10.1.1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "peer": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "peer": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "peer": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.13.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", + "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", + "dev": true, + "peer": true, + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "peer": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "peer": true + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "peer": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "peer": true + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "peer": true + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001706", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001706.tgz", + "integrity": "sha512-3ZczoTApMAZwPKYWmwVbQMFpXBDds3/0VciVoUwPUbldlYyVLmRVuRs/PcUZtHpbLRpzzDvrvnFuREsGt6lUug==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "peer": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.123", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.123.tgz", + "integrity": "sha512-refir3NlutEZqlKaBLK0tzlVLe5P2wDKS7UQt/3SpibizgsRAPOsqQC3ffw1nlv3ze5gjRQZYHoPymgVZkplFA==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dev": true, + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true, + "peer": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "peer": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "peer": true + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "peer": true + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "peer": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "peer": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "peer": true + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-comment": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-comment/-/postcss-comment-2.0.0.tgz", + "integrity": "sha512-5zT5iKU7c0tK9KJFNrVf+g1MGTkzf/4V3e0Zzm2g1uoFQC5jeTHmB9O1iAqh97+jnKpc6al204e0pwFUiCwseg==", + "dev": true, + "dependencies": { + "postcss": "^6.0.0" + } + }, + "node_modules/postcss-comment/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-comment/node_modules/postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss-comment/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-import": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-16.1.0.tgz", + "integrity": "sha512-7hsAZ4xGXl4MW+OKEWCnF6T5jqBw80/EE9aXg1r2yyn1RsVEU8EtKXbijEODa+rg7iih4bKf7vlvTGYR4CnPNg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "peer": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peer": true + }, + "node_modules/sass": { + "version": "1.32.13", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.13.tgz", + "integrity": "sha512-dEgI9nShraqP7cXQH+lEXVf73WOPCse0QlFzSD8k+1TcOxCMwVXfQlr0jtoluZysQOyJGnfr21dLvYKDJq8HkA==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/sass-loader": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.1.1.tgz", + "integrity": "sha512-W6gVDXAd5hR/WHsPicvZdjAWHBcEJ44UahgxcIE196fW2ong0ZHMPO1kZuI5q0VlvMQZh32gpv69PLWQm70qrw==", + "dev": true, + "dependencies": { + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0", + "sass": "^1.3.0", + "webpack": "^4.36.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "peer": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", + "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "peer": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "dev": true, + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "peer": true + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uview-ui": { + "version": "2.0.31", + "resolved": "https://registry.npmjs.org/uview-ui/-/uview-ui-2.0.31.tgz", + "integrity": "sha512-I/0fGuvtiKHH/mBb864SGYk+SJ7WaF32tsBgYgeBOsxlUp+Th+Ac2tgz2cTvsQJl6eZYWsKZ3ixiSXCAcxZ8Sw==", + "engines": { + "HBuilderX": "^3.1.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "dev": true, + "peer": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.98.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", + "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", + "dev": true, + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "peer": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "dev": true, + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + } + } +} diff --git a/Cunkebao/package.json b/Cunkebao/package.json new file mode 100644 index 00000000..da2ffd08 --- /dev/null +++ b/Cunkebao/package.json @@ -0,0 +1,28 @@ +{ + "name": "cunkebao", + "version": "1.0.0", + "description": "村客宝 - 基于 uni-app 的跨平台应用", + "main": "main.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "uni-app", + "vue", + "小程序", + "APP", + "H5" + ], + "author": "CunkeBao Team", + "license": "MIT", + "dependencies": { + "uview-ui": "^2.0.31" + }, + "devDependencies": { + "sass": "^1.32.13", + "sass-loader": "^10.1.1", + "postcss-comment": "^2.0.0", + "postcss-import": "^14.0.2", + "autoprefixer": "^10.3.1" + } +} diff --git a/Cunkebao/pages.json b/Cunkebao/pages.json new file mode 100644 index 00000000..b17b2301 --- /dev/null +++ b/Cunkebao/pages.json @@ -0,0 +1,84 @@ +{ + "pages": [ + { + "path": "pages/index/index", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "首页" + } + }, + { + "path": "pages/login/index", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "登录", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black" + } + }, + { + "path": "pages/profile/index", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "我的" + } + }, + { + "path": "pages/agreement/user", + "style": { + "navigationBarTitleText": "用户协议", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black" + } + }, + { + "path": "pages/agreement/privacy", + "style": { + "navigationBarTitleText": "隐私政策", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black" + } + }, + { + "path": "pages/scenarios/index", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "场景获客" + } + }, + { + "path": "pages/scenarios/detail", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "获客详情" + } + }, + { + "path": "pages/scenarios/create", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "新建计划" + } + }, + { + "path": "pages/work/index", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "工作台" + } + } + ], + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "村客宝", + "navigationBarBackgroundColor": "#F8F8F8", + "backgroundColor": "#F8F8F8" + }, + "easycom": { + "autoscan": true, + "custom": { + "^u-(.*)": "uview-ui/components/u-$1/u-$1.vue", + "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue" + } + } +} \ No newline at end of file diff --git a/Cunkebao/pages/agreement/privacy.vue b/Cunkebao/pages/agreement/privacy.vue new file mode 100644 index 00000000..fe0e0da9 --- /dev/null +++ b/Cunkebao/pages/agreement/privacy.vue @@ -0,0 +1,201 @@ + + + + + \ No newline at end of file diff --git a/Cunkebao/pages/agreement/user.vue b/Cunkebao/pages/agreement/user.vue new file mode 100644 index 00000000..1dcf72a9 --- /dev/null +++ b/Cunkebao/pages/agreement/user.vue @@ -0,0 +1,154 @@ + + + + + \ No newline at end of file diff --git a/Cunkebao/pages/index/index.vue b/Cunkebao/pages/index/index.vue new file mode 100644 index 00000000..3802e58b --- /dev/null +++ b/Cunkebao/pages/index/index.vue @@ -0,0 +1,342 @@ + + + + + \ No newline at end of file diff --git a/Cunkebao/pages/login/index.vue b/Cunkebao/pages/login/index.vue new file mode 100644 index 00000000..56854e76 --- /dev/null +++ b/Cunkebao/pages/login/index.vue @@ -0,0 +1,445 @@ + + + + + \ No newline at end of file diff --git a/Cunkebao/pages/profile/index.vue b/Cunkebao/pages/profile/index.vue new file mode 100644 index 00000000..8ae4d418 --- /dev/null +++ b/Cunkebao/pages/profile/index.vue @@ -0,0 +1,297 @@ + + + + + \ No newline at end of file diff --git a/Cunkebao/pages/scenarios/create.vue b/Cunkebao/pages/scenarios/create.vue new file mode 100644 index 00000000..e91dbb50 --- /dev/null +++ b/Cunkebao/pages/scenarios/create.vue @@ -0,0 +1,203 @@ + + + + + \ No newline at end of file diff --git a/Cunkebao/pages/scenarios/detail.vue b/Cunkebao/pages/scenarios/detail.vue new file mode 100644 index 00000000..969435d9 --- /dev/null +++ b/Cunkebao/pages/scenarios/detail.vue @@ -0,0 +1,482 @@ + + + + + \ No newline at end of file diff --git a/Cunkebao/pages/scenarios/index.vue b/Cunkebao/pages/scenarios/index.vue new file mode 100644 index 00000000..d8864fef --- /dev/null +++ b/Cunkebao/pages/scenarios/index.vue @@ -0,0 +1,447 @@ + + + + + \ No newline at end of file diff --git a/Cunkebao/pages/work/index.vue b/Cunkebao/pages/work/index.vue new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/Cunkebao/pages/work/index.vue @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Cunkebao/postcss.config.js b/Cunkebao/postcss.config.js new file mode 100644 index 00000000..902a44fd --- /dev/null +++ b/Cunkebao/postcss.config.js @@ -0,0 +1,23 @@ +const path = require('path') + +module.exports = { + parser: require('postcss-comment'), + plugins: [ + require('postcss-import')({ + resolve(id, basedir, importOptions) { + if (id.startsWith('~@/')) { + return path.resolve(process.env.UNI_INPUT_DIR, id.substr(3)) + } else if (id.startsWith('@/')) { + return path.resolve(process.env.UNI_INPUT_DIR, id.substr(2)) + } else if (id.startsWith('/') && !id.startsWith('//')) { + return path.resolve(process.env.UNI_INPUT_DIR, id.substr(1)) + } + return id + } + }), + require('autoprefixer')({ + remove: process.env.UNI_PLATFORM !== 'h5' + }), + require('@dcloudio/vue-cli-plugin-uni/packages/postcss') + ] +} \ No newline at end of file diff --git a/Cunkebao/static/fonts/fonts.css b/Cunkebao/static/fonts/fonts.css new file mode 100644 index 00000000..c09b4e0b --- /dev/null +++ b/Cunkebao/static/fonts/fonts.css @@ -0,0 +1,27 @@ +@font-face { + font-family: 'Digital-Bold'; + src: url('https://cdn.jsdelivr.net/npm/alibaba-puhuiti@1.0.0/AlibabaPuHuiTi-Bold.ttf') format('truetype'); + font-weight: bold; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'Digital-Medium'; + src: url('https://cdn.jsdelivr.net/npm/alibaba-puhuiti@1.0.0/AlibabaPuHuiTi-Medium.ttf') format('truetype'); + font-weight: normal; + font-style: normal; + font-display: swap; +} + +.digital-text { + font-family: 'Digital-Bold', sans-serif; + font-weight: bold; + letter-spacing: 0.5px; +} + +.digital-number { + font-family: 'Digital-Bold', sans-serif; + font-weight: bold; + letter-spacing: 0.5px; +} \ No newline at end of file diff --git a/Cunkebao/static/images/apple.png b/Cunkebao/static/images/apple.png new file mode 100644 index 00000000..d6b26475 --- /dev/null +++ b/Cunkebao/static/images/apple.png @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/Cunkebao/static/images/icons/heartbeat.svg b/Cunkebao/static/images/icons/heartbeat.svg new file mode 100644 index 00000000..38006377 --- /dev/null +++ b/Cunkebao/static/images/icons/heartbeat.svg @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/Cunkebao/static/images/icons/smartphone.svg b/Cunkebao/static/images/icons/smartphone.svg new file mode 100644 index 00000000..556da438 --- /dev/null +++ b/Cunkebao/static/images/icons/smartphone.svg @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/Cunkebao/static/images/icons/team.svg b/Cunkebao/static/images/icons/team.svg new file mode 100644 index 00000000..8d82f6e9 --- /dev/null +++ b/Cunkebao/static/images/icons/team.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Cunkebao/static/images/icons/users.svg b/Cunkebao/static/images/icons/users.svg new file mode 100644 index 00000000..a623fe4b --- /dev/null +++ b/Cunkebao/static/images/icons/users.svg @@ -0,0 +1,24 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Cunkebao/static/images/icons/wechat.svg b/Cunkebao/static/images/icons/wechat.svg new file mode 100644 index 00000000..1c32bafc --- /dev/null +++ b/Cunkebao/static/images/icons/wechat.svg @@ -0,0 +1,21 @@ + + + + + + + + + \ No newline at end of file diff --git a/Cunkebao/static/images/tabbar/home-active.png b/Cunkebao/static/images/tabbar/home-active.png new file mode 100644 index 00000000..258b9b8b --- /dev/null +++ b/Cunkebao/static/images/tabbar/home-active.png @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Cunkebao/static/images/tabbar/home.png b/Cunkebao/static/images/tabbar/home.png new file mode 100644 index 00000000..a8a4961e --- /dev/null +++ b/Cunkebao/static/images/tabbar/home.png @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Cunkebao/static/images/tabbar/market-active.png b/Cunkebao/static/images/tabbar/market-active.png new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/Cunkebao/static/images/tabbar/market-active.png @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Cunkebao/static/images/tabbar/market.png b/Cunkebao/static/images/tabbar/market.png new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/Cunkebao/static/images/tabbar/market.png @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Cunkebao/static/images/tabbar/profile-active.png b/Cunkebao/static/images/tabbar/profile-active.png new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/Cunkebao/static/images/tabbar/profile-active.png @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Cunkebao/static/images/tabbar/profile.png b/Cunkebao/static/images/tabbar/profile.png new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/Cunkebao/static/images/tabbar/profile.png @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Cunkebao/static/images/tabbar/work-active.png b/Cunkebao/static/images/tabbar/work-active.png new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/Cunkebao/static/images/tabbar/work-active.png @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Cunkebao/static/images/tabbar/work.png b/Cunkebao/static/images/tabbar/work.png new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/Cunkebao/static/images/tabbar/work.png @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Cunkebao/uni.scss b/Cunkebao/uni.scss new file mode 100644 index 00000000..700a1e67 --- /dev/null +++ b/Cunkebao/uni.scss @@ -0,0 +1,68 @@ +/* 主题色变量 */ +$primary-color: #4080ff; +$success-color: #07c160; +$warning-color: #ff9900; +$error-color: #fa5151; +$info-color: #909399; + +/* 文字颜色 */ +$text-color-main: #333333; +$text-color-regular: #666666; +$text-color-secondary: #999999; +$text-color-placeholder: #c0c4cc; +$text-color-white: #ffffff; + +/* 边框颜色 */ +$border-color-base: #dcdfe6; +$border-color-light: #e4e7ed; +$border-color-lighter: #ebeef5; +$border-color-extra-light: #f2f6fc; + +/* 背景颜色 */ +$bg-color: #f9fafb; +$bg-color-white: #ffffff; +$bg-color-primary: rgba(64, 128, 255, 0.1); +$bg-color-success: rgba(7, 193, 96, 0.1); +$bg-color-warning: rgba(255, 153, 0, 0.1); +$bg-color-error: rgba(250, 81, 81, 0.1); + +/* 字体大小 */ +$font-size-xs: 20rpx; +$font-size-sm: 24rpx; +$font-size-base: 28rpx; +$font-size-medium: 32rpx; +$font-size-lg: 36rpx; +$font-size-xl: 40rpx; +$font-size-xxl: 48rpx; + +/* 圆角大小 */ +$border-radius-sm: 4rpx; +$border-radius-base: 8rpx; +$border-radius-lg: 16rpx; +$border-radius-circle: 50%; + +/* 间距大小 */ +$spacing-xs: 10rpx; +$spacing-sm: 20rpx; +$spacing-base: 30rpx; +$spacing-lg: 40rpx; +$spacing-xl: 50rpx; + +/* 字体加粗 */ +$font-weight-normal: 400; +$font-weight-medium: 500; +$font-weight-bold: 700; + +/* 阴影 */ +$shadow-sm: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); +$shadow-base: 0 4rpx 12rpx rgba(0, 0, 0, 0.08); +$shadow-lg: 0 8rpx 16rpx rgba(0, 0, 0, 0.1); + +/* 基础动画 */ +$animation-duration-fast: 0.2s; +$animation-duration-base: 0.3s; +$animation-duration-slow: 0.4s; +$animation-timing-function-base: ease-in-out; + +/* 导入uView的变量 */ +@import 'uview-ui/theme.scss'; \ No newline at end of file diff --git a/Cunkebao/utils/auth.js b/Cunkebao/utils/auth.js new file mode 100644 index 00000000..896a29c5 --- /dev/null +++ b/Cunkebao/utils/auth.js @@ -0,0 +1,96 @@ +/** + * 认证相关工具函数 + */ + +const TOKEN_KEY = 'token'; +const TOKEN_EXPIRES_KEY = 'token_expires'; +const USER_INFO_KEY = 'user_info'; + +/** + * 设置Token + * @param {string} token 令牌 + * @param {number} expires 过期时间(秒) + */ +function setToken(token, expires = 7200) { + uni.setStorageSync(TOKEN_KEY, token); + + // 计算过期时间点(当前时间 + 有效期) + const expiresTime = Math.floor(Date.now() / 1000) + expires; + uni.setStorageSync(TOKEN_EXPIRES_KEY, expiresTime); +} + +/** + * 获取Token + * @returns {string} token值 + */ +function getToken() { + return uni.getStorageSync(TOKEN_KEY); +} + +/** + * 移除Token + */ +function removeToken() { + uni.removeStorageSync(TOKEN_KEY); + uni.removeStorageSync(TOKEN_EXPIRES_KEY); +} + +/** + * 设置用户信息 + * @param {Object} userInfo 用户信息 + */ +function setUserInfo(userInfo) { + uni.setStorageSync(USER_INFO_KEY, JSON.stringify(userInfo)); +} + +/** + * 获取用户信息 + * @returns {Object} 用户信息 + */ +function getUserInfo() { + const userInfo = uni.getStorageSync(USER_INFO_KEY); + return userInfo ? JSON.parse(userInfo) : null; +} + +/** + * 移除用户信息 + */ +function removeUserInfo() { + uni.removeStorageSync(USER_INFO_KEY); +} + +/** + * 移除所有认证信息 + */ +function removeAll() { + removeToken(); + removeUserInfo(); +} + +/** + * 判断是否已登录 + * @returns {boolean} 是否已登录 + */ +function isLogin() { + const token = getToken(); + // 如果没有token,直接返回未登录 + if (!token) return false; + + // 检查token是否过期 + const expiresTime = uni.getStorageSync(TOKEN_EXPIRES_KEY) || 0; + const nowTime = Math.floor(Date.now() / 1000); + + // 如果当前时间超过过期时间,则返回未登录 + return nowTime < expiresTime; +} + +export default { + setToken, + getToken, + removeToken, + setUserInfo, + getUserInfo, + removeUserInfo, + removeAll, + isLogin +}; \ No newline at end of file diff --git a/Cunkebao/utils/common.js b/Cunkebao/utils/common.js new file mode 100644 index 00000000..69fa5a2f --- /dev/null +++ b/Cunkebao/utils/common.js @@ -0,0 +1,141 @@ +/** + * 通用工具函数集合 + */ + +/** + * 格式化日期 + * @param {Date|string|number} date 日期对象/日期字符串/时间戳 + * @param {string} format 格式化模板,如:YYYY-MM-DD HH:mm:ss + * @returns {string} 格式化后的日期字符串 + */ +function formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') { + if (!date) return ''; + + // 如果是时间戳或字符串,转为日期对象 + if (typeof date === 'string' || typeof date === 'number') { + date = new Date(date); + } + + // 定义替换规则 + const rules = { + 'YYYY': date.getFullYear(), + 'MM': padZero(date.getMonth() + 1), + 'DD': padZero(date.getDate()), + 'HH': padZero(date.getHours()), + 'mm': padZero(date.getMinutes()), + 'ss': padZero(date.getSeconds()) + }; + + // 替换 + return format.replace(/(YYYY|MM|DD|HH|mm|ss)/g, function(key) { + return rules[key]; + }); +} + +/** + * 补零 + * @param {number} num 数字 + * @returns {string} 补零后的字符串 + */ +function padZero(num) { + return String(num).padStart(2, '0'); +} + +/** + * 格式化手机号 + * @param {string} mobile 手机号 + * @returns {string} 格式化后的手机号,如:138****8888 + */ +function formatMobile(mobile) { + if (!mobile) return ''; + return mobile.replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2'); +} + +/** + * 生成UUID + * @returns {string} UUID + */ +function generateUUID() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + const r = Math.random() * 16 | 0; + const v = c === 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); +} + +/** + * 深度克隆对象 + * @param {Object} obj 需要克隆的对象 + * @returns {Object} 克隆后的对象 + */ +function deepClone(obj) { + if (obj === null || typeof obj !== 'object') { + return obj; + } + + // 处理日期 + if (obj instanceof Date) { + return new Date(obj.getTime()); + } + + // 处理数组 + if (obj instanceof Array) { + return obj.map(item => deepClone(item)); + } + + // 处理对象 + const clonedObj = {}; + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + clonedObj[key] = deepClone(obj[key]); + } + } + + return clonedObj; +} + +/** + * 防抖函数 + * @param {Function} fn 需要防抖的函数 + * @param {number} delay 延迟时间,单位ms + * @returns {Function} 防抖后的函数 + */ +function debounce(fn, delay = 300) { + let timer = null; + + return function(...args) { + if (timer) clearTimeout(timer); + + timer = setTimeout(() => { + fn.apply(this, args); + }, delay); + }; +} + +/** + * 节流函数 + * @param {Function} fn 需要节流的函数 + * @param {number} delay 延迟时间,单位ms + * @returns {Function} 节流后的函数 + */ +function throttle(fn, delay = 300) { + let lastTime = 0; + + return function(...args) { + const now = Date.now(); + + if (now - lastTime >= delay) { + fn.apply(this, args); + lastTime = now; + } + }; +} + +export default { + formatDate, + formatMobile, + generateUUID, + deepClone, + debounce, + throttle +}; \ No newline at end of file diff --git a/Cunkebao/utils/request.js b/Cunkebao/utils/request.js new file mode 100644 index 00000000..4359f87b --- /dev/null +++ b/Cunkebao/utils/request.js @@ -0,0 +1,132 @@ +import Auth from './auth'; + +// 服务器地址 +const BASE_URL = process.env.NODE_ENV === 'development' + ? 'http://localhost:8080' + : 'https://api.example.com'; + +// 请求超时时间 +const TIMEOUT = 10000; + +/** + * 请求拦截器 + * @param {Object} config 请求配置 + * @returns {Object} 处理后的请求配置 + */ +function requestInterceptor(config) { + // 获取 token + const token = uni.getStorageSync('token'); + + // 如果有 token,则带上请求头 + if (token) { + config.header = { + ...config.header, + 'Authorization': `Bearer ${token}` + }; + } + + // 打印请求日志 + console.log('请求地址:', `${config.baseURL || BASE_URL}${config.url}`); + + return config; +} + +/** + * 响应拦截器 + * @param {Object} response 响应数据 + * @returns {Object|Promise} 处理后的响应数据或Promise + */ +function responseInterceptor(response) { + // 未登录或token失效 - 取消登录拦截 + if (response.data.code === 401) { + // 只在控制台打印信息,不进行拦截 + console.log('登录已过期,但不进行拦截'); + + /* + // 以下代码已注释,取消登录拦截 + // 清除登录信息 + Auth.removeToken(); + Auth.removeUserInfo(); + + // 跳转到登录页 + uni.reLaunch({ + url: '/pages/login/index' + }); + + return Promise.reject(new Error('登录已过期,请重新登录')); + */ + + // 直接返回响应,不拦截 + return response.data; + } + + // token需要刷新 - 取消登录拦截 + if (response.data.code === 410) { + // 只在控制台打印信息,不进行拦截 + console.log('Token需要刷新,但不进行拦截'); + + /* + // 以下代码已注释,取消登录拦截 + // 处理token刷新逻辑,这里简化处理 + uni.reLaunch({ + url: '/pages/login/index' + }); + + return Promise.reject(new Error('登录已过期,请重新登录')); + */ + + // 直接返回响应,不拦截 + return response.data; + } + + return response.data; +} + +/** + * 统一请求函数 + * @param {Object} options 请求选项 + * @returns {Promise} 请求结果 + */ +function request(options) { + // 合并请求选项 + const config = { + baseURL: BASE_URL, + timeout: TIMEOUT, + header: { + 'Content-Type': 'application/json' + }, + ...options + }; + + // 请求拦截 + const interceptedConfig = requestInterceptor(config); + + // 发起请求 + return new Promise((resolve, reject) => { + uni.request({ + url: `${interceptedConfig.baseURL}${interceptedConfig.url}`, + method: interceptedConfig.method || 'GET', + data: interceptedConfig.data, + header: interceptedConfig.header, + timeout: interceptedConfig.timeout, + success: (res) => { + try { + const result = responseInterceptor(res); + resolve(result); + } catch (error) { + reject(error); + } + }, + fail: (err) => { + uni.showToast({ + title: '网络请求失败', + icon: 'none', + duration: 2000 + }); + reject(err); + } + }); + }); +} + +export default request; \ No newline at end of file diff --git a/Server/.env b/Server/.env index cf5b3d6e..9a16c12e 100644 --- a/Server/.env +++ b/Server/.env @@ -4,7 +4,7 @@ trace = true [database] type = mysql -hostname = 127.0.0.1 +hostname = 103.144.2.26 database = yi_54iis_com username = yi_54iis_com password = c1RbMwrZCCyxF1bC diff --git a/Server/application/common/config/route.php b/Server/application/common/config/route.php index e8bb5000..c7f823d8 100644 --- a/Server/application/common/config/route.php +++ b/Server/application/common/config/route.php @@ -13,4 +13,10 @@ Route::group('v1/auth', function () { // 需要JWT认证的接口 Route::get('info', 'app\\common\\controller\\Auth@info')->middleware(['jwt']); // 获取用户信息 Route::post('refresh', 'app\\common\\controller\\Auth@refresh')->middleware(['jwt']); // 刷新令牌 -}); \ No newline at end of file +}); + +// 附件上传相关路由 +Route::group('v1/', function () { + Route::post('attachment/upload', 'app\\common\\controller\\Attachment@upload'); // 上传附件 + Route::get('attachment/:id', 'app\\common\\controller\\Attachment@info'); // 获取附件信息 +})->middleware(['jwt']); \ No newline at end of file diff --git a/Server/application/common/controller/Attachment.php b/Server/application/common/controller/Attachment.php new file mode 100644 index 00000000..78bbb759 --- /dev/null +++ b/Server/application/common/controller/Attachment.php @@ -0,0 +1,140 @@ + 400, + 'msg' => '请选择要上传的文件' + ]); + } + + // 验证文件 + $validate = \think\facade\Validate::rule([ + 'file' => [ + 'fileSize' => 10485760, // 10MB + 'fileExt' => 'jpg,jpeg,png,gif,doc,docx,pdf,zip,rar', + 'fileMime' => 'image/jpeg,image/png,image/gif,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf,application/zip,application/x-rar-compressed' + ] + ]); + + if (!$validate->check(['file' => $file])) { + return json([ + 'code' => 400, + 'msg' => $validate->getError() + ]); + } + + // 生成文件hash + $hashKey = md5_file($file->getRealPath()); + + // 检查文件是否已存在 + $existFile = AttachmentModel::getByHashKey($hashKey); + if ($existFile) { + return json([ + 'code' => 200, + 'msg' => '文件已存在', + 'data' => [ + 'id' => $existFile['id'], + 'name' => $existFile['name'], + 'url' => $existFile['source'] + ] + ]); + } + + // 生成OSS对象名称 + $objectName = AliyunOSS::generateObjectName($file->getOriginalName()); + + // 上传到OSS + $result = AliyunOSS::uploadFile($file->getRealPath(), $objectName); + + if (!$result['success']) { + return json([ + 'code' => 500, + 'msg' => '文件上传失败:' . $result['error'] + ]); + } + + // 保存到数据库 + $attachmentData = [ + 'name' => Request::param('name') ?: $file->getOriginalName(), + 'hash_key' => $hashKey, + 'server' => 'aliyun_oss', + 'source' => $result['url'], + 'size' => $result['size'], + 'suffix' => pathinfo($file->getOriginalName(), PATHINFO_EXTENSION) + ]; + + $attachmentId = AttachmentModel::addAttachment($attachmentData); + + if (!$attachmentId) { + return json([ + 'code' => 500, + 'msg' => '保存附件信息失败' + ]); + } + + return json([ + 'code' => 200, + 'msg' => '上传成功', + 'data' => [ + 'id' => $attachmentId, + 'name' => $attachmentData['name'], + 'url' => $attachmentData['source'] + ] + ]); + + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => '上传失败:' . $e->getMessage() + ]); + } + } + + /** + * 获取附件信息 + * @param int $id 附件ID + * @return \think\response\Json + */ + public function info($id) + { + try { + $attachment = AttachmentModel::find($id); + + if (!$attachment) { + return json([ + 'code' => 404, + 'msg' => '附件不存在' + ]); + } + + return json([ + 'code' => 200, + 'msg' => '获取成功', + 'data' => $attachment + ]); + + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => '获取失败:' . $e->getMessage() + ]); + } + } +} \ No newline at end of file diff --git a/Server/application/common/model/Attachment.php b/Server/application/common/model/Attachment.php new file mode 100644 index 00000000..cf3a8a57 --- /dev/null +++ b/Server/application/common/model/Attachment.php @@ -0,0 +1,55 @@ + 'integer', + 'dl_count' => 'integer', + 'size' => 'integer', + 'scene' => 'integer', + 'create_at' => 'datetime', + 'update_at' => 'datetime', + 'delete_at' => 'datetime' + ]; + + /** + * 添加附件记录 + * @param array $data 附件数据 + * @return int|bool + */ + public static function addAttachment($data) + { + $attachment = new self(); + return $attachment->allowField(true)->save($data); + } + + /** + * 根据hash_key获取附件 + * @param string $hashKey + * @return array|null + */ + public static function getByHashKey($hashKey) + { + return self::where('hash_key', $hashKey) + ->where('delete_at', null) + ->find(); + } +} \ No newline at end of file diff --git a/Server/application/common/util/AliyunOSS.php b/Server/application/common/util/AliyunOSS.php new file mode 100644 index 00000000..a8ec91c8 --- /dev/null +++ b/Server/application/common/util/AliyunOSS.php @@ -0,0 +1,77 @@ +getMessage()); + } + } + + /** + * 上传文件到OSS + * @param string $filePath 本地文件路径 + * @param string $objectName OSS对象名称 + * @return array + * @throws OssException + */ + public static function uploadFile($filePath, $objectName) + { + try { + $client = self::getClient(); + + // 上传文件 + $result = $client->uploadFile(self::BUCKET, $objectName, $filePath); + + // 获取文件访问URL + $url = $client->signUrl(self::BUCKET, $objectName, 3600); + + return [ + 'success' => true, + 'url' => $url, + 'object_name' => $objectName, + 'size' => filesize($filePath), + 'mime_type' => mime_content_type($filePath) + ]; + } catch (OssException $e) { + return [ + 'success' => false, + 'error' => $e->getMessage() + ]; + } + } + + /** + * 生成OSS对象名称 + * @param string $originalName 原始文件名 + * @return string + */ + public static function generateObjectName($originalName) + { + $ext = pathinfo($originalName, PATHINFO_EXTENSION); + $name = md5(uniqid(mt_rand(), true)); + return date('Y/m/d/') . $name . '.' . $ext; + } +} \ No newline at end of file diff --git a/Server/application/common/AliyunSMS.php b/Server/application/common/util/AliyunSMS.php similarity index 98% rename from Server/application/common/AliyunSMS.php rename to Server/application/common/util/AliyunSMS.php index 3a920d4b..e742591a 100644 --- a/Server/application/common/AliyunSMS.php +++ b/Server/application/common/util/AliyunSMS.php @@ -1,6 +1,6 @@ middleware(['jwt']); \ No newline at end of file diff --git a/Server/application/devices/controller/DeviceWechat.php b/Server/application/devices/controller/DeviceWechat.php new file mode 100644 index 00000000..5e151423 --- /dev/null +++ b/Server/application/devices/controller/DeviceWechat.php @@ -0,0 +1,476 @@ + 200, + 'msg' => '获取成功', + 'data' => [ + 'count' => $count + ] + ]); + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => '获取失败:' . $e->getMessage() + ]); + } + } + + /** + * 获取有登录微信的设备数量 + * @return \think\response\Json + */ + public function deviceCount() + { + try { + // 获取有登录微信的设备数量 + $count = WechatAccount::getDeviceWithWechatCount(); + + return json([ + 'code' => 200, + 'msg' => '获取成功', + 'data' => [ + 'count' => $count + ] + ]); + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => '获取失败:' . $e->getMessage() + ]); + } + } + + /** + * 刷新设备微信状态 + * @return \think\response\Json + */ + public function refresh() + { + try { + return json([ + 'code' => 200, + 'msg' => '刷新成功', + 'data' => [] + ]); + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => '获取失败:' . $e->getMessage() + ]); + } + } + + /** + * 获取在线微信账号列表 + * @return \think\response\Json + */ + public function index() + { + try { + // 获取查询条件 + $where = []; + + // 微信ID + $wechatId = Request::param('wechat_id'); + if (!empty($wechatId)) { + $where['wechatId'] = ['like', "%{$wechatId}%"]; + } + + // 昵称 + $nickname = Request::param('nickname'); + if (!empty($nickname)) { + $where['nickname|accountNickname'] = ['like', "%{$nickname}%"]; + } + + // 获取分页参数 + $page = (int)Request::param('page', 1); + $limit = (int)Request::param('limit', 10); + + // 获取排序参数 + $sort = Request::param('sort', 'id'); + $order = Request::param('order', 'desc'); + + // 获取在线微信账号列表 + $list = WechatAccount::getOnlineWechatList($where, "{$sort} {$order}", $page, $limit); + + // 处理返回数据 + $data = []; + foreach ($list->items() as $item) { + // 计算今日可添加好友数量(这里使用一个示例算法,你可以根据实际需求修改) + $canAddFriendCount = 30 - (isset($item['yesterdayMsgCount']) ? intval($item['yesterdayMsgCount']) : 0); + if ($canAddFriendCount < 0) { + $canAddFriendCount = 0; + } + + // 计算今日新增好友数量(示例数据,实际需要从数据库获取或通过其他方式计算) + // 这里只是一个示例,你需要根据实际情况替换 + $todayNewFriendCount = mt_rand(0, 10); // 随机生成0-10的数字作为示例 + + $data[] = [ + 'id' => $item['id'], + 'wechatId' => $item['wechatId'], + 'nickname' => $item['nickname'] ?: $item['accountNickname'], + 'avatar' => $item['avatar'], + 'accountUserName' => $item['accountUserName'], + 'status' => $item['wechatAlive'] ? '在线' : '离线', + 'deviceStatus' => $item['deviceAlive'] ? '在线' : '离线', + 'totalFriend' => $item['totalFriend'], + 'canAddFriendCount' => $canAddFriendCount, + 'deviceInfo' => $item['imei'] . ($item['deviceMemo'] ? " ({$item['deviceMemo']})" : ''), + 'todayNewFriendCount' => $todayNewFriendCount + ]; + } + + return json([ + 'code' => 200, + 'msg' => '获取成功', + 'data' => [ + 'total' => $list->total(), + 'list' => $data + ] + ]); + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => '获取失败:' . $e->getMessage() + ]); + } + } + + /** + * 获取微信号详情 + * @param int $id 微信号ID + * @return \think\response\Json + */ + public function detail($id) + { + try { + // 获取微信号基本信息 + $wechat = WechatAccount::where('id', $id) + ->where('isDeleted', 0) + ->find(); + + if (!$wechat) { + return json([ + 'code' => 404, + 'msg' => '微信号不存在' + ]); + } + + // 计算账号年龄(从创建时间到现在) + $accountAge = 0; + if ($wechat['createTime']) { + $createTime = strtotime($wechat['createTime']); + $now = time(); + $accountAge = floor(($now - $createTime) / (24 * 3600)); + } + + // 计算活跃程度(根据消息数) + $activityLevel = '低'; + if ($wechat['thirtyDayMsgCount'] > 1000) { + $activityLevel = '高'; + } elseif ($wechat['thirtyDayMsgCount'] > 500) { + $activityLevel = '中'; + } + + // 评估账号权重(示例算法) + $weight = 0; + // 基础权重 + $weight += 10; + // 好友数量权重 + $weight += min($wechat['totalFriend'] / 100, 20); + // 活跃度权重 + $weight += min($wechat['thirtyDayMsgCount'] / 100, 20); + // 账号年龄权重 + $weight += min($accountAge / 30, 10); + // 在线状态权重 + if ($wechat['wechatAlive']) { + $weight += 5; + } + + // 获取限制记录(示例数据,实际需要从数据库获取) + $restrictions = [ + [ + 'type' => '添加好友限制', + 'reason' => '频繁添加好友', + 'startTime' => date('Y-m-d H:i:s', strtotime('-1 day')), + 'endTime' => date('Y-m-d H:i:s', strtotime('+1 day')) + ] + ]; + + // 获取微信好友列表 + $friends = Db::table('tk_wechat_friend') + ->where('wechatAccountId', $id) + ->where('isDeleted', 0) + ->field([ + 'id', + 'wechatId', + 'nickname', + 'avatar', + 'gender', + 'region', + 'signature', + 'labels', + 'createTime' + ]) + ->select(); + + // 处理返回数据 + $data = [ + 'basicInfo' => [ + 'id' => $wechat['id'], + 'wechatId' => $wechat['wechatId'], + 'nickname' => $wechat['nickname'] ?: $wechat['accountNickname'], + 'avatar' => $wechat['avatar'], + 'status' => $wechat['wechatAlive'] ? '在线' : '离线', + 'deviceStatus' => $wechat['deviceAlive'] ? '在线' : '离线', + 'deviceInfo' => $wechat['imei'] . ($wechat['deviceMemo'] ? " ({$wechat['deviceMemo']})" : ''), + 'gender' => $wechat['gender'], + 'region' => $wechat['region'], + 'signature' => $wechat['signature'] + ], + 'statistics' => [ + 'totalFriend' => $wechat['totalFriend'], + 'maleFriend' => $wechat['maleFriend'], + 'femaleFriend' => $wechat['femaleFriend'], + 'canAddFriendCount' => 30 - (isset($wechat['yesterdayMsgCount']) ? intval($wechat['yesterdayMsgCount']) : 0), + 'yesterdayMsgCount' => $wechat['yesterdayMsgCount'], + 'sevenDayMsgCount' => $wechat['sevenDayMsgCount'], + 'thirtyDayMsgCount' => $wechat['thirtyDayMsgCount'] + ], + 'accountInfo' => [ + 'age' => $accountAge, + 'activityLevel' => $activityLevel, + 'weight' => round($weight, 2), + 'createTime' => $wechat['createTime'], + 'lastUpdateTime' => $wechat['updateTime'] + ], + 'restrictions' => $restrictions, + 'friends' => $friends + ]; + + return json([ + 'code' => 200, + 'msg' => '获取成功', + 'data' => $data + ]); + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => '获取失败:' . $e->getMessage() + ]); + } + } + + /** + * 微信好友转移 + * 将一个微信号的好友转移至另一个在线微信号 + * + * @return \think\response\Json + */ + public function transferFriends() + { + try { + // 获取请求参数 + $sourceWechatId = Request::param('source_id'); // 源微信账号ID + $targetWechatId = Request::param('target_id'); // 目标微信账号ID + + // 参数验证 + if (empty($sourceWechatId) || empty($targetWechatId)) { + return json([ + 'code' => 400, + 'msg' => '参数错误:源微信账号ID和目标微信账号ID不能为空' + ]); + } + + // 检查源微信账号是否存在 + $sourceWechat = WechatAccount::where('id', $sourceWechatId) + ->where('isDeleted', 0) + ->find(); + + if (!$sourceWechat) { + return json([ + 'code' => 404, + 'msg' => '源微信账号不存在' + ]); + } + + // 检查目标微信账号是否存在且在线 + $targetWechat = WechatAccount::where('id', $targetWechatId) + ->where('isDeleted', 0) + ->where('wechatAlive', 1) + ->where('deviceAlive', 1) + ->find(); + + if (!$targetWechat) { + return json([ + 'code' => 404, + 'msg' => '目标微信账号不存在或不在线' + ]); + } + + // 获取源微信账号的好友列表 + $friends = Db::table('tk_wechat_friend') + ->where('wechatAccountId', $sourceWechatId) + ->where('isDeleted', 0) + ->select(); + + // 统计好友数量 + $totalFriends = count($friends); + + if ($totalFriends == 0) { + return json([ + 'code' => 400, + 'msg' => '源微信账号没有可转移的好友' + ]); + } + + // 开始事务 + Db::startTrans(); + + try { + $successCount = 0; + $failCount = 0; + $duplicateCount = 0; + $failList = []; + + foreach ($friends as $friend) { + // 检查目标微信账号是否已经有此好友 + $existFriend = Db::table('tk_wechat_friend') + ->where('wechatAccountId', $targetWechatId) + ->where('wechatId', $friend['wechatId']) + ->where('isDeleted', 0) + ->find(); + + if ($existFriend) { + // 已经存在此好友,跳过 + $duplicateCount++; + continue; + } + + // 准备插入数据 + $newFriend = [ + 'wechatAccountId' => $targetWechatId, + 'alias' => $friend['alias'], + 'wechatId' => $friend['wechatId'], + 'conRemark' => $friend['conRemark'], + 'nickname' => $friend['nickname'], + 'pyInitial' => $friend['pyInitial'], + 'quanPin' => $friend['quanPin'], + 'avatar' => $friend['avatar'], + 'gender' => $friend['gender'], + 'region' => $friend['region'], + 'addFrom' => $friend['addFrom'], + 'labels' => $friend['labels'], + 'signature' => $friend['signature'], + 'isDeleted' => 0, + 'isPassed' => $friend['isPassed'], + 'accountId' => $friend['accountId'], + 'extendFields' => $friend['extendFields'], + 'accountUserName' => $friend['accountUserName'], + 'accountRealName' => $friend['accountRealName'], + 'accountNickname' => $friend['accountNickname'], + 'ownerAlias' => $targetWechat['alias'], + 'ownerWechatId' => $targetWechat['wechatId'], + 'ownerNickname' => $targetWechat['nickname'] ?: $targetWechat['accountNickname'], + 'ownerAvatar' => $targetWechat['avatar'], + 'phone' => $friend['phone'], + 'thirdParty' => $friend['thirdParty'], + 'groupId' => $friend['groupId'], + 'passTime' => $friend['passTime'], + 'additionalPicture' => $friend['additionalPicture'], + 'desc' => $friend['desc'], + 'country' => $friend['country'], + 'province' => $friend['province'], + 'city' => $friend['city'], + 'createTime' => date('Y-m-d H:i:s'), + 'updateTime' => date('Y-m-d H:i:s') + ]; + + // 插入新好友记录 + $result = Db::table('tk_wechat_friend')->insert($newFriend); + + if ($result) { + $successCount++; + } else { + $failCount++; + $failList[] = [ + 'id' => $friend['id'], + 'wechatId' => $friend['wechatId'], + 'nickname' => $friend['nickname'] + ]; + } + } + + // 更新两个微信账号的好友数量 + $maleFriendsCount = Db::table('tk_wechat_friend') + ->where('wechatAccountId', $targetWechatId) + ->where('isDeleted', 0) + ->where('gender', 1) + ->count(); + + $femaleFriendsCount = Db::table('tk_wechat_friend') + ->where('wechatAccountId', $targetWechatId) + ->where('isDeleted', 0) + ->where('gender', 2) + ->count(); + + $totalFriendsCount = $maleFriendsCount + $femaleFriendsCount; + + // 更新目标微信账号的好友数量 + WechatAccount::where('id', $targetWechatId) + ->update([ + 'totalFriend' => $totalFriendsCount, + 'maleFriend' => $maleFriendsCount, + 'femaleFriend' => $femaleFriendsCount, + 'updateTime' => date('Y-m-d H:i:s') + ]); + + // 提交事务 + Db::commit(); + + return json([ + 'code' => 200, + 'msg' => '好友转移成功', + 'data' => [ + 'total' => $totalFriends, + 'success' => $successCount, + 'fail' => $failCount, + 'duplicate' => $duplicateCount, + 'failList' => $failList + ] + ]); + } catch (\Exception $e) { + // 回滚事务 + Db::rollback(); + throw $e; + } + } catch (\Exception $e) { + return json([ + 'code' => 500, + 'msg' => '好友转移失败:' . $e->getMessage() + ]); + } + } +} \ No newline at end of file diff --git a/Server/application/devices/model/WechatAccount.php b/Server/application/devices/model/WechatAccount.php new file mode 100644 index 00000000..64fc8a64 --- /dev/null +++ b/Server/application/devices/model/WechatAccount.php @@ -0,0 +1,129 @@ + 'integer', + 'deviceAccountId' => 'integer', + 'keFuAlive' => 'integer', + 'deviceAlive' => 'integer', + 'wechatAlive' => 'integer', + 'yesterdayMsgCount' => 'integer', + 'sevenDayMsgCount' => 'integer', + 'thirtyDayMsgCount' => 'integer', + 'totalFriend' => 'integer', + 'maleFriend' => 'integer', + 'femaleFriend' => 'integer', + 'gender' => 'integer', + 'currentDeviceId' => 'integer', + 'isDeleted' => 'integer', + 'groupId' => 'integer' + ]; + + /** + * 获取在线微信账号数量 + * + * @param array $where 额外的查询条件 + * @return int 微信账号数量 + */ + public static function getOnlineWechatCount($where = []) + { + $condition = [ + 'deviceAlive' => 1, + 'wechatAlive' => 1, + 'isDeleted' => 0 + ]; + + // 合并额外条件 + if (!empty($where)) { + $condition = array_merge($condition, $where); + } + + return self::where($condition)->count(); + } + + /** + * 获取有登录微信的设备数量 + * + * @param array $where 额外的查询条件 + * @return int 设备数量 + */ + public static function getDeviceWithWechatCount($where = []) + { + $condition = [ + 'deviceAlive' => 1, + 'isDeleted' => 0 + ]; + + // 合并额外条件 + if (!empty($where)) { + $condition = array_merge($condition, $where); + } + + return self::where($condition)->count(); + } + + /** + * 获取在线微信账号列表 + * + * @param array $where 额外的查询条件 + * @param string $order 排序方式 + * @param int $page 页码 + * @param int $limit 每页数量 + * @return \think\Paginator 分页对象 + */ + public static function getOnlineWechatList($where = [], $order = 'id desc', $page = 1, $limit = 10) + { + $condition = [ + 'wechatAlive' => 1, + 'deviceAlive' => 1, + 'isDeleted' => 0 + ]; + + // 合并额外条件 + if (!empty($where)) { + $condition = array_merge($condition, $where); + } + + return self::where($condition) + ->field([ + 'id', + 'wechatId', + 'accountNickname', + 'nickname', + 'accountUserName', + 'avatar', + 'wechatAlive', + 'deviceAlive', + 'totalFriend', + 'maleFriend', + 'femaleFriend', + 'imei', + 'deviceMemo', + 'yesterdayMsgCount' + ]) + ->order($order) + ->paginate($limit, false, ['page' => $page]); + } +} \ No newline at end of file