diff --git a/Touchkebao/.env.development b/Touchkebao/.env.development new file mode 100644 index 00000000..2ddb67f5 --- /dev/null +++ b/Touchkebao/.env.development @@ -0,0 +1,6 @@ +# 基础环境变量示例 +VITE_API_BASE_URL=http://www.yishi.com +VITE_API_BASE_URL2=https://kf.quwanzhi.com:9991 +VITE_API_WS_URL=wss://kf.quwanzhi.com:9993 +# VITE_API_BASE_URL=https://ckbapi.quwanzhi.com +VITE_APP_TITLE=存客宝 diff --git a/Touchkebao/.env.production b/Touchkebao/.env.production new file mode 100644 index 00000000..838935bb --- /dev/null +++ b/Touchkebao/.env.production @@ -0,0 +1,6 @@ +# 基础环境变量示例 +VITE_API_BASE_URL=https://ckbapi.quwanzhi.com +VITE_API_BASE_URL2=https://kf.quwanzhi.com:9991 +VITE_API_WS_URL=wss://kf.quwanzhi.com:9993 +# VITE_API_BASE_URL=http://www.yishi.com +VITE_APP_TITLE=存客宝 diff --git a/Touchkebao/.eslintrc.js b/Touchkebao/.eslintrc.js new file mode 100644 index 00000000..0ffa2239 --- /dev/null +++ b/Touchkebao/.eslintrc.js @@ -0,0 +1,64 @@ +module.exports = { + root: true, + env: { + browser: true, + es2021: true, + node: true, + }, + extends: [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:react-hooks/recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended", // 这个配置会自动处理大部分冲突 + ], + parser: "@typescript-eslint/parser", + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 12, + sourceType: "module", + }, + plugins: ["react", "react-hooks", "@typescript-eslint", "prettier"], + rules: { + "prettier/prettier": "error", + "react/react-in-jsx-scope": "off", + "@typescript-eslint/no-unused-vars": "warn", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unnecessary-type-constraint": "warn", + "react/prop-types": "off", + "linebreak-style": "off", + "eol-last": "off", + "no-empty": "warn", + "prefer-const": "warn", + // 确保与 Prettier 完全兼容 + "comma-dangle": "off", + "comma-spacing": "off", + "comma-style": "off", + "object-curly-spacing": "off", + "array-bracket-spacing": "off", + indent: "off", + quotes: "off", + semi: "off", + "arrow-parens": "off", + "no-multiple-empty-lines": "off", + "max-len": "off", + "space-before-function-paren": "off", + "space-before-blocks": "off", + "keyword-spacing": "off", + "space-infix-ops": "off", + "space-in-parens": "off", + "space-in-brackets": "off", + "object-property-newline": "off", + "array-element-newline": "off", + "function-paren-newline": "off", + "object-curly-newline": "off", + "array-bracket-newline": "off", + }, + settings: { + react: { + version: "detect", + }, + }, +}; diff --git a/Touchkebao/.gitattributes b/Touchkebao/.gitattributes new file mode 100644 index 00000000..b1c74b26 --- /dev/null +++ b/Touchkebao/.gitattributes @@ -0,0 +1,27 @@ +# 设置默认行为,如果core.autocrlf没有设置,Git会自动处理行尾符 +* text=auto + +# 明确指定文本文件使用LF +*.js text eol=lf +*.jsx text eol=lf +*.ts text eol=lf +*.tsx text eol=lf +*.json text eol=lf +*.css text eol=lf +*.scss text eol=lf +*.html text eol=lf +*.md text eol=lf +*.yml text eol=lf +*.yaml text eol=lf + +# 二进制文件 +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.svg binary +*.woff binary +*.woff2 binary +*.ttf binary +*.eot binary diff --git a/Touchkebao/.gitignore b/Touchkebao/.gitignore new file mode 100644 index 00000000..dcf55aed --- /dev/null +++ b/Touchkebao/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +dist/ +build/ +yarn.lock +.env +.DS_Store \ No newline at end of file diff --git a/Touchkebao/.prettierrc b/Touchkebao/.prettierrc new file mode 100644 index 00000000..e8e77819 --- /dev/null +++ b/Touchkebao/.prettierrc @@ -0,0 +1,13 @@ +{ + "semi": true, + "trailingComma": "all", + "singleQuote": false, + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "endOfLine": "lf", + "bracketSpacing": true, + "arrowParens": "avoid", + "jsxSingleQuote": false, + "quoteProps": "as-needed" +} \ No newline at end of file diff --git a/Touchkebao/.vite/deps/_metadata.json b/Touchkebao/.vite/deps/_metadata.json new file mode 100644 index 00000000..b6601954 --- /dev/null +++ b/Touchkebao/.vite/deps/_metadata.json @@ -0,0 +1,8 @@ +{ + "hash": "efe0acf4", + "configHash": "2bed34b3", + "lockfileHash": "ef01d341", + "browserHash": "91bd3b2c", + "optimized": {}, + "chunks": {} +} \ No newline at end of file diff --git a/Touchkebao/.vite/deps/package.json b/Touchkebao/.vite/deps/package.json new file mode 100644 index 00000000..3dbc1ca5 --- /dev/null +++ b/Touchkebao/.vite/deps/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/Touchkebao/.vscode/extensions.json b/Touchkebao/.vscode/extensions.json new file mode 100644 index 00000000..37f481a2 --- /dev/null +++ b/Touchkebao/.vscode/extensions.json @@ -0,0 +1,11 @@ +{ + "recommendations": [ + "esbenp.prettier-vscode", + "dbaeumer.vscode-eslint", + "bradlc.vscode-tailwindcss", + "ms-vscode.vscode-typescript-next", + "formulahendry.auto-rename-tag", + "christian-kohler.path-intellisense", + "ms-vscode.vscode-json" + ] +} diff --git a/Touchkebao/.vscode/settings.json b/Touchkebao/.vscode/settings.json new file mode 100644 index 00000000..7241ea90 --- /dev/null +++ b/Touchkebao/.vscode/settings.json @@ -0,0 +1,45 @@ +{ + "files.eol": "\n", + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true, + "files.trimTrailingWhitespace": true, + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit", + "source.organizeImports": "never" + }, + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact" + ], + "eslint.format.enable": false, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[scss]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[css]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "typescript.preferences.importModuleSpecifier": "relative", + "typescript.suggest.autoImports": true, + "editor.tabSize": 2, + "editor.insertSpaces": true, + "editor.detectIndentation": false +} diff --git a/Touchkebao/devlop.py b/Touchkebao/devlop.py new file mode 100644 index 00000000..f1aecf46 --- /dev/null +++ b/Touchkebao/devlop.py @@ -0,0 +1,95 @@ +import os +import zipfile +import paramiko + +# 配置 +local_dir = './dist' # 本地要打包的目录 +zip_name = 'dist.zip' +# 上传到服务器的 zip 路径 +remote_path = '/www/wwwroot/auto-devlop/ckb-operation/dist.zip' # 服务器上的临时zip路径 +server_ip = '42.194.245.239' +server_port = 6523 +server_user = 'yongpxu' +server_pwd = 'Aa123456789.' +# 服务器 dist 相关目录 +remote_base_dir = '/www/wwwroot/auto-devlop/ckb-operation' +dist_dir = f'{remote_base_dir}/dist' +dist1_dir = f'{remote_base_dir}/dist1' +dist2_dir = f'{remote_base_dir}/dist2' + +# 美化输出用的函数 +from datetime import datetime + +def info(msg): + print(f"\033[36m[INFO {datetime.now().strftime('%H:%M:%S')}] {msg}\033[0m") + +def success(msg): + print(f"\033[32m[SUCCESS] {msg}\033[0m") + +def error(msg): + print(f"\033[31m[ERROR] {msg}\033[0m") + +def step(msg): + print(f"\n\033[35m==== {msg} ====" + "\033[0m") + +# 1. 先运行 pnpm build +step('Step 1: 构建项目 (pnpm build)') +info('开始执行 pnpm build...') +ret = os.system('pnpm build') +if ret != 0: + error('pnpm build 失败,终止部署!') + exit(1) +success('pnpm build 完成') + +# 2. 打包 +step('Step 2: 打包 dist 目录为 zip') +info('开始打包 dist 目录...') +with zipfile.ZipFile(zip_name, 'w', zipfile.ZIP_DEFLATED) as zipf: + for root, dirs, files in os.walk(local_dir): + for file in files: + filepath = os.path.join(root, file) + arcname = os.path.relpath(filepath, local_dir) + zipf.write(filepath, arcname) +success('本地打包完成') + +# 3. 上传 +step('Step 3: 上传 zip 包到服务器') +info('开始上传 zip 包...') +transport = paramiko.Transport((server_ip, server_port)) +transport.connect(username=server_user, password=server_pwd) +sftp = paramiko.SFTPClient.from_transport(transport) +sftp.put(zip_name, remote_path) +sftp.close() +transport.close() +success('上传到服务器完成') + +# 删除本地 dist.zip +try: + os.remove(zip_name) + success('本地 dist.zip 已删除') +except Exception as e: + error(f'本地 dist.zip 删除失败: {e}') + +# 4. 远程解压并覆盖 +step('Step 4: 服务器端解压、切换目录') +ssh = paramiko.SSHClient() +ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +ssh.connect(server_ip, server_port, server_user, server_pwd) +commands = [ + f'unzip -oq {remote_path} -d {dist2_dir}', # 静默解压 + f'rm {remote_path}', + f'if [ -d {dist_dir} ]; then mv {dist_dir} {dist1_dir}; fi', + f'mv {dist2_dir} {dist_dir}', + f'rm -rf {dist1_dir}' +] +for i, cmd in enumerate(commands, 1): + info(f'执行第{i}步: {cmd}') + stdin, stdout, stderr = ssh.exec_command(cmd) + out, err = stdout.read().decode(), stderr.read().decode() + # 只打印非 unzip 命令的输出 + if i != 1 and out.strip(): + print(out.strip()) + if err.strip(): + error(err.strip()) +ssh.close() +success('服务器解压并覆盖完成,部署成功!') diff --git a/Touchkebao/favicon.ico b/Touchkebao/favicon.ico new file mode 100644 index 00000000..54e9891c Binary files /dev/null and b/Touchkebao/favicon.ico differ diff --git a/Touchkebao/index.html b/Touchkebao/index.html new file mode 100644 index 00000000..16de6c67 --- /dev/null +++ b/Touchkebao/index.html @@ -0,0 +1,19 @@ + + + + + + 存客宝 + + + + + +
+ + + diff --git a/Touchkebao/package.json b/Touchkebao/package.json new file mode 100644 index 00000000..0277589c --- /dev/null +++ b/Touchkebao/package.json @@ -0,0 +1,53 @@ +{ + "name": "cunkebao", + "version": "3.0.0", + "license": "MIT", + "private": true, + "dependencies": { + "@ant-design/icons": "^5.6.1", + "antd": "^5.13.1", + "antd-mobile": "^5.39.1", + "antd-mobile-icons": "^0.3.0", + "axios": "^1.6.7", + "dayjs": "^1.11.13", + "dexie": "^4.2.0", + "echarts": "^5.6.0", + "echarts-for-react": "^3.0.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.20.0", + "react-window": "^1.8.11", + "vconsole": "^3.15.1", + "xmldom": "^0.6.0", + "zustand": "^5.0.6" + }, + "devDependencies": { + "@types/node": "^24.0.14", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@typescript-eslint/eslint-plugin": "^7.7.0", + "@typescript-eslint/parser": "^7.7.0", + "@vitejs/plugin-react": "^4.6.0", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-react": "^7.34.1", + "eslint-plugin-react-hooks": "^5.2.0", + "postcss": "^8.4.38", + "postcss-pxtorem": "^6.0.0", + "prettier": "^3.2.5", + "sass": "^1.75.0", + "typescript": "^5.4.5", + "vite": "^7.0.5" + }, + "scripts": { + "dev": "pnpm vite", + "build": "pnpm vite build", + "build:check": "tsc && pnpm vite build", + "preview": "pnpm vite preview", + "lint": "eslint src --ext .js,.jsx,.ts,.tsx --fix", + "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,scss,css}\"", + "lint:check": "eslint src --ext .js,.jsx,.ts,.tsx", + "format:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx,json,scss,css}\"" + } +} diff --git a/Touchkebao/pnpm-lock.yaml b/Touchkebao/pnpm-lock.yaml new file mode 100644 index 00000000..818ed7d7 --- /dev/null +++ b/Touchkebao/pnpm-lock.yaml @@ -0,0 +1,4998 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@ant-design/icons': + specifier: ^5.6.1 + version: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + antd: + specifier: ^5.13.1 + version: 5.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + antd-mobile: + specifier: ^5.39.1 + version: 5.40.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + antd-mobile-icons: + specifier: ^0.3.0 + version: 0.3.0 + axios: + specifier: ^1.6.7 + version: 1.11.0 + dayjs: + specifier: ^1.11.13 + version: 1.11.13 + dexie: + specifier: ^4.2.0 + version: 4.2.0 + echarts: + specifier: ^5.6.0 + version: 5.6.0 + echarts-for-react: + specifier: ^3.0.2 + version: 3.0.2(echarts@5.6.0)(react@18.3.1) + react: + specifier: ^18.2.0 + version: 18.3.1 + react-dom: + specifier: ^18.2.0 + version: 18.3.1(react@18.3.1) + react-router-dom: + specifier: ^6.20.0 + version: 6.30.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-window: + specifier: ^1.8.11 + version: 1.8.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + vconsole: + specifier: ^3.15.1 + version: 3.15.1 + xmldom: + specifier: ^0.6.0 + version: 0.6.0 + zustand: + specifier: ^5.0.6 + version: 5.0.7(@types/react@19.1.10)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1)) + devDependencies: + '@types/node': + specifier: ^24.0.14 + version: 24.2.1 + '@types/react': + specifier: ^19.1.8 + version: 19.1.10 + '@types/react-dom': + specifier: ^19.1.6 + version: 19.1.7(@types/react@19.1.10) + '@typescript-eslint/eslint-plugin': + specifier: ^7.7.0 + version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.9.2))(eslint@8.57.1)(typescript@5.9.2) + '@typescript-eslint/parser': + specifier: ^7.7.0 + version: 7.18.0(eslint@8.57.1)(typescript@5.9.2) + '@vitejs/plugin-react': + specifier: ^4.6.0 + version: 4.7.0(vite@7.1.2(@types/node@24.2.1)(sass@1.90.0)) + eslint: + specifier: ^8.57.0 + version: 8.57.1 + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.2(eslint@8.57.1) + eslint-plugin-prettier: + specifier: ^5.1.3 + version: 5.5.4(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint@8.57.1)(prettier@3.6.2) + eslint-plugin-react: + specifier: ^7.34.1 + version: 7.37.5(eslint@8.57.1) + eslint-plugin-react-hooks: + specifier: ^5.2.0 + version: 5.2.0(eslint@8.57.1) + postcss: + specifier: ^8.4.38 + version: 8.5.6 + postcss-pxtorem: + specifier: ^6.0.0 + version: 6.1.0(postcss@8.5.6) + prettier: + specifier: ^3.2.5 + version: 3.6.2 + sass: + specifier: ^1.75.0 + version: 1.90.0 + typescript: + specifier: ^5.4.5 + version: 5.9.2 + vite: + specifier: ^7.0.5 + version: 7.1.2(@types/node@24.2.1)(sass@1.90.0) + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@ant-design/colors@7.2.1': + resolution: {integrity: sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==} + + '@ant-design/cssinjs-utils@1.1.3': + resolution: {integrity: sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@ant-design/cssinjs@1.24.0': + resolution: {integrity: sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@ant-design/fast-color@2.0.6': + resolution: {integrity: sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==} + engines: {node: '>=8.x'} + + '@ant-design/icons-svg@4.4.2': + resolution: {integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==} + + '@ant-design/icons@5.6.1': + resolution: {integrity: sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==} + engines: {node: '>=8'} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@ant-design/react-slick@1.1.2': + resolution: {integrity: sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==} + peerDependencies: + react: '>=16.9.0' + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.0': + resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.0': + resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.0': + resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.27.3': + resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.2': + resolution: {integrity: sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.28.2': + resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.0': + resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.2': + resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} + engines: {node: '>=6.9.0'} + + '@emotion/hash@0.8.0': + resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} + + '@emotion/unitless@0.7.5': + resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} + + '@esbuild/aix-ppc64@0.25.8': + resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.8': + resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.8': + resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.8': + resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.8': + resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.8': + resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.8': + resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.8': + resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.8': + resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.8': + resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.8': + resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.8': + resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.8': + resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.8': + resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.8': + resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.8': + resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.8': + resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.8': + resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.8': + resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.8': + resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.8': + resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.8': + resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.8': + resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.8': + resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.8': + resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.8': + resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.3': + resolution: {integrity: sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==} + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.30': + resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} + + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@rc-component/async-validator@5.0.4': + resolution: {integrity: sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==} + engines: {node: '>=14.x'} + + '@rc-component/color-picker@2.0.1': + resolution: {integrity: sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/context@1.4.0': + resolution: {integrity: sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/mini-decimal@1.1.0': + resolution: {integrity: sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==} + engines: {node: '>=8.x'} + + '@rc-component/mutate-observer@1.1.0': + resolution: {integrity: sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/portal@1.1.2': + resolution: {integrity: sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/qrcode@1.0.0': + resolution: {integrity: sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/tour@1.15.1': + resolution: {integrity: sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/trigger@2.3.0': + resolution: {integrity: sha512-iwaxZyzOuK0D7lS+0AQEtW52zUWxoGqTGkke3dRyb8pYiShmRpCjB/8TzPI4R6YySCH7Vm9BZj/31VPiiQTLBg==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@react-spring/animated@9.6.1': + resolution: {integrity: sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@react-spring/core@9.6.1': + resolution: {integrity: sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@react-spring/rafz@9.6.1': + resolution: {integrity: sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ==} + + '@react-spring/shared@9.6.1': + resolution: {integrity: sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@react-spring/types@9.6.1': + resolution: {integrity: sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q==} + + '@react-spring/web@9.6.1': + resolution: {integrity: sha512-X2zR6q2Z+FjsWfGAmAXlQaoUHbPmfuCaXpuM6TcwXPpLE1ZD4A1eys/wpXboFQmDkjnrlTmKvpVna1MjWpZ5Hw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@remix-run/router@1.23.0': + resolution: {integrity: sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==} + engines: {node: '>=14.0.0'} + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/rollup-android-arm-eabi@4.46.2': + resolution: {integrity: sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.46.2': + resolution: {integrity: sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.46.2': + resolution: {integrity: sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.46.2': + resolution: {integrity: sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.46.2': + resolution: {integrity: sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.46.2': + resolution: {integrity: sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.46.2': + resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.46.2': + resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.46.2': + resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.46.2': + resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.46.2': + resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.46.2': + resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.46.2': + resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.46.2': + resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.46.2': + resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.46.2': + resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.46.2': + resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.46.2': + resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.46.2': + resolution: {integrity: sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.46.2': + resolution: {integrity: sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==} + cpu: [x64] + os: [win32] + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/node@24.2.1': + resolution: {integrity: sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==} + + '@types/react-dom@19.1.7': + resolution: {integrity: sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==} + peerDependencies: + '@types/react': ^19.0.0 + + '@types/react@19.1.10': + resolution: {integrity: sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==} + + '@typescript-eslint/eslint-plugin@7.18.0': + resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@7.18.0': + resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@7.18.0': + resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/type-utils@7.18.0': + resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@7.18.0': + resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/typescript-estree@7.18.0': + resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@7.18.0': + resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + + '@typescript-eslint/visitor-keys@7.18.0': + resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@use-gesture/core@10.3.0': + resolution: {integrity: sha512-rh+6MND31zfHcy9VU3dOZCqGY511lvGcfyJenN4cWZe0u1BH6brBpBddLVXhF2r4BMqWbvxfsbL7D287thJU2A==} + + '@use-gesture/react@10.3.0': + resolution: {integrity: sha512-3zc+Ve99z4usVP6l9knYVbVnZgfqhKah7sIG+PS2w+vpig2v2OLct05vs+ZXMzwxdNCMka8B+8WlOo0z6Pn6DA==} + peerDependencies: + react: '>= 16.8.0' + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ahooks@3.9.0: + resolution: {integrity: sha512-r20/C38aFyU3Zqp3620gkdLnxmQhnmWORB3eGGTDlM4i/fOc0GUvM+f2oleMzEu7b3+pHXyzz+FB6ojxsUdYdw==} + engines: {node: '>=8.0.0'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + antd-mobile-icons@0.3.0: + resolution: {integrity: sha512-rqINQpJWZWrva9moCd1Ye695MZYWmqLPE+bY8d2xLRy7iSQwPsinCdZYjpUPp2zL/LnKYSyXxP2ut2A+DC+whQ==} + + antd-mobile-v5-count@1.0.1: + resolution: {integrity: sha512-YGsiEDCPUDz3SzfXi6gLZn/HpeSMW+jgPc4qiYUr1fSopg3hkUie2TnooJdExgfiETHefH3Ggs58He0OVfegLA==} + + antd-mobile@5.40.0: + resolution: {integrity: sha512-nNfkTLiYPsa7A2i7eoG/hr2BM0agR4cfugE2+5HTyGnCg5xQT04Pmt9qEoKv7MOW5BaiiMyjO462Kh8KRF5QBA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + + antd@5.27.0: + resolution: {integrity: sha512-o54dmpooLOc08RSGCkeEQBYAGPxUSmnhmYJKCNTHH46vzjOVxdteu+wPTRVkRbAkDTbs2VcNr5VL7Lu67rPIiA==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + async-validator@4.2.5: + resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axios@1.11.0: + resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.25.2: + resolution: {integrity: sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001734: + resolution: {integrity: sha512-uhE1Ye5vgqju6OI71HTQqcBCZrvHugk0MjLak7Q+HfoBgoq5Bi+5YnwjP4fjDgrtYr/l8MVRBvzz9dPD4KyK0A==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + compute-scroll-into-view@3.1.1: + resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + copy-text-to-clipboard@3.2.0: + resolution: {integrity: sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==} + engines: {node: '>=12'} + + copy-to-clipboard@3.3.3: + resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} + + core-js@3.45.0: + resolution: {integrity: sha512-c2KZL9lP4DjkN3hk/an4pWn5b5ZefhRJnAc42n6LJ19kSnbeRbdQZE5dSeE2LBol1OwJD3X1BQvFTAsa8ReeDA==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + + dexie@4.2.0: + resolution: {integrity: sha512-OSeyyWOUetDy9oFWeddJgi83OnRA3hSFh3RrbltmPgqHszE9f24eUCVLI4mPg0ifsWk0lQTdnS+jyGNrPMvhDA==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + echarts-for-react@3.0.2: + resolution: {integrity: sha512-DRwIiTzx8JfwPOVgGttDytBqdp5VzCSyMRIxubgU/g2n9y3VLUmF2FK7Icmg/sNVkv4+rktmrLN9w22U2yy3fA==} + peerDependencies: + echarts: ^3.0.0 || ^4.0.0 || ^5.0.0 + react: ^15.0.0 || >=16.0.0 + + echarts@5.6.0: + resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==} + + electron-to-chromium@1.5.200: + resolution: {integrity: sha512-rFCxROw7aOe4uPTfIAx+rXv9cEcGx+buAF4npnhtTqCJk5KDFRnh3+KYj7rdVh6lsFt5/aPs+Irj9rZ33WMA7w==} + + es-abstract@1.24.0: + resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.2.1: + resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + esbuild@0.25.8: + resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-prettier@9.1.2: + resolution: {integrity: sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-prettier@5.5.4: + resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + immutable@5.1.3: + resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + intersection-observer@0.12.2: + resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-generator-function@1.1.0: + resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json2mq@0.2.0: + resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + memoize-one@5.2.1: + resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mutation-observer@1.0.3: + resolution: {integrity: sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA==} + + nano-memoize@3.0.16: + resolution: {integrity: sha512-JyK96AKVGAwVeMj3MoMhaSXaUNqgMbCRSQB3trUV8tYZfWEzqUBKdK1qJpfuNXgKeHOx1jv/IEYTM659ly7zUA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss-pxtorem@6.1.0: + resolution: {integrity: sha512-ROODSNci9ADal3zUcPHOF/K83TiCgNSPXQFSbwyPHNV8ioHIE4SaC+FPOufd8jsr5jV2uIz29v1Uqy1c4ov42g==} + peerDependencies: + postcss: ^8.0.0 + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + rc-cascader@3.34.0: + resolution: {integrity: sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-checkbox@3.5.0: + resolution: {integrity: sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-collapse@3.9.0: + resolution: {integrity: sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-dialog@9.6.0: + resolution: {integrity: sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-drawer@7.3.0: + resolution: {integrity: sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-dropdown@4.2.1: + resolution: {integrity: sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA==} + peerDependencies: + react: '>=16.11.0' + react-dom: '>=16.11.0' + + rc-field-form@1.44.0: + resolution: {integrity: sha512-el7w87fyDUsca63Y/s8qJcq9kNkf/J5h+iTdqG5WsSHLH0e6Usl7QuYSmSVzJMgtp40mOVZIY/W/QP9zwrp1FA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-field-form@2.7.0: + resolution: {integrity: sha512-hgKsCay2taxzVnBPZl+1n4ZondsV78G++XVsMIJCAoioMjlMQR9YwAp7JZDIECzIu2Z66R+f4SFIRrO2DjDNAA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-image@7.12.0: + resolution: {integrity: sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-input-number@9.5.0: + resolution: {integrity: sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-input@1.8.0: + resolution: {integrity: sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + rc-mentions@2.20.0: + resolution: {integrity: sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-menu@9.16.1: + resolution: {integrity: sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-motion@2.9.5: + resolution: {integrity: sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-notification@5.6.4: + resolution: {integrity: sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-overflow@1.4.1: + resolution: {integrity: sha512-3MoPQQPV1uKyOMVNd6SZfONi+f3st0r8PksexIdBTeIYbMX0Jr+k7pHEDvsXtR4BpCv90/Pv2MovVNhktKrwvw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-pagination@5.1.0: + resolution: {integrity: sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-picker@4.11.3: + resolution: {integrity: sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg==} + engines: {node: '>=8.x'} + peerDependencies: + date-fns: '>= 2.x' + dayjs: '>= 1.x' + luxon: '>= 3.x' + moment: '>= 2.x' + react: '>=16.9.0' + react-dom: '>=16.9.0' + peerDependenciesMeta: + date-fns: + optional: true + dayjs: + optional: true + luxon: + optional: true + moment: + optional: true + + rc-progress@4.0.0: + resolution: {integrity: sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-rate@2.13.1: + resolution: {integrity: sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-resize-observer@1.4.3: + resolution: {integrity: sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-segmented@2.4.1: + resolution: {integrity: sha512-KUi+JJFdKnumV9iXlm+BJ00O4NdVBp2TEexLCk6bK1x/RH83TvYKQMzIz/7m3UTRPD08RM/8VG/JNjWgWbd4cw==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + rc-segmented@2.7.0: + resolution: {integrity: sha512-liijAjXz+KnTRVnxxXG2sYDGd6iLL7VpGGdR8gwoxAXy2KglviKCxLWZdjKYJzYzGSUwKDSTdYk8brj54Bn5BA==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + rc-select@14.16.8: + resolution: {integrity: sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==} + engines: {node: '>=8.x'} + peerDependencies: + react: '*' + react-dom: '*' + + rc-slider@11.1.8: + resolution: {integrity: sha512-2gg/72YFSpKP+Ja5AjC5DPL1YnV8DEITDQrcc1eASrUYjl0esptaBVJBh5nLTXCCp15eD8EuGjwezVGSHhs9tQ==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-steps@6.0.1: + resolution: {integrity: sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-switch@4.1.0: + resolution: {integrity: sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-table@7.51.1: + resolution: {integrity: sha512-5iq15mTHhvC42TlBLRCoCBLoCmGlbRZAlyF21FonFnS/DIC8DeRqnmdyVREwt2CFbPceM0zSNdEeVfiGaqYsKw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-tabs@15.7.0: + resolution: {integrity: sha512-ZepiE+6fmozYdWf/9gVp7k56PKHB1YYoDsKeQA1CBlJ/POIhjkcYiv0AGP0w2Jhzftd3AVvZP/K+V+Lpi2ankA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-textarea@1.10.2: + resolution: {integrity: sha512-HfaeXiaSlpiSp0I/pvWpecFEHpVysZ9tpDLNkxQbMvMz6gsr7aVZ7FpWP9kt4t7DB+jJXesYS0us1uPZnlRnwQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-tooltip@6.4.0: + resolution: {integrity: sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-tree-select@5.27.0: + resolution: {integrity: sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww==} + peerDependencies: + react: '*' + react-dom: '*' + + rc-tree@5.13.1: + resolution: {integrity: sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A==} + engines: {node: '>=10.x'} + peerDependencies: + react: '*' + react-dom: '*' + + rc-upload@4.9.2: + resolution: {integrity: sha512-nHx+9rbd1FKMiMRYsqQ3NkXUv7COHPBo3X1Obwq9SWS6/diF/A0aJ5OHubvwUAIDs+4RMleljV0pcrNUc823GQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-util@5.44.4: + resolution: {integrity: sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + rc-virtual-list@3.19.1: + resolution: {integrity: sha512-DCapO2oyPqmooGhxBuXHM4lFuX+sshQwWqqkuyFA+4rShLe//+GEPVwiDgO+jKtKHtbeYwZoNvetwfHdOf+iUQ==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react-router-dom@6.30.1: + resolution: {integrity: sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + react-router@6.30.1: + resolution: {integrity: sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + + react-window@1.8.11: + resolution: {integrity: sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ==} + engines: {node: '>8.0.0'} + peerDependencies: + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rollup@4.46.2: + resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + runes2@1.1.4: + resolution: {integrity: sha512-LNPnEDPOOU4ehF71m5JoQyzT2yxwD6ZreFJ7MxZUAoMKNMY1XrAo60H1CUoX5ncSm0rIuKlqn9JZNRrRkNou2g==} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + sass@1.90.0: + resolution: {integrity: sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==} + engines: {node: '>=14.0.0'} + hasBin: true + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + screenfull@5.2.0: + resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} + engines: {node: '>=0.10.0'} + + scroll-into-view-if-needed@3.1.0: + resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + size-sensor@1.0.2: + resolution: {integrity: sha512-2NCmWxY7A9pYKGXNBfteo4hy14gWu47rg5692peVMst6lQLPKrVjhY+UTEsPI5ceFRJSl3gVgMYaUi/hKuaiKw==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + staged-components@1.1.3: + resolution: {integrity: sha512-9EIswzDqjwlEu+ymkV09TTlJfzSbKgEnNteUnZSTxkpMgr5Wx2CzzA9WcMFWBNCldqVPsHVnRGGrApduq2Se5A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string-convert@0.2.1: + resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + stylis@4.3.6: + resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + synckit@0.11.11: + resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} + engines: {node: ^14.18.0 || >=16.0.0} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + throttle-debounce@5.0.2: + resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} + engines: {node: '>=12.22'} + + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toggle-selection@1.0.6: + resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + + ts-api-utils@1.4.3: + resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + tslib@2.3.0: + resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript@5.9.2: + resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@7.10.0: + resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-sync-external-store@1.5.0: + resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + vconsole@3.15.1: + resolution: {integrity: sha512-KH8XLdrq9T5YHJO/ixrjivHfmF2PC2CdVoK6RWZB4yftMykYIaXY1mxZYAic70vADM54kpMQF+dYmvl5NRNy1g==} + + vite@7.1.2: + resolution: {integrity: sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + xmldom@0.6.0: + resolution: {integrity: sha512-iAcin401y58LckRZ0TkI4k0VSM1Qg0KGSc3i8rU+xrxe19A/BN1zHyVSJY7uoutVlaTSzYyk/v5AmkewAP7jtg==} + engines: {node: '>=10.0.0'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zrender@5.6.1: + resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==} + + zustand@5.0.7: + resolution: {integrity: sha512-Ot6uqHDW/O2VdYsKLLU8GQu8sCOM1LcoE8RwvLv9uuRT9s6SOHCKs0ZEOhxg+I1Ld+A1Q5lwx+UlKXXUoCZITg==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + + '@ant-design/colors@7.2.1': + dependencies: + '@ant-design/fast-color': 2.0.6 + + '@ant-design/cssinjs-utils@1.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@ant-design/cssinjs': 1.24.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@babel/runtime': 7.28.2 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@ant-design/cssinjs@1.24.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.2 + '@emotion/hash': 0.8.0 + '@emotion/unitless': 0.7.5 + classnames: 2.5.1 + csstype: 3.1.3 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + stylis: 4.3.6 + + '@ant-design/fast-color@2.0.6': + dependencies: + '@babel/runtime': 7.28.2 + + '@ant-design/icons-svg@4.4.2': {} + + '@ant-design/icons@5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@ant-design/colors': 7.2.1 + '@ant-design/icons-svg': 4.4.2 + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@ant-design/react-slick@1.1.2(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + json2mq: 0.2.0 + react: 18.3.1 + resize-observer-polyfill: 1.5.1 + throttle-debounce: 5.0.2 + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.0': {} + + '@babel/core@7.28.0': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.0 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) + '@babel/helpers': 7.28.2 + '@babel/parser': 7.28.0 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.2 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.0': + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.25.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.27.3(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.2': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.2 + + '@babel/parser@7.28.0': + dependencies: + '@babel/types': 7.28.2 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/runtime@7.28.2': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 + + '@babel/traverse@7.28.0': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.0 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.0 + '@babel/template': 7.27.2 + '@babel/types': 7.28.2 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.2': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@emotion/hash@0.8.0': {} + + '@emotion/unitless@0.7.5': {} + + '@esbuild/aix-ppc64@0.25.8': + optional: true + + '@esbuild/android-arm64@0.25.8': + optional: true + + '@esbuild/android-arm@0.25.8': + optional: true + + '@esbuild/android-x64@0.25.8': + optional: true + + '@esbuild/darwin-arm64@0.25.8': + optional: true + + '@esbuild/darwin-x64@0.25.8': + optional: true + + '@esbuild/freebsd-arm64@0.25.8': + optional: true + + '@esbuild/freebsd-x64@0.25.8': + optional: true + + '@esbuild/linux-arm64@0.25.8': + optional: true + + '@esbuild/linux-arm@0.25.8': + optional: true + + '@esbuild/linux-ia32@0.25.8': + optional: true + + '@esbuild/linux-loong64@0.25.8': + optional: true + + '@esbuild/linux-mips64el@0.25.8': + optional: true + + '@esbuild/linux-ppc64@0.25.8': + optional: true + + '@esbuild/linux-riscv64@0.25.8': + optional: true + + '@esbuild/linux-s390x@0.25.8': + optional: true + + '@esbuild/linux-x64@0.25.8': + optional: true + + '@esbuild/netbsd-arm64@0.25.8': + optional: true + + '@esbuild/netbsd-x64@0.25.8': + optional: true + + '@esbuild/openbsd-arm64@0.25.8': + optional: true + + '@esbuild/openbsd-x64@0.25.8': + optional: true + + '@esbuild/openharmony-arm64@0.25.8': + optional: true + + '@esbuild/sunos-x64@0.25.8': + optional: true + + '@esbuild/win32-arm64@0.25.8': + optional: true + + '@esbuild/win32-ia32@0.25.8': + optional: true + + '@esbuild/win32-x64@0.25.8': + optional: true + + '@eslint-community/eslint-utils@4.7.0(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.12.6 + debug: 4.4.1 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.1': {} + + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.3': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/utils@0.2.10': {} + + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.4.1 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.30 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.30': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true + + '@parcel/watcher-win32-arm64@2.5.1': + optional: true + + '@parcel/watcher-win32-ia32@2.5.1': + optional: true + + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + optional: true + + '@pkgr/core@0.2.9': {} + + '@rc-component/async-validator@5.0.4': + dependencies: + '@babel/runtime': 7.28.2 + + '@rc-component/color-picker@2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@ant-design/fast-color': 2.0.6 + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/context@1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.2 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/mini-decimal@1.1.0': + dependencies: + '@babel/runtime': 7.28.2 + + '@rc-component/mutate-observer@1.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/portal@1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/qrcode@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/tour@1.15.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.2 + '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/trigger': 2.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@rc-component/trigger@2.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.2 + '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@react-spring/animated@9.6.1(react@18.3.1)': + dependencies: + '@react-spring/shared': 9.6.1(react@18.3.1) + '@react-spring/types': 9.6.1 + react: 18.3.1 + + '@react-spring/core@9.6.1(react@18.3.1)': + dependencies: + '@react-spring/animated': 9.6.1(react@18.3.1) + '@react-spring/rafz': 9.6.1 + '@react-spring/shared': 9.6.1(react@18.3.1) + '@react-spring/types': 9.6.1 + react: 18.3.1 + + '@react-spring/rafz@9.6.1': {} + + '@react-spring/shared@9.6.1(react@18.3.1)': + dependencies: + '@react-spring/rafz': 9.6.1 + '@react-spring/types': 9.6.1 + react: 18.3.1 + + '@react-spring/types@9.6.1': {} + + '@react-spring/web@9.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@react-spring/animated': 9.6.1(react@18.3.1) + '@react-spring/core': 9.6.1(react@18.3.1) + '@react-spring/shared': 9.6.1(react@18.3.1) + '@react-spring/types': 9.6.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@remix-run/router@1.23.0': {} + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/rollup-android-arm-eabi@4.46.2': + optional: true + + '@rollup/rollup-android-arm64@4.46.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.46.2': + optional: true + + '@rollup/rollup-darwin-x64@4.46.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.46.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.46.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.46.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.46.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.46.2': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.46.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.46.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.46.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.46.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.46.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.46.2': + optional: true + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.28.2 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.28.2 + + '@types/estree@1.0.8': {} + + '@types/node@24.2.1': + dependencies: + undici-types: 7.10.0 + + '@types/react-dom@19.1.7(@types/react@19.1.10)': + dependencies: + '@types/react': 19.1.10 + + '@types/react@19.1.10': + dependencies: + csstype: 3.1.3 + + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.9.2))(eslint@8.57.1)(typescript@5.9.2)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.9.2) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/type-utils': 7.18.0(eslint@8.57.1)(typescript@5.9.2) + '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 7.18.0 + eslint: 8.57.1 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 1.4.3(typescript@5.9.2) + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.9.2)': + dependencies: + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.4.1 + eslint: 8.57.1 + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@7.18.0': + dependencies: + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + + '@typescript-eslint/type-utils@7.18.0(eslint@8.57.1)(typescript@5.9.2)': + dependencies: + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.9.2) + '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.9.2) + debug: 4.4.1 + eslint: 8.57.1 + ts-api-utils: 1.4.3(typescript@5.9.2) + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@7.18.0': {} + + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.9.2)': + dependencies: + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.4.1 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 1.4.3(typescript@5.9.2) + optionalDependencies: + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@7.18.0(eslint@8.57.1)(typescript@5.9.2)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.9.2) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@7.18.0': + dependencies: + '@typescript-eslint/types': 7.18.0 + eslint-visitor-keys: 3.4.3 + + '@ungap/structured-clone@1.3.0': {} + + '@use-gesture/core@10.3.0': {} + + '@use-gesture/react@10.3.0(react@18.3.1)': + dependencies: + '@use-gesture/core': 10.3.0 + react: 18.3.1 + + '@vitejs/plugin-react@4.7.0(vite@7.1.2(@types/node@24.2.1)(sass@1.90.0))': + dependencies: + '@babel/core': 7.28.0 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.0) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 7.1.2(@types/node@24.2.1)(sass@1.90.0) + transitivePeerDependencies: + - supports-color + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ahooks@3.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + dayjs: 1.11.13 + intersection-observer: 0.12.2 + js-cookie: 3.0.5 + lodash: 4.17.21 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-fast-compare: 3.2.2 + resize-observer-polyfill: 1.5.1 + screenfull: 5.2.0 + tslib: 2.8.1 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + antd-mobile-icons@0.3.0: {} + + antd-mobile-v5-count@1.0.1: {} + + antd-mobile@5.40.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@floating-ui/dom': 1.7.3 + '@rc-component/mini-decimal': 1.1.0 + '@react-spring/web': 9.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@use-gesture/react': 10.3.0(react@18.3.1) + ahooks: 3.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + antd-mobile-icons: 0.3.0 + antd-mobile-v5-count: 1.0.1 + classnames: 2.5.1 + dayjs: 1.11.13 + deepmerge: 4.3.1 + nano-memoize: 3.0.16 + rc-field-form: 1.44.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-segmented: 2.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-fast-compare: 3.2.2 + react-is: 18.3.1 + runes2: 1.1.4 + staged-components: 1.1.3(react@18.3.1) + tslib: 2.8.1 + use-sync-external-store: 1.5.0(react@18.3.1) + + antd@5.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@ant-design/colors': 7.2.1 + '@ant-design/cssinjs': 1.24.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ant-design/cssinjs-utils': 1.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ant-design/fast-color': 2.0.6 + '@ant-design/icons': 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ant-design/react-slick': 1.1.2(react@18.3.1) + '@babel/runtime': 7.28.2 + '@rc-component/color-picker': 2.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/mutate-observer': 1.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/qrcode': 1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/tour': 1.15.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@rc-component/trigger': 2.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + copy-to-clipboard: 3.3.3 + dayjs: 1.11.13 + rc-cascader: 3.34.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-checkbox: 3.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-collapse: 3.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-dialog: 9.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-drawer: 7.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-dropdown: 4.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-field-form: 2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-image: 7.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-input: 1.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-input-number: 9.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-mentions: 2.20.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-menu: 9.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-notification: 5.6.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-pagination: 5.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-picker: 4.11.3(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-progress: 4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-rate: 2.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-segmented: 2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-select: 14.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-slider: 11.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-steps: 6.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-switch: 4.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-table: 7.51.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tabs: 15.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-textarea: 1.10.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tooltip: 6.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tree: 5.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tree-select: 5.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-upload: 4.9.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + scroll-into-view-if-needed: 3.1.0 + throttle-debounce: 5.0.2 + transitivePeerDependencies: + - date-fns + - luxon + - moment + + argparse@2.0.1: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array-union@2.1.0: {} + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + async-function@1.0.0: {} + + async-validator@4.2.5: {} + + asynckit@0.4.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axios@1.11.0: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.4 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.25.2: + dependencies: + caniuse-lite: 1.0.30001734 + electron-to-chromium: 1.5.200 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.2) + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001734: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + classnames@2.5.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + compute-scroll-into-view@3.1.1: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + copy-text-to-clipboard@3.2.0: {} + + copy-to-clipboard@3.3.3: + dependencies: + toggle-selection: 1.0.6 + + core-js@3.45.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.1.3: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + dayjs@1.11.13: {} + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + delayed-stream@1.0.0: {} + + detect-libc@1.0.3: + optional: true + + dexie@4.2.0: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + echarts-for-react@3.0.2(echarts@5.6.0)(react@18.3.1): + dependencies: + echarts: 5.6.0 + fast-deep-equal: 3.1.3 + react: 18.3.1 + size-sensor: 1.0.2 + + echarts@5.6.0: + dependencies: + tslib: 2.3.0 + zrender: 5.6.1 + + electron-to-chromium@1.5.200: {} + + es-abstract@1.24.0: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + safe-array-concat: 1.1.3 + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.25.8: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.8 + '@esbuild/android-arm': 0.25.8 + '@esbuild/android-arm64': 0.25.8 + '@esbuild/android-x64': 0.25.8 + '@esbuild/darwin-arm64': 0.25.8 + '@esbuild/darwin-x64': 0.25.8 + '@esbuild/freebsd-arm64': 0.25.8 + '@esbuild/freebsd-x64': 0.25.8 + '@esbuild/linux-arm': 0.25.8 + '@esbuild/linux-arm64': 0.25.8 + '@esbuild/linux-ia32': 0.25.8 + '@esbuild/linux-loong64': 0.25.8 + '@esbuild/linux-mips64el': 0.25.8 + '@esbuild/linux-ppc64': 0.25.8 + '@esbuild/linux-riscv64': 0.25.8 + '@esbuild/linux-s390x': 0.25.8 + '@esbuild/linux-x64': 0.25.8 + '@esbuild/netbsd-arm64': 0.25.8 + '@esbuild/netbsd-x64': 0.25.8 + '@esbuild/openbsd-arm64': 0.25.8 + '@esbuild/openbsd-x64': 0.25.8 + '@esbuild/openharmony-arm64': 0.25.8 + '@esbuild/sunos-x64': 0.25.8 + '@esbuild/win32-arm64': 0.25.8 + '@esbuild/win32-ia32': 0.25.8 + '@esbuild/win32-x64': 0.25.8 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@9.1.2(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-plugin-prettier@5.5.4(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint@8.57.1)(prettier@3.6.2): + dependencies: + eslint: 8.57.1 + prettier: 3.6.2 + prettier-linter-helpers: 1.0.0 + synckit: 0.11.11 + optionalDependencies: + eslint-config-prettier: 9.1.2(eslint@8.57.1) + + eslint-plugin-react-hooks@5.2.0(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-plugin-react@7.37.5(eslint@8.57.1): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.2.1 + eslint: 8.57.1 + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint@8.57.1: + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) + '@eslint-community/regexpp': 4.12.1 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.3.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.1 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + espree@9.6.1: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 3.4.3 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fdir@6.4.6(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@3.2.0: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + rimraf: 3.0.2 + + flatted@3.3.3: {} + + follow-redirects@1.15.11: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + form-data@4.0.4: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.2.0: {} + + graphemer@1.4.0: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + ignore@5.3.2: {} + + immutable@5.1.3: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + intersection-observer@0.12.2: {} + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-generator-function@1.1.0: + dependencies: + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-path-inside@3.0.3: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + js-cookie@3.0.5: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json2mq@0.2.0: + dependencies: + string-convert: 0.2.1 + + json5@2.2.3: {} + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + lodash@4.17.21: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + math-intrinsics@1.1.0: {} + + memoize-one@5.2.1: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + ms@2.1.3: {} + + mutation-observer@1.0.3: {} + + nano-memoize@3.0.16: {} + + nanoid@3.3.11: {} + + natural-compare@1.4.0: {} + + node-addon-api@7.1.1: + optional: true + + node-releases@2.0.19: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-type@4.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + possible-typed-array-names@1.1.0: {} + + postcss-pxtorem@6.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier@3.6.2: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + proxy-from-env@1.1.0: {} + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + rc-cascader@3.34.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-select: 14.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tree: 5.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-checkbox@3.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-collapse@3.9.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-dialog@9.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-drawer@7.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-dropdown@4.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + '@rc-component/trigger': 2.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-field-form@1.44.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + async-validator: 4.2.5 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-field-form@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + '@rc-component/async-validator': 5.0.4 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-image@7.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + '@rc-component/portal': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-dialog: 9.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-input-number@9.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + '@rc-component/mini-decimal': 1.1.0 + classnames: 2.5.1 + rc-input: 1.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-input@1.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-mentions@2.20.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + '@rc-component/trigger': 2.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-input: 1.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-menu: 9.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-textarea: 1.10.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-menu@9.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + '@rc-component/trigger': 2.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-overflow: 1.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-motion@2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-notification@5.6.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-overflow@1.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-pagination@5.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-picker@4.11.3(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + '@rc-component/trigger': 2.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-overflow: 1.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + dayjs: 1.11.13 + + rc-progress@4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-rate@2.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-resize-observer@1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + resize-observer-polyfill: 1.5.1 + + rc-segmented@2.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-segmented@2.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-select@14.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + '@rc-component/trigger': 2.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-overflow: 1.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-virtual-list: 3.19.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-slider@11.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-steps@6.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-switch@4.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-table@7.51.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + '@rc-component/context': 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-virtual-list: 3.19.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-tabs@15.7.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-dropdown: 4.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-menu: 9.16.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-textarea@1.10.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-input: 1.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-tooltip@6.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + '@rc-component/trigger': 2.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-tree-select@5.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-select: 14.16.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-tree: 5.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-tree@5.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-motion: 2.9.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-virtual-list: 3.19.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-upload@4.9.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + rc-util@5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 18.3.1 + + rc-virtual-list@3.19.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + classnames: 2.5.1 + rc-resize-observer: 1.4.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rc-util: 5.44.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-fast-compare@3.2.2: {} + + react-is@16.13.1: {} + + react-is@18.3.1: {} + + react-refresh@0.17.0: {} + + react-router-dom@6.30.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@remix-run/router': 1.23.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 6.30.1(react@18.3.1) + + react-router@6.30.1(react@18.3.1): + dependencies: + '@remix-run/router': 1.23.0 + react: 18.3.1 + + react-window@1.8.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.2 + memoize-one: 5.2.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + readdirp@4.1.2: {} + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + resize-observer-polyfill@1.5.1: {} + + resolve-from@4.0.0: {} + + resolve@2.0.0-next.5: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + rollup@4.46.2: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.46.2 + '@rollup/rollup-android-arm64': 4.46.2 + '@rollup/rollup-darwin-arm64': 4.46.2 + '@rollup/rollup-darwin-x64': 4.46.2 + '@rollup/rollup-freebsd-arm64': 4.46.2 + '@rollup/rollup-freebsd-x64': 4.46.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.46.2 + '@rollup/rollup-linux-arm-musleabihf': 4.46.2 + '@rollup/rollup-linux-arm64-gnu': 4.46.2 + '@rollup/rollup-linux-arm64-musl': 4.46.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.46.2 + '@rollup/rollup-linux-ppc64-gnu': 4.46.2 + '@rollup/rollup-linux-riscv64-gnu': 4.46.2 + '@rollup/rollup-linux-riscv64-musl': 4.46.2 + '@rollup/rollup-linux-s390x-gnu': 4.46.2 + '@rollup/rollup-linux-x64-gnu': 4.46.2 + '@rollup/rollup-linux-x64-musl': 4.46.2 + '@rollup/rollup-win32-arm64-msvc': 4.46.2 + '@rollup/rollup-win32-ia32-msvc': 4.46.2 + '@rollup/rollup-win32-x64-msvc': 4.46.2 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + runes2@1.1.4: {} + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + sass@1.90.0: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.3 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + screenfull@5.2.0: {} + + scroll-into-view-if-needed@3.1.0: + dependencies: + compute-scroll-into-view: 3.1.1 + + semver@6.3.1: {} + + semver@7.7.2: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + size-sensor@1.0.2: {} + + slash@3.0.0: {} + + source-map-js@1.2.1: {} + + staged-components@1.1.3(react@18.3.1): + dependencies: + react: 18.3.1 + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string-convert@0.2.1: {} + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.0 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-json-comments@3.1.1: {} + + stylis@4.3.6: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + synckit@0.11.11: + dependencies: + '@pkgr/core': 0.2.9 + + text-table@0.2.0: {} + + throttle-debounce@5.0.2: {} + + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.6(picomatch@4.0.3) + picomatch: 4.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toggle-selection@1.0.6: {} + + ts-api-utils@1.4.3(typescript@5.9.2): + dependencies: + typescript: 5.9.2 + + tslib@2.3.0: {} + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.20.2: {} + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript@5.9.2: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@7.10.0: {} + + update-browserslist-db@1.1.3(browserslist@4.25.2): + dependencies: + browserslist: 4.25.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-sync-external-store@1.5.0(react@18.3.1): + dependencies: + react: 18.3.1 + + vconsole@3.15.1: + dependencies: + '@babel/runtime': 7.28.2 + copy-text-to-clipboard: 3.2.0 + core-js: 3.45.0 + mutation-observer: 1.0.3 + + vite@7.1.2(@types/node@24.2.1)(sass@1.90.0): + dependencies: + esbuild: 0.25.8 + fdir: 6.4.6(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.46.2 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 24.2.1 + fsevents: 2.3.3 + sass: 1.90.0 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.0 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + wrappy@1.0.2: {} + + xmldom@0.6.0: {} + + yallist@3.1.1: {} + + yocto-queue@0.1.0: {} + + zrender@5.6.1: + dependencies: + tslib: 2.3.0 + + zustand@5.0.7(@types/react@19.1.10)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1)): + optionalDependencies: + '@types/react': 19.1.10 + react: 18.3.1 + use-sync-external-store: 1.5.0(react@18.3.1) diff --git a/Touchkebao/postcss.config.js b/Touchkebao/postcss.config.js new file mode 100644 index 00000000..82ee5d38 --- /dev/null +++ b/Touchkebao/postcss.config.js @@ -0,0 +1,8 @@ +module.exports = { + plugins: { + 'postcss-pxtorem': { + rootValue: 16, + propList: ['*'], + }, + }, +}; \ No newline at end of file diff --git a/Touchkebao/public/assets/animal/发抖.png b/Touchkebao/public/assets/animal/发抖.png new file mode 100644 index 00000000..2bc6cf22 Binary files /dev/null and b/Touchkebao/public/assets/animal/发抖.png differ diff --git a/Touchkebao/public/assets/animal/猪头.png b/Touchkebao/public/assets/animal/猪头.png new file mode 100644 index 00000000..c14749ee Binary files /dev/null and b/Touchkebao/public/assets/animal/猪头.png differ diff --git a/Touchkebao/public/assets/animal/跳跳.png b/Touchkebao/public/assets/animal/跳跳.png new file mode 100644 index 00000000..ae1307aa Binary files /dev/null and b/Touchkebao/public/assets/animal/跳跳.png differ diff --git a/Touchkebao/public/assets/animal/转圈.png b/Touchkebao/public/assets/animal/转圈.png new file mode 100644 index 00000000..7aa3b5a5 Binary files /dev/null and b/Touchkebao/public/assets/animal/转圈.png differ diff --git a/Touchkebao/public/assets/blessing/庆祝.png b/Touchkebao/public/assets/blessing/庆祝.png new file mode 100644 index 00000000..6b3c6391 Binary files /dev/null and b/Touchkebao/public/assets/blessing/庆祝.png differ diff --git a/Touchkebao/public/assets/blessing/烟花.png b/Touchkebao/public/assets/blessing/烟花.png new file mode 100644 index 00000000..58d2848f Binary files /dev/null and b/Touchkebao/public/assets/blessing/烟花.png differ diff --git a/Touchkebao/public/assets/blessing/爆竹.png b/Touchkebao/public/assets/blessing/爆竹.png new file mode 100644 index 00000000..52ac8fb2 Binary files /dev/null and b/Touchkebao/public/assets/blessing/爆竹.png differ diff --git a/Touchkebao/public/assets/blessing/發.png b/Touchkebao/public/assets/blessing/發.png new file mode 100644 index 00000000..2af454cb Binary files /dev/null and b/Touchkebao/public/assets/blessing/發.png differ diff --git a/Touchkebao/public/assets/blessing/礼物.png b/Touchkebao/public/assets/blessing/礼物.png new file mode 100644 index 00000000..6082fbaf Binary files /dev/null and b/Touchkebao/public/assets/blessing/礼物.png differ diff --git a/Touchkebao/public/assets/blessing/福.png b/Touchkebao/public/assets/blessing/福.png new file mode 100644 index 00000000..d4a5b174 Binary files /dev/null and b/Touchkebao/public/assets/blessing/福.png differ diff --git a/Touchkebao/public/assets/blessing/红包.png b/Touchkebao/public/assets/blessing/红包.png new file mode 100644 index 00000000..28b9698d Binary files /dev/null and b/Touchkebao/public/assets/blessing/红包.png differ diff --git a/Touchkebao/public/assets/face/666.png b/Touchkebao/public/assets/face/666.png new file mode 100644 index 00000000..f947dc66 Binary files /dev/null and b/Touchkebao/public/assets/face/666.png differ diff --git a/Touchkebao/public/assets/face/Emm.png b/Touchkebao/public/assets/face/Emm.png new file mode 100644 index 00000000..8fb9370d Binary files /dev/null and b/Touchkebao/public/assets/face/Emm.png differ diff --git a/Touchkebao/public/assets/face/亲亲.png b/Touchkebao/public/assets/face/亲亲.png new file mode 100644 index 00000000..3190fa39 Binary files /dev/null and b/Touchkebao/public/assets/face/亲亲.png differ diff --git a/Touchkebao/public/assets/face/偷笑.png b/Touchkebao/public/assets/face/偷笑.png new file mode 100644 index 00000000..51836d13 Binary files /dev/null and b/Touchkebao/public/assets/face/偷笑.png differ diff --git a/Touchkebao/public/assets/face/傲慢.png b/Touchkebao/public/assets/face/傲慢.png new file mode 100644 index 00000000..cfd4cf55 Binary files /dev/null and b/Touchkebao/public/assets/face/傲慢.png differ diff --git a/Touchkebao/public/assets/face/再见.png b/Touchkebao/public/assets/face/再见.png new file mode 100644 index 00000000..8ef994b6 Binary files /dev/null and b/Touchkebao/public/assets/face/再见.png differ diff --git a/Touchkebao/public/assets/face/加油.png b/Touchkebao/public/assets/face/加油.png new file mode 100644 index 00000000..911ca554 Binary files /dev/null and b/Touchkebao/public/assets/face/加油.png differ diff --git a/Touchkebao/public/assets/face/发呆.png b/Touchkebao/public/assets/face/发呆.png new file mode 100644 index 00000000..e6e388f0 Binary files /dev/null and b/Touchkebao/public/assets/face/发呆.png differ diff --git a/Touchkebao/public/assets/face/发怒.png b/Touchkebao/public/assets/face/发怒.png new file mode 100644 index 00000000..c8cba000 Binary files /dev/null and b/Touchkebao/public/assets/face/发怒.png differ diff --git a/Touchkebao/public/assets/face/可怜.png b/Touchkebao/public/assets/face/可怜.png new file mode 100644 index 00000000..1e75cbb5 Binary files /dev/null and b/Touchkebao/public/assets/face/可怜.png differ diff --git a/Touchkebao/public/assets/face/右哼哼.png b/Touchkebao/public/assets/face/右哼哼.png new file mode 100644 index 00000000..e466e2ae Binary files /dev/null and b/Touchkebao/public/assets/face/右哼哼.png differ diff --git a/Touchkebao/public/assets/face/叹气.png b/Touchkebao/public/assets/face/叹气.png new file mode 100644 index 00000000..2840584d Binary files /dev/null and b/Touchkebao/public/assets/face/叹气.png differ diff --git a/Touchkebao/public/assets/face/吃瓜.png b/Touchkebao/public/assets/face/吃瓜.png new file mode 100644 index 00000000..64f72ade Binary files /dev/null and b/Touchkebao/public/assets/face/吃瓜.png differ diff --git a/Touchkebao/public/assets/face/吐.png b/Touchkebao/public/assets/face/吐.png new file mode 100644 index 00000000..b3e072ed Binary files /dev/null and b/Touchkebao/public/assets/face/吐.png differ diff --git a/Touchkebao/public/assets/face/呲牙.png b/Touchkebao/public/assets/face/呲牙.png new file mode 100644 index 00000000..abce5e0c Binary files /dev/null and b/Touchkebao/public/assets/face/呲牙.png differ diff --git a/Touchkebao/public/assets/face/咒骂.png b/Touchkebao/public/assets/face/咒骂.png new file mode 100644 index 00000000..0a8c0bf2 Binary files /dev/null and b/Touchkebao/public/assets/face/咒骂.png differ diff --git a/Touchkebao/public/assets/face/哇.png b/Touchkebao/public/assets/face/哇.png new file mode 100644 index 00000000..5d1c1796 Binary files /dev/null and b/Touchkebao/public/assets/face/哇.png differ diff --git a/Touchkebao/public/assets/face/嘘.png b/Touchkebao/public/assets/face/嘘.png new file mode 100644 index 00000000..a6700568 Binary files /dev/null and b/Touchkebao/public/assets/face/嘘.png differ diff --git a/Touchkebao/public/assets/face/嘿哈.png b/Touchkebao/public/assets/face/嘿哈.png new file mode 100644 index 00000000..d424a3f3 Binary files /dev/null and b/Touchkebao/public/assets/face/嘿哈.png differ diff --git a/Touchkebao/public/assets/face/囧.png b/Touchkebao/public/assets/face/囧.png new file mode 100644 index 00000000..fc7fbfc2 Binary files /dev/null and b/Touchkebao/public/assets/face/囧.png differ diff --git a/Touchkebao/public/assets/face/困.png b/Touchkebao/public/assets/face/困.png new file mode 100644 index 00000000..148c66fe Binary files /dev/null and b/Touchkebao/public/assets/face/困.png differ diff --git a/Touchkebao/public/assets/face/坏笑.png b/Touchkebao/public/assets/face/坏笑.png new file mode 100644 index 00000000..8585d822 Binary files /dev/null and b/Touchkebao/public/assets/face/坏笑.png differ diff --git a/Touchkebao/public/assets/face/大哭.png b/Touchkebao/public/assets/face/大哭.png new file mode 100644 index 00000000..3c648869 Binary files /dev/null and b/Touchkebao/public/assets/face/大哭.png differ diff --git a/Touchkebao/public/assets/face/天啊.png b/Touchkebao/public/assets/face/天啊.png new file mode 100644 index 00000000..67d1b970 Binary files /dev/null and b/Touchkebao/public/assets/face/天啊.png differ diff --git a/Touchkebao/public/assets/face/失望.png b/Touchkebao/public/assets/face/失望.png new file mode 100644 index 00000000..d38c8888 Binary files /dev/null and b/Touchkebao/public/assets/face/失望.png differ diff --git a/Touchkebao/public/assets/face/奸笑.png b/Touchkebao/public/assets/face/奸笑.png new file mode 100644 index 00000000..895c5917 Binary files /dev/null and b/Touchkebao/public/assets/face/奸笑.png differ diff --git a/Touchkebao/public/assets/face/好的.png b/Touchkebao/public/assets/face/好的.png new file mode 100644 index 00000000..7005ce98 Binary files /dev/null and b/Touchkebao/public/assets/face/好的.png differ diff --git a/Touchkebao/public/assets/face/委屈.png b/Touchkebao/public/assets/face/委屈.png new file mode 100644 index 00000000..b6491fd9 Binary files /dev/null and b/Touchkebao/public/assets/face/委屈.png differ diff --git a/Touchkebao/public/assets/face/害羞.png b/Touchkebao/public/assets/face/害羞.png new file mode 100644 index 00000000..50ec7868 Binary files /dev/null and b/Touchkebao/public/assets/face/害羞.png differ diff --git a/Touchkebao/public/assets/face/尴尬.png b/Touchkebao/public/assets/face/尴尬.png new file mode 100644 index 00000000..6d4900e2 Binary files /dev/null and b/Touchkebao/public/assets/face/尴尬.png differ diff --git a/Touchkebao/public/assets/face/得意.png b/Touchkebao/public/assets/face/得意.png new file mode 100644 index 00000000..0bac94b0 Binary files /dev/null and b/Touchkebao/public/assets/face/得意.png differ diff --git a/Touchkebao/public/assets/face/微笑.png b/Touchkebao/public/assets/face/微笑.png new file mode 100644 index 00000000..a3195d6e Binary files /dev/null and b/Touchkebao/public/assets/face/微笑.png differ diff --git a/Touchkebao/public/assets/face/快哭了.png b/Touchkebao/public/assets/face/快哭了.png new file mode 100644 index 00000000..f0558ba1 Binary files /dev/null and b/Touchkebao/public/assets/face/快哭了.png differ diff --git a/Touchkebao/public/assets/face/恐惧.png b/Touchkebao/public/assets/face/恐惧.png new file mode 100644 index 00000000..6e935b2a Binary files /dev/null and b/Touchkebao/public/assets/face/恐惧.png differ diff --git a/Touchkebao/public/assets/face/悠闲.png b/Touchkebao/public/assets/face/悠闲.png new file mode 100644 index 00000000..dd19c444 Binary files /dev/null and b/Touchkebao/public/assets/face/悠闲.png differ diff --git a/Touchkebao/public/assets/face/惊恐.png b/Touchkebao/public/assets/face/惊恐.png new file mode 100644 index 00000000..8bfda020 Binary files /dev/null and b/Touchkebao/public/assets/face/惊恐.png differ diff --git a/Touchkebao/public/assets/face/惊讶.png b/Touchkebao/public/assets/face/惊讶.png new file mode 100644 index 00000000..33feac55 Binary files /dev/null and b/Touchkebao/public/assets/face/惊讶.png differ diff --git a/Touchkebao/public/assets/face/愉快.png b/Touchkebao/public/assets/face/愉快.png new file mode 100644 index 00000000..a78c8e69 Binary files /dev/null and b/Touchkebao/public/assets/face/愉快.png differ diff --git a/Touchkebao/public/assets/face/憨笑.png b/Touchkebao/public/assets/face/憨笑.png new file mode 100644 index 00000000..b5548942 Binary files /dev/null and b/Touchkebao/public/assets/face/憨笑.png differ diff --git a/Touchkebao/public/assets/face/打脸.png b/Touchkebao/public/assets/face/打脸.png new file mode 100644 index 00000000..d12031aa Binary files /dev/null and b/Touchkebao/public/assets/face/打脸.png differ diff --git a/Touchkebao/public/assets/face/抓狂.png b/Touchkebao/public/assets/face/抓狂.png new file mode 100644 index 00000000..b4408379 Binary files /dev/null and b/Touchkebao/public/assets/face/抓狂.png differ diff --git a/Touchkebao/public/assets/face/抠鼻.png b/Touchkebao/public/assets/face/抠鼻.png new file mode 100644 index 00000000..e44adf69 Binary files /dev/null and b/Touchkebao/public/assets/face/抠鼻.png differ diff --git a/Touchkebao/public/assets/face/捂脸.png b/Touchkebao/public/assets/face/捂脸.png new file mode 100644 index 00000000..ea8a13cd Binary files /dev/null and b/Touchkebao/public/assets/face/捂脸.png differ diff --git a/Touchkebao/public/assets/face/撇嘴.png b/Touchkebao/public/assets/face/撇嘴.png new file mode 100644 index 00000000..937ae748 Binary files /dev/null and b/Touchkebao/public/assets/face/撇嘴.png differ diff --git a/Touchkebao/public/assets/face/擦汗.png b/Touchkebao/public/assets/face/擦汗.png new file mode 100644 index 00000000..b9256ad9 Binary files /dev/null and b/Touchkebao/public/assets/face/擦汗.png differ diff --git a/Touchkebao/public/assets/face/敲打.png b/Touchkebao/public/assets/face/敲打.png new file mode 100644 index 00000000..5eb4480c Binary files /dev/null and b/Touchkebao/public/assets/face/敲打.png differ diff --git a/Touchkebao/public/assets/face/无语.png b/Touchkebao/public/assets/face/无语.png new file mode 100644 index 00000000..443f9d69 Binary files /dev/null and b/Touchkebao/public/assets/face/无语.png differ diff --git a/Touchkebao/public/assets/face/旺柴.png b/Touchkebao/public/assets/face/旺柴.png new file mode 100644 index 00000000..02000fb2 Binary files /dev/null and b/Touchkebao/public/assets/face/旺柴.png differ diff --git a/Touchkebao/public/assets/face/晕.png b/Touchkebao/public/assets/face/晕.png new file mode 100644 index 00000000..8b0a5a28 Binary files /dev/null and b/Touchkebao/public/assets/face/晕.png differ diff --git a/Touchkebao/public/assets/face/机智.png b/Touchkebao/public/assets/face/机智.png new file mode 100644 index 00000000..999d4b56 Binary files /dev/null and b/Touchkebao/public/assets/face/机智.png differ diff --git a/Touchkebao/public/assets/face/汗.png b/Touchkebao/public/assets/face/汗.png new file mode 100644 index 00000000..3b940c55 Binary files /dev/null and b/Touchkebao/public/assets/face/汗.png differ diff --git a/Touchkebao/public/assets/face/流泪.png b/Touchkebao/public/assets/face/流泪.png new file mode 100644 index 00000000..bdfe6fc7 Binary files /dev/null and b/Touchkebao/public/assets/face/流泪.png differ diff --git a/Touchkebao/public/assets/face/生病.png b/Touchkebao/public/assets/face/生病.png new file mode 100644 index 00000000..39fdd919 Binary files /dev/null and b/Touchkebao/public/assets/face/生病.png differ diff --git a/Touchkebao/public/assets/face/疑问.png b/Touchkebao/public/assets/face/疑问.png new file mode 100644 index 00000000..c2bb9c97 Binary files /dev/null and b/Touchkebao/public/assets/face/疑问.png differ diff --git a/Touchkebao/public/assets/face/白眼.png b/Touchkebao/public/assets/face/白眼.png new file mode 100644 index 00000000..fa261a4e Binary files /dev/null and b/Touchkebao/public/assets/face/白眼.png differ diff --git a/Touchkebao/public/assets/face/皱眉.png b/Touchkebao/public/assets/face/皱眉.png new file mode 100644 index 00000000..123bf081 Binary files /dev/null and b/Touchkebao/public/assets/face/皱眉.png differ diff --git a/Touchkebao/public/assets/face/睡.png b/Touchkebao/public/assets/face/睡.png new file mode 100644 index 00000000..8b378481 Binary files /dev/null and b/Touchkebao/public/assets/face/睡.png differ diff --git a/Touchkebao/public/assets/face/破涕为笑.png b/Touchkebao/public/assets/face/破涕为笑.png new file mode 100644 index 00000000..447dcb65 Binary files /dev/null and b/Touchkebao/public/assets/face/破涕为笑.png differ diff --git a/Touchkebao/public/assets/face/社会社会.png b/Touchkebao/public/assets/face/社会社会.png new file mode 100644 index 00000000..e4a311b2 Binary files /dev/null and b/Touchkebao/public/assets/face/社会社会.png differ diff --git a/Touchkebao/public/assets/face/笑脸.png b/Touchkebao/public/assets/face/笑脸.png new file mode 100644 index 00000000..e3ac78d0 Binary files /dev/null and b/Touchkebao/public/assets/face/笑脸.png differ diff --git a/Touchkebao/public/assets/face/翻白眼.png b/Touchkebao/public/assets/face/翻白眼.png new file mode 100644 index 00000000..4ebf05dc Binary files /dev/null and b/Touchkebao/public/assets/face/翻白眼.png differ diff --git a/Touchkebao/public/assets/face/耶.png b/Touchkebao/public/assets/face/耶.png new file mode 100644 index 00000000..969fd94e Binary files /dev/null and b/Touchkebao/public/assets/face/耶.png differ diff --git a/Touchkebao/public/assets/face/脸红.png b/Touchkebao/public/assets/face/脸红.png new file mode 100644 index 00000000..92ba41d1 Binary files /dev/null and b/Touchkebao/public/assets/face/脸红.png differ diff --git a/Touchkebao/public/assets/face/色.png b/Touchkebao/public/assets/face/色.png new file mode 100644 index 00000000..630ae0ec Binary files /dev/null and b/Touchkebao/public/assets/face/色.png differ diff --git a/Touchkebao/public/assets/face/苦涩.png b/Touchkebao/public/assets/face/苦涩.png new file mode 100644 index 00000000..bf0dd647 Binary files /dev/null and b/Touchkebao/public/assets/face/苦涩.png differ diff --git a/Touchkebao/public/assets/face/衰.png b/Touchkebao/public/assets/face/衰.png new file mode 100644 index 00000000..47ed4719 Binary files /dev/null and b/Touchkebao/public/assets/face/衰.png differ diff --git a/Touchkebao/public/assets/face/裂开.png b/Touchkebao/public/assets/face/裂开.png new file mode 100644 index 00000000..626d4798 Binary files /dev/null and b/Touchkebao/public/assets/face/裂开.png differ diff --git a/Touchkebao/public/assets/face/让我看看.png b/Touchkebao/public/assets/face/让我看看.png new file mode 100644 index 00000000..c3fafee6 Binary files /dev/null and b/Touchkebao/public/assets/face/让我看看.png differ diff --git a/Touchkebao/public/assets/face/调皮.png b/Touchkebao/public/assets/face/调皮.png new file mode 100644 index 00000000..5aba419c Binary files /dev/null and b/Touchkebao/public/assets/face/调皮.png differ diff --git a/Touchkebao/public/assets/face/鄙视.png b/Touchkebao/public/assets/face/鄙视.png new file mode 100644 index 00000000..3d32430e Binary files /dev/null and b/Touchkebao/public/assets/face/鄙视.png differ diff --git a/Touchkebao/public/assets/face/闭嘴.png b/Touchkebao/public/assets/face/闭嘴.png new file mode 100644 index 00000000..d89f3507 Binary files /dev/null and b/Touchkebao/public/assets/face/闭嘴.png differ diff --git a/Touchkebao/public/assets/face/阴险.png b/Touchkebao/public/assets/face/阴险.png new file mode 100644 index 00000000..d37f39c1 Binary files /dev/null and b/Touchkebao/public/assets/face/阴险.png differ diff --git a/Touchkebao/public/assets/face/难过.png b/Touchkebao/public/assets/face/难过.png new file mode 100644 index 00000000..636ae7b8 Binary files /dev/null and b/Touchkebao/public/assets/face/难过.png differ diff --git a/Touchkebao/public/assets/face/骷髅.png b/Touchkebao/public/assets/face/骷髅.png new file mode 100644 index 00000000..f8835797 Binary files /dev/null and b/Touchkebao/public/assets/face/骷髅.png differ diff --git a/Touchkebao/public/assets/face/鼓掌.png b/Touchkebao/public/assets/face/鼓掌.png new file mode 100644 index 00000000..c6963bcc Binary files /dev/null and b/Touchkebao/public/assets/face/鼓掌.png differ diff --git a/Touchkebao/public/assets/gesture/OK.png b/Touchkebao/public/assets/gesture/OK.png new file mode 100644 index 00000000..2d0f6e5f Binary files /dev/null and b/Touchkebao/public/assets/gesture/OK.png differ diff --git a/Touchkebao/public/assets/gesture/勾引.png b/Touchkebao/public/assets/gesture/勾引.png new file mode 100644 index 00000000..28e3733e Binary files /dev/null and b/Touchkebao/public/assets/gesture/勾引.png differ diff --git a/Touchkebao/public/assets/gesture/合十.png b/Touchkebao/public/assets/gesture/合十.png new file mode 100644 index 00000000..eca2b73c Binary files /dev/null and b/Touchkebao/public/assets/gesture/合十.png differ diff --git a/Touchkebao/public/assets/gesture/弱.png b/Touchkebao/public/assets/gesture/弱.png new file mode 100644 index 00000000..be8b1a8d Binary files /dev/null and b/Touchkebao/public/assets/gesture/弱.png differ diff --git a/Touchkebao/public/assets/gesture/强.png b/Touchkebao/public/assets/gesture/强.png new file mode 100644 index 00000000..f81c624b Binary files /dev/null and b/Touchkebao/public/assets/gesture/强.png differ diff --git a/Touchkebao/public/assets/gesture/抱拳.png b/Touchkebao/public/assets/gesture/抱拳.png new file mode 100644 index 00000000..51d17dbd Binary files /dev/null and b/Touchkebao/public/assets/gesture/抱拳.png differ diff --git a/Touchkebao/public/assets/gesture/拥抱.png b/Touchkebao/public/assets/gesture/拥抱.png new file mode 100644 index 00000000..0bbcdb98 Binary files /dev/null and b/Touchkebao/public/assets/gesture/拥抱.png differ diff --git a/Touchkebao/public/assets/gesture/拳头.png b/Touchkebao/public/assets/gesture/拳头.png new file mode 100644 index 00000000..91c10e02 Binary files /dev/null and b/Touchkebao/public/assets/gesture/拳头.png differ diff --git a/Touchkebao/public/assets/gesture/握手.png b/Touchkebao/public/assets/gesture/握手.png new file mode 100644 index 00000000..9e6be93c Binary files /dev/null and b/Touchkebao/public/assets/gesture/握手.png differ diff --git a/Touchkebao/public/assets/gesture/胜利.png b/Touchkebao/public/assets/gesture/胜利.png new file mode 100644 index 00000000..2a54535a Binary files /dev/null and b/Touchkebao/public/assets/gesture/胜利.png differ diff --git a/Touchkebao/public/assets/other/便便.png b/Touchkebao/public/assets/other/便便.png new file mode 100644 index 00000000..9ee508fa Binary files /dev/null and b/Touchkebao/public/assets/other/便便.png differ diff --git a/Touchkebao/public/assets/other/凋谢.png b/Touchkebao/public/assets/other/凋谢.png new file mode 100644 index 00000000..b189bb9d Binary files /dev/null and b/Touchkebao/public/assets/other/凋谢.png differ diff --git a/Touchkebao/public/assets/other/咖啡.png b/Touchkebao/public/assets/other/咖啡.png new file mode 100644 index 00000000..91b7c79a Binary files /dev/null and b/Touchkebao/public/assets/other/咖啡.png differ diff --git a/Touchkebao/public/assets/other/啤酒.png b/Touchkebao/public/assets/other/啤酒.png new file mode 100644 index 00000000..81d40ba7 Binary files /dev/null and b/Touchkebao/public/assets/other/啤酒.png differ diff --git a/Touchkebao/public/assets/other/嘴唇.png b/Touchkebao/public/assets/other/嘴唇.png new file mode 100644 index 00000000..858e8547 Binary files /dev/null and b/Touchkebao/public/assets/other/嘴唇.png differ diff --git a/Touchkebao/public/assets/other/太阳.png b/Touchkebao/public/assets/other/太阳.png new file mode 100644 index 00000000..04578c43 Binary files /dev/null and b/Touchkebao/public/assets/other/太阳.png differ diff --git a/Touchkebao/public/assets/other/心碎.png b/Touchkebao/public/assets/other/心碎.png new file mode 100644 index 00000000..dc23ec82 Binary files /dev/null and b/Touchkebao/public/assets/other/心碎.png differ diff --git a/Touchkebao/public/assets/other/月亮.png b/Touchkebao/public/assets/other/月亮.png new file mode 100644 index 00000000..20ed34ef Binary files /dev/null and b/Touchkebao/public/assets/other/月亮.png differ diff --git a/Touchkebao/public/assets/other/炸弹.png b/Touchkebao/public/assets/other/炸弹.png new file mode 100644 index 00000000..9ece24cb Binary files /dev/null and b/Touchkebao/public/assets/other/炸弹.png differ diff --git a/Touchkebao/public/assets/other/爱心.png b/Touchkebao/public/assets/other/爱心.png new file mode 100644 index 00000000..aa9b744c Binary files /dev/null and b/Touchkebao/public/assets/other/爱心.png differ diff --git a/Touchkebao/public/assets/other/玫瑰.png b/Touchkebao/public/assets/other/玫瑰.png new file mode 100644 index 00000000..83cc3b27 Binary files /dev/null and b/Touchkebao/public/assets/other/玫瑰.png differ diff --git a/Touchkebao/public/assets/other/菜刀.png b/Touchkebao/public/assets/other/菜刀.png new file mode 100644 index 00000000..77c5a137 Binary files /dev/null and b/Touchkebao/public/assets/other/菜刀.png differ diff --git a/Touchkebao/public/assets/other/蛋糕.png b/Touchkebao/public/assets/other/蛋糕.png new file mode 100644 index 00000000..9a62501c Binary files /dev/null and b/Touchkebao/public/assets/other/蛋糕.png differ diff --git a/Touchkebao/public/logo.png b/Touchkebao/public/logo.png new file mode 100644 index 00000000..40daafb0 Binary files /dev/null and b/Touchkebao/public/logo.png differ diff --git a/Touchkebao/public/manifest.json b/Touchkebao/public/manifest.json new file mode 100644 index 00000000..fc1e9f5a --- /dev/null +++ b/Touchkebao/public/manifest.json @@ -0,0 +1,30 @@ +{ + "name": "Cunkebao", + "short_name": "Cunkebao", + "description": "Cunkebao Mobile App", + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone", + "orientation": "portrait", + "scope": "/", + "start_url": "/", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "logo.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any maskable" + } + ] +} \ No newline at end of file diff --git a/Touchkebao/public/websdk.js b/Touchkebao/public/websdk.js new file mode 100644 index 00000000..99870fa4 --- /dev/null +++ b/Touchkebao/public/websdk.js @@ -0,0 +1,308 @@ +!(function (e, n) { + "object" == typeof exports && "undefined" != typeof module + ? (module.exports = n()) + : "function" == typeof define && define.amd + ? define(n) + : ((e = e || self).uni = n()); +})(this, function () { + "use strict"; + try { + var e = {}; + (Object.defineProperty(e, "passive", { + get: function () { + !0; + }, + }), + window.addEventListener("test-passive", null, e)); + } catch (e) {} + var n = Object.prototype.hasOwnProperty; + function i(e, i) { + return n.call(e, i); + } + var t = []; + function o() { + return window.__dcloud_weex_postMessage || window.__dcloud_weex_; + } + function a() { + return window.__uniapp_x_postMessage || window.__uniapp_x_; + } + var r = function (e, n) { + var i = { options: { timestamp: +new Date() }, name: e, arg: n }; + if (a()) { + if ("postMessage" === e) { + var r = { data: n }; + return window.__uniapp_x_postMessage + ? window.__uniapp_x_postMessage(r) + : window.__uniapp_x_.postMessage(JSON.stringify(r)); + } + var d = { + type: "WEB_INVOKE_APPSERVICE", + args: { data: i, webviewIds: t }, + }; + window.__uniapp_x_postMessage + ? window.__uniapp_x_postMessageToService(d) + : window.__uniapp_x_.postMessageToService(JSON.stringify(d)); + } else if (o()) { + if ("postMessage" === e) { + var s = { data: [n] }; + return window.__dcloud_weex_postMessage + ? window.__dcloud_weex_postMessage(s) + : window.__dcloud_weex_.postMessage(JSON.stringify(s)); + } + var w = { + type: "WEB_INVOKE_APPSERVICE", + args: { data: i, webviewIds: t }, + }; + window.__dcloud_weex_postMessage + ? window.__dcloud_weex_postMessageToService(w) + : window.__dcloud_weex_.postMessageToService(JSON.stringify(w)); + } else { + if (!window.plus) + return window.parent.postMessage( + { type: "WEB_INVOKE_APPSERVICE", data: i, pageId: "" }, + "*", + ); + if (0 === t.length) { + var u = plus.webview.currentWebview(); + if (!u) throw new Error("plus.webview.currentWebview() is undefined"); + var g = u.parent(), + v = ""; + ((v = g ? g.id : u.id), t.push(v)); + } + if (plus.webview.getWebviewById("__uniapp__service")) + plus.webview.postMessageToUniNView( + { type: "WEB_INVOKE_APPSERVICE", args: { data: i, webviewIds: t } }, + "__uniapp__service", + ); + else { + var c = JSON.stringify(i); + plus.webview + .getLaunchWebview() + .evalJS( + 'UniPlusBridge.subscribeHandler("' + .concat("WEB_INVOKE_APPSERVICE", '",') + .concat(c, ",") + .concat(JSON.stringify(t), ");"), + ); + } + } + }, + d = { + navigateTo: function () { + var e = + arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}, + n = e.url; + r("navigateTo", { url: encodeURI(n) }); + }, + navigateBack: function () { + var e = + arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}, + n = e.delta; + r("navigateBack", { delta: parseInt(n) || 1 }); + }, + switchTab: function () { + var e = + arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}, + n = e.url; + r("switchTab", { url: encodeURI(n) }); + }, + reLaunch: function () { + var e = + arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}, + n = e.url; + r("reLaunch", { url: encodeURI(n) }); + }, + redirectTo: function () { + var e = + arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}, + n = e.url; + r("redirectTo", { url: encodeURI(n) }); + }, + getEnv: function (e) { + a() + ? e({ uvue: !0 }) + : o() + ? e({ nvue: !0 }) + : window.plus + ? e({ plus: !0 }) + : e({ h5: !0 }); + }, + postMessage: function () { + var e = + arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; + r("postMessage", e.data || {}); + }, + }, + s = /uni-app/i.test(navigator.userAgent), + w = /Html5Plus/i.test(navigator.userAgent), + u = /complete|loaded|interactive/; + var g = + window.my && + navigator.userAgent.indexOf( + ["t", "n", "e", "i", "l", "C", "y", "a", "p", "i", "l", "A"] + .reverse() + .join(""), + ) > -1; + var v = + window.swan && window.swan.webView && /swan/i.test(navigator.userAgent); + var c = + window.qq && + window.qq.miniProgram && + /QQ/i.test(navigator.userAgent) && + /miniProgram/i.test(navigator.userAgent); + var p = + window.tt && + window.tt.miniProgram && + /toutiaomicroapp/i.test(navigator.userAgent); + var _ = + window.wx && + window.wx.miniProgram && + /micromessenger/i.test(navigator.userAgent) && + /miniProgram/i.test(navigator.userAgent); + var m = window.qa && /quickapp/i.test(navigator.userAgent); + var f = + window.ks && + window.ks.miniProgram && + /micromessenger/i.test(navigator.userAgent) && + /miniProgram/i.test(navigator.userAgent); + var l = + window.tt && + window.tt.miniProgram && + /Lark|Feishu/i.test(navigator.userAgent); + var E = + window.jd && window.jd.miniProgram && /jdmp/i.test(navigator.userAgent); + var x = + window.xhs && + window.xhs.miniProgram && + /xhsminiapp/i.test(navigator.userAgent); + for ( + var S, + h = function () { + ((window.UniAppJSBridge = !0), + document.dispatchEvent( + new CustomEvent("UniAppJSBridgeReady", { + bubbles: !0, + cancelable: !0, + }), + )); + }, + y = [ + function (e) { + if (s || w) + return ( + window.__uniapp_x_postMessage || + window.__uniapp_x_ || + window.__dcloud_weex_postMessage || + window.__dcloud_weex_ + ? document.addEventListener("DOMContentLoaded", e) + : window.plus && u.test(document.readyState) + ? setTimeout(e, 0) + : document.addEventListener("plusready", e), + d + ); + }, + function (e) { + if (_) + return ( + window.WeixinJSBridge && window.WeixinJSBridge.invoke + ? setTimeout(e, 0) + : document.addEventListener("WeixinJSBridgeReady", e), + window.wx.miniProgram + ); + }, + function (e) { + if (c) + return ( + window.QQJSBridge && window.QQJSBridge.invoke + ? setTimeout(e, 0) + : document.addEventListener("QQJSBridgeReady", e), + window.qq.miniProgram + ); + }, + function (e) { + if (g) { + document.addEventListener("DOMContentLoaded", e); + var n = window.my; + return { + navigateTo: n.navigateTo, + navigateBack: n.navigateBack, + switchTab: n.switchTab, + reLaunch: n.reLaunch, + redirectTo: n.redirectTo, + postMessage: n.postMessage, + getEnv: n.getEnv, + }; + } + }, + function (e) { + if (v) + return ( + document.addEventListener("DOMContentLoaded", e), + window.swan.webView + ); + }, + function (e) { + if (p) + return ( + document.addEventListener("DOMContentLoaded", e), + window.tt.miniProgram + ); + }, + function (e) { + if (m) { + window.QaJSBridge && window.QaJSBridge.invoke + ? setTimeout(e, 0) + : document.addEventListener("QaJSBridgeReady", e); + var n = window.qa; + return { + navigateTo: n.navigateTo, + navigateBack: n.navigateBack, + switchTab: n.switchTab, + reLaunch: n.reLaunch, + redirectTo: n.redirectTo, + postMessage: n.postMessage, + getEnv: n.getEnv, + }; + } + }, + function (e) { + if (f) + return ( + window.WeixinJSBridge && window.WeixinJSBridge.invoke + ? setTimeout(e, 0) + : document.addEventListener("WeixinJSBridgeReady", e), + window.ks.miniProgram + ); + }, + function (e) { + if (l) + return ( + document.addEventListener("DOMContentLoaded", e), + window.tt.miniProgram + ); + }, + function (e) { + if (E) + return ( + window.JDJSBridgeReady && window.JDJSBridgeReady.invoke + ? setTimeout(e, 0) + : document.addEventListener("JDJSBridgeReady", e), + window.jd.miniProgram + ); + }, + function (e) { + if (x) return window.xhs.miniProgram; + }, + function (e) { + return (document.addEventListener("DOMContentLoaded", e), d); + }, + ], + M = 0; + M < y.length && !(S = y[M](h)); + M++ + ); + S || (S = {}); + var P = "undefined" != typeof uni ? uni : {}; + if (!P.navigateTo) for (var b in S) i(S, b) && (P[b] = S[b]); + return ((P.webView = S), P); +}); diff --git a/Touchkebao/src/App.tsx b/Touchkebao/src/App.tsx new file mode 100644 index 00000000..01bf3208 --- /dev/null +++ b/Touchkebao/src/App.tsx @@ -0,0 +1,14 @@ +import React from "react"; +import AppRouter from "@/router"; +import UpdateNotification from "@/components/UpdateNotification"; + +function App() { + return ( + <> + + + + ); +} + +export default App; diff --git a/Touchkebao/src/android-polyfills.ts b/Touchkebao/src/android-polyfills.ts new file mode 100644 index 00000000..583fd397 --- /dev/null +++ b/Touchkebao/src/android-polyfills.ts @@ -0,0 +1,352 @@ +// Android 专用 polyfill - 解决Android 7等低版本系统的兼容性问题 + +// 检测是否为Android设备 +const isAndroid = () => { + return /Android/i.test(navigator.userAgent); +}; + +// 检测Android版本 +const getAndroidVersion = () => { + const match = navigator.userAgent.match(/Android\s+(\d+)/); + return match ? parseInt(match[1]) : 0; +}; + +// 检测是否为低版本Android +const isLowVersionAndroid = () => { + const version = getAndroidVersion(); + return version <= 7; // Android 7及以下版本 +}; + +// 只在Android设备上执行polyfill +if (isAndroid() && isLowVersionAndroid()) { + console.log("检测到低版本Android系统,启用兼容性polyfill"); + + // 修复Array.prototype.includes在Android WebView中的问题 + if (!Array.prototype.includes) { + Array.prototype.includes = function (searchElement, fromIndex) { + if (this == null) { + throw new TypeError('"this" is null or not defined'); + } + var o = Object(this); + var len = o.length >>> 0; + if (len === 0) { + return false; + } + var n = fromIndex | 0; + var k = Math.max(n >= 0 ? n : len + n, 0); + while (k < len) { + if (o[k] === searchElement) { + return true; + } + k++; + } + return false; + }; + } + + // 修复String.prototype.includes在Android WebView中的问题 + if (!String.prototype.includes) { + String.prototype.includes = function (search, start) { + if (typeof start !== "number") { + start = 0; + } + if (start + search.length > this.length) { + return false; + } else { + return this.indexOf(search, start) !== -1; + } + }; + } + + // 修复String.prototype.startsWith在Android WebView中的问题 + if (!String.prototype.startsWith) { + String.prototype.startsWith = function (searchString, position) { + position = position || 0; + return this.substr(position, searchString.length) === searchString; + }; + } + + // 修复String.prototype.endsWith在Android WebView中的问题 + if (!String.prototype.endsWith) { + String.prototype.endsWith = function (searchString, length) { + if (length === undefined || length > this.length) { + length = this.length; + } + return ( + this.substring(length - searchString.length, length) === searchString + ); + }; + } + + // 修复Array.prototype.find在Android WebView中的问题 + if (!Array.prototype.find) { + Array.prototype.find = function (predicate) { + if (this == null) { + throw new TypeError("Array.prototype.find called on null or undefined"); + } + if (typeof predicate !== "function") { + throw new TypeError("predicate must be a function"); + } + var list = Object(this); + var length = parseInt(list.length) || 0; + var thisArg = arguments[1]; + for (var i = 0; i < length; i++) { + var element = list[i]; + if (predicate.call(thisArg, element, i, list)) { + return element; + } + } + return undefined; + }; + } + + // 修复Array.prototype.findIndex在Android WebView中的问题 + if (!Array.prototype.findIndex) { + Array.prototype.findIndex = function (predicate) { + if (this == null) { + throw new TypeError( + "Array.prototype.findIndex called on null or undefined", + ); + } + if (typeof predicate !== "function") { + throw new TypeError("predicate must be a function"); + } + var list = Object(this); + var length = parseInt(list.length) || 0; + var thisArg = arguments[1]; + for (var i = 0; i < length; i++) { + var element = list[i]; + if (predicate.call(thisArg, element, i, list)) { + return i; + } + } + return -1; + }; + } + + // 修复Object.assign在Android WebView中的问题 + if (typeof Object.assign !== "function") { + Object.assign = function (target) { + if (target == null) { + throw new TypeError("Cannot convert undefined or null to object"); + } + var to = Object(target); + for (var index = 1; index < arguments.length; index++) { + var nextSource = arguments[index]; + if (nextSource != null) { + for (var nextKey in nextSource) { + if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { + to[nextKey] = nextSource[nextKey]; + } + } + } + } + return to; + }; + } + + // 修复Array.from在Android WebView中的问题 + if (!Array.from) { + Array.from = (function () { + var toStr = Object.prototype.toString; + var isCallable = function (fn) { + return ( + typeof fn === "function" || toStr.call(fn) === "[object Function]" + ); + }; + var toInteger = function (value) { + var number = Number(value); + if (isNaN(number)) { + return 0; + } + if (number === 0 || !isFinite(number)) { + return number; + } + return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number)); + }; + var maxSafeInteger = Math.pow(2, 53) - 1; + var toLength = function (value) { + var len = toInteger(value); + return Math.min(Math.max(len, 0), maxSafeInteger); + }; + return function from(arrayLike) { + var C = this; + var items = Object(arrayLike); + if (arrayLike == null) { + throw new TypeError( + "Array.from requires an array-like object - not null or undefined", + ); + } + var mapFunction = arguments.length > 1 ? arguments[1] : void undefined; + var T; + if (typeof mapFunction !== "undefined") { + if (typeof mapFunction !== "function") { + throw new TypeError( + "Array.from: when provided, the second argument must be a function", + ); + } + if (arguments.length > 2) { + T = arguments[2]; + } + } + var len = toLength(items.length); + var A = isCallable(C) ? Object(new C(len)) : new Array(len); + var k = 0; + var kValue; + while (k < len) { + kValue = items[k]; + if (mapFunction) { + A[k] = + typeof T === "undefined" + ? mapFunction(kValue, k) + : mapFunction.call(T, kValue, k); + } else { + A[k] = kValue; + } + k += 1; + } + A.length = len; + return A; + }; + })(); + } + + // 修复requestAnimationFrame在Android WebView中的问题 + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = function (callback) { + return setTimeout(function () { + callback(Date.now()); + }, 1000 / 60); + }; + } + + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function (id) { + clearTimeout(id); + }; + } + + // 修复IntersectionObserver在Android WebView中的问题 + if (!window.IntersectionObserver) { + window.IntersectionObserver = function (callback, options) { + this.callback = callback; + this.options = options || {}; + this.observers = []; + + this.observe = function (element) { + this.observers.push(element); + // 简单的实现,实际项目中可能需要更复杂的逻辑 + setTimeout(() => { + this.callback([ + { + target: element, + isIntersecting: true, + intersectionRatio: 1, + }, + ]); + }, 100); + }; + + this.unobserve = function (element) { + var index = this.observers.indexOf(element); + if (index > -1) { + this.observers.splice(index, 1); + } + }; + + this.disconnect = function () { + this.observers = []; + }; + }; + } + + // 修复ResizeObserver在Android WebView中的问题 + if (!window.ResizeObserver) { + window.ResizeObserver = function (callback) { + this.callback = callback; + this.observers = []; + + this.observe = function (element) { + this.observers.push(element); + }; + + this.unobserve = function (element) { + var index = this.observers.indexOf(element); + if (index > -1) { + this.observers.splice(index, 1); + } + }; + + this.disconnect = function () { + this.observers = []; + }; + }; + } + + // 修复URLSearchParams在Android WebView中的问题 + if (!window.URLSearchParams) { + window.URLSearchParams = function (init) { + this.params = {}; + + if (init) { + if (typeof init === "string") { + if (init.charAt(0) === "?") { + init = init.slice(1); + } + var pairs = init.split("&"); + for (var i = 0; i < pairs.length; i++) { + var pair = pairs[i].split("="); + var key = decodeURIComponent(pair[0]); + var value = decodeURIComponent(pair[1] || ""); + this.append(key, value); + } + } + } + + this.append = function (name, value) { + if (!this.params[name]) { + this.params[name] = []; + } + this.params[name].push(value); + }; + + this.get = function (name) { + return this.params[name] ? this.params[name][0] : null; + }; + + this.getAll = function (name) { + return this.params[name] || []; + }; + + this.has = function (name) { + return !!this.params[name]; + }; + + this.set = function (name, value) { + this.params[name] = [value]; + }; + + this.delete = function (name) { + delete this.params[name]; + }; + + this.toString = function () { + var pairs = []; + for (var key in this.params) { + if (this.params.hasOwnProperty(key)) { + for (var i = 0; i < this.params[key].length; i++) { + pairs.push( + encodeURIComponent(key) + + "=" + + encodeURIComponent(this.params[key][i]), + ); + } + } + } + return pairs.join("&"); + }; + }; + } + + console.log("Android兼容性polyfill已加载完成"); +} diff --git a/Touchkebao/src/api/common.ts b/Touchkebao/src/api/common.ts new file mode 100644 index 00000000..d6147d80 --- /dev/null +++ b/Touchkebao/src/api/common.ts @@ -0,0 +1,37 @@ +import axios from "axios"; +import { useUserStore } from "@/store/module/user"; + +/** + * 通用文件上传方法(支持图片、文件) + * @param {File} file - 要上传的文件对象 + * @param {string} [uploadUrl='/v1/attachment/upload'] - 上传接口地址 + * @returns {Promise} - 上传成功后返回文件url + */ +export async function uploadFile( + file: File, + uploadUrl: string = "/v1/attachment/upload", +): Promise { + try { + // 创建 FormData 对象用于文件上传 + const formData = new FormData(); + formData.append("file", file); + + // 获取用户token + const { token } = useUserStore.getState(); + + const fullUrl = `${(import.meta as any).env?.VITE_API_BASE_URL || "/api"}${uploadUrl}`; + + // 直接使用 axios 上传文件 + const response = await axios.post(fullUrl, formData, { + headers: { + Authorization: token ? `Bearer ${token}` : undefined, + }, + timeout: 20000, + }); + return response?.data?.data?.url || ""; + } catch (e: any) { + const errorMessage = + e.response?.data?.message || e.message || "文件上传失败"; + throw new Error(errorMessage); + } +} diff --git a/Touchkebao/src/api/request.ts b/Touchkebao/src/api/request.ts new file mode 100644 index 00000000..6394d22e --- /dev/null +++ b/Touchkebao/src/api/request.ts @@ -0,0 +1,90 @@ +import axios, { + AxiosInstance, + AxiosRequestConfig, + Method, + AxiosResponse, +} from "axios"; +import { Toast } from "antd-mobile"; +import { useUserStore } from "@/store/module/user"; +const { token } = useUserStore.getState(); +const DEFAULT_DEBOUNCE_GAP = 1000; +const debounceMap = new Map(); + +const instance: AxiosInstance = axios.create({ + baseURL: (import.meta as any).env?.VITE_API_BASE_URL || "/api", + timeout: 20000, + headers: { + "Content-Type": "application/json", + }, +}); + +instance.interceptors.request.use((config: any) => { + if (token) { + config.headers = config.headers || {}; + config.headers["Authorization"] = `Bearer ${token}`; + } + return config; +}); + +instance.interceptors.response.use( + (res: AxiosResponse) => { + const { code, success, msg } = res.data || {}; + if (code === 200 || success) { + return res.data.data ?? res.data; + } + Toast.show({ content: msg || "接口错误", position: "top" }); + if (code === 401) { + localStorage.removeItem("token"); + const currentPath = window.location.pathname + window.location.search; + if (currentPath === "/login") { + window.location.href = "/login"; + } else { + window.location.href = `/login?redirect=${encodeURIComponent(currentPath)}`; + } + } + return Promise.reject(msg || "接口错误"); + }, + err => { + Toast.show({ content: err.message || "网络异常", position: "top" }); + return Promise.reject(err); + }, +); + +export function request( + url: string, + data?: any, + method: Method = "GET", + config?: AxiosRequestConfig, + debounceGap?: number, +): Promise { + const gap = + typeof debounceGap === "number" ? debounceGap : DEFAULT_DEBOUNCE_GAP; + const key = `${method}_${url}_${JSON.stringify(data)}`; + const now = Date.now(); + const last = debounceMap.get(key) || 0; + if (gap > 0 && now - last < gap) { + // Toast.show({ content: '请求过于频繁,请稍后再试', position: 'top' }); + return Promise.reject("请求过于频繁,请稍后再试"); + } + debounceMap.set(key, now); + + const axiosConfig: AxiosRequestConfig = { + url, + method, + ...config, + }; + + // 如果是FormData,不设置Content-Type,让浏览器自动设置 + if (data instanceof FormData) { + delete axiosConfig.headers?.["Content-Type"]; + } + + if (method.toUpperCase() === "GET") { + axiosConfig.params = data; + } else { + axiosConfig.data = data; + } + return instance(axiosConfig); +} + +export default request; diff --git a/Touchkebao/src/api/request2.ts b/Touchkebao/src/api/request2.ts new file mode 100644 index 00000000..deb3d9bf --- /dev/null +++ b/Touchkebao/src/api/request2.ts @@ -0,0 +1,89 @@ +import axios, { + AxiosInstance, + AxiosRequestConfig, + Method, + AxiosResponse, +} from "axios"; +import { Toast } from "antd-mobile"; +import { useUserStore } from "@/store/module/user"; +const DEFAULT_DEBOUNCE_GAP = 1000; +const debounceMap = new Map(); + +interface RequestConfig extends AxiosRequestConfig { + headers: { + Client?: string; + "Content-Type"?: string; + }; +} + +const instance: AxiosInstance = axios.create({ + baseURL: (import.meta as any).env?.VITE_API_BASE_URL2 || "/api", + timeout: 20000, + headers: { + "Content-Type": "application/json", + Client: "kefu-client", + }, +}); + +instance.interceptors.request.use((config: any) => { + // 在每次请求时动态获取最新的 token2 + const { token2 } = useUserStore.getState(); + if (token2) { + config.headers = config.headers || {}; + config.headers["Authorization"] = `bearer ${token2}`; + } + return config; +}); + +instance.interceptors.response.use( + (res: AxiosResponse) => { + return res.data; + }, + err => { + // 处理401错误,跳转到登录页面 + if (err.response && err.response.status === 401) { + Toast.show({ content: "登录已过期,请重新登录", position: "top" }); + // 获取当前路径,用于登录后跳回 + const currentPath = window.location.pathname + window.location.search; + window.location.href = `/login?returnUrl=${encodeURIComponent(currentPath)}`; + return Promise.reject(err); + } + + Toast.show({ content: err.message || "网络异常", position: "top" }); + return Promise.reject(err); + }, +); + +export function request( + url: string, + data?: any, + method: Method = "GET", + config?: RequestConfig, + debounceGap?: number, +): Promise { + const gap = + typeof debounceGap === "number" ? debounceGap : DEFAULT_DEBOUNCE_GAP; + const key = `${method}_${url}_${JSON.stringify(data)}`; + const now = Date.now(); + const last = debounceMap.get(key) || 0; + if (gap > 0 && now - last < gap) { + // Toast.show({ content: '请求过于频繁,请稍后再试', position: 'top' }); + return Promise.reject("请求过于频繁,请稍后再试"); + } + debounceMap.set(key, now); + + const axiosConfig: RequestConfig = { + url, + method, + ...config, + }; + + if (method.toUpperCase() === "GET") { + axiosConfig.params = data; + } else { + axiosConfig.data = data; + } + return instance(axiosConfig); +} + +export default request; diff --git a/Touchkebao/src/components/AccountSelection/api.ts b/Touchkebao/src/components/AccountSelection/api.ts new file mode 100644 index 00000000..bf069cb8 --- /dev/null +++ b/Touchkebao/src/components/AccountSelection/api.ts @@ -0,0 +1,10 @@ +import request from "@/api/request"; + +// 获取好友列表 +export function getAccountList(params: { + page: number; + limit: number; + keyword?: string; +}) { + return request("/v1/workbench/account-list", params, "GET"); +} diff --git a/Touchkebao/src/components/AccountSelection/data.ts b/Touchkebao/src/components/AccountSelection/data.ts new file mode 100644 index 00000000..c0ce4343 --- /dev/null +++ b/Touchkebao/src/components/AccountSelection/data.ts @@ -0,0 +1,35 @@ +// 账号对象类型 +export interface AccountItem { + id: number; + userName: string; + realName: string; + departmentName: string; + avatar?: string; + [key: string]: any; +} +//弹窗的 +export interface SelectionPopupProps { + visible: boolean; + onVisibleChange: (visible: boolean) => void; + selectedOptions: AccountItem[]; + onSelect: (options: AccountItem[]) => void; + readonly?: boolean; + onConfirm?: (selectedOptions: AccountItem[]) => void; +} + +// 组件属性接口 +export interface AccountSelectionProps { + selectedOptions: AccountItem[]; + onSelect: (options: AccountItem[]) => void; + accounts?: AccountItem[]; // 可选:用于在外层显示已选账号详情 + placeholder?: string; + className?: string; + visible?: boolean; + onVisibleChange?: (visible: boolean) => void; + selectedListMaxHeight?: number; + showInput?: boolean; + showSelectedList?: boolean; + readonly?: boolean; + onConfirm?: (selectedOptions: AccountItem[]) => void; + accountGroups?: any[]; // 传递账号组数据 +} diff --git a/Touchkebao/src/components/AccountSelection/index.module.scss b/Touchkebao/src/components/AccountSelection/index.module.scss new file mode 100644 index 00000000..51eb1af5 --- /dev/null +++ b/Touchkebao/src/components/AccountSelection/index.module.scss @@ -0,0 +1,231 @@ +.inputWrapper { + position: relative; +} +.inputIcon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: #bdbdbd; + font-size: 20px; +} +.input { + padding-left: 38px !important; + height: 48px; + border-radius: 16px !important; + border: 1px solid #e5e6eb !important; + font-size: 16px; + background: #f8f9fa; +} + +.popupContainer { + display: flex; + flex-direction: column; + height: 100vh; + background: #fff; +} +.popupHeader { + padding: 24px; +} +.popupTitle { + text-align: center; + font-size: 20px; + font-weight: 600; + margin-bottom: 24px; +} +.searchWrapper { + position: relative; + margin-bottom: 16px; +} +.searchInput { + padding-left: 40px !important; + padding-top: 8px !important; + padding-bottom: 8px !important; + border-radius: 24px !important; + border: 1px solid #e5e6eb !important; + font-size: 15px; + background: #f8f9fa; +} +.searchIcon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: #bdbdbd; + font-size: 16px; +} +.clearBtn { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + height: 24px; + width: 24px; + border-radius: 50%; + min-width: 24px; +} + +.friendList { + flex: 1; + overflow-y: auto; +} +.friendListInner { + border-top: 1px solid #f0f0f0; +} +.friendItem { + display: flex; + align-items: center; + padding: 16px 24px; + border-bottom: 1px solid #f0f0f0; + cursor: pointer; + transition: background 0.2s; + &:hover { + background: #f5f6fa; + } +} +.radioWrapper { + margin-right: 12px; + display: flex; + align-items: center; + justify-content: center; +} +.radioSelected { + width: 20px; + height: 20px; + border-radius: 50%; + border: 2px solid #1890ff; + display: flex; + align-items: center; + justify-content: center; +} +.radioUnselected { + width: 20px; + height: 20px; + border-radius: 50%; + border: 2px solid #e5e6eb; + display: flex; + align-items: center; + justify-content: center; +} +.radioDot { + width: 12px; + height: 12px; + border-radius: 50%; + background: #1890ff; +} +.friendInfo { + display: flex; + align-items: center; + gap: 12px; + flex: 1; +} +.friendAvatar { + width: 40px; + height: 40px; + border-radius: 50%; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + display: flex; + align-items: center; + justify-content: center; + color: #fff; + font-size: 14px; + font-weight: 500; + overflow: hidden; +} +.avatarImg { + width: 100%; + height: 100%; + object-fit: cover; +} +.friendDetail { + flex: 1; +} +.friendName { + font-weight: 500; + font-size: 16px; + color: #222; + margin-bottom: 2px; +} +.friendId { + font-size: 13px; + color: #888; + margin-bottom: 2px; +} +.friendCustomer { + font-size: 13px; + color: #bdbdbd; +} + +.loadingBox { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} +.loadingText { + color: #888; + font-size: 15px; +} +.emptyBox { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} +.emptyText { + color: #888; + font-size: 15px; +} + +.paginationRow { + border-top: 1px solid #f0f0f0; + padding: 16px; + display: flex; + align-items: center; + justify-content: space-between; + background: #fff; +} +.totalCount { + font-size: 14px; + color: #888; +} +.paginationControls { + display: flex; + align-items: center; + gap: 8px; +} +.pageBtn { + padding: 0 8px; + height: 32px; + min-width: 32px; +} +.pageInfo { + font-size: 14px; + color: #222; +} + +.popupFooter { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px; + border-top: 1px solid #f0f0f0; + background: #fff; +} +.selectedCount { + font-size: 14px; + color: #888; +} +.footerBtnGroup { + display: flex; + gap: 12px; +} +.cancelBtn { + padding: 0 24px; + border-radius: 24px; + border: 1px solid #e5e6eb; +} +.confirmBtn { + padding: 0 24px; + border-radius: 24px; +} diff --git a/Touchkebao/src/components/AccountSelection/index.tsx b/Touchkebao/src/components/AccountSelection/index.tsx new file mode 100644 index 00000000..6e19a88b --- /dev/null +++ b/Touchkebao/src/components/AccountSelection/index.tsx @@ -0,0 +1,139 @@ +import React, { useState } from "react"; +import { SearchOutlined, DeleteOutlined } from "@ant-design/icons"; +import { Button, Input } from "antd"; +import style from "./index.module.scss"; +import SelectionPopup from "./selectionPopup"; +import { AccountItem, AccountSelectionProps } from "./data"; + +export default function AccountSelection({ + selectedOptions, + onSelect, + accounts: propAccounts = [], + placeholder = "选择账号", + className = "", + visible, + onVisibleChange, + selectedListMaxHeight = 300, + showInput = true, + showSelectedList = true, + readonly = false, + onConfirm, +}: AccountSelectionProps) { + const [popupVisible, setPopupVisible] = useState(false); + + // 受控弹窗逻辑 + const realVisible = visible !== undefined ? visible : popupVisible; + const setRealVisible = (v: boolean) => { + if (onVisibleChange) onVisibleChange(v); + if (visible === undefined) setPopupVisible(v); + }; + + // 打开弹窗 + const openPopup = () => { + if (readonly) return; + setRealVisible(true); + }; + + // 获取显示文本 + const getDisplayText = () => { + if (selectedOptions.length === 0) return ""; + return `已选择 ${selectedOptions.length} 个账号`; + }; + + // 删除已选账号 + const handleRemoveAccount = (id: number) => { + if (readonly) return; + onSelect(selectedOptions.filter(d => d.id !== id)); + }; + + return ( + <> + {/* 输入框 */} + {showInput && ( +
+ } + allowClear={!readonly} + size="large" + readOnly={readonly} + disabled={readonly} + style={ + readonly ? { background: "#f5f5f5", cursor: "not-allowed" } : {} + } + /> +
+ )} + {/* 已选账号列表窗口 */} + {showSelectedList && selectedOptions.length > 0 && ( +
+ {selectedOptions.map(acc => ( +
+
+ 【{acc.realName}】 {acc.userName} +
+ {!readonly && ( +
+ ))} +
+ )} + {/* 弹窗 */} + + + ); +} diff --git a/Touchkebao/src/components/AccountSelection/selectionPopup.tsx b/Touchkebao/src/components/AccountSelection/selectionPopup.tsx new file mode 100644 index 00000000..7f4fc98a --- /dev/null +++ b/Touchkebao/src/components/AccountSelection/selectionPopup.tsx @@ -0,0 +1,237 @@ +import React, { useEffect, useMemo, useRef, useState } from "react"; +import { Popup } from "antd-mobile"; +import Layout from "@/components/Layout/Layout"; +import PopupHeader from "@/components/PopuLayout/header"; +import PopupFooter from "@/components/PopuLayout/footer"; +import style from "./index.module.scss"; +import { getAccountList } from "./api"; +import { AccountItem, SelectionPopupProps } from "./data"; + +export default function SelectionPopup({ + visible, + onVisibleChange, + selectedOptions, + onSelect, + readonly = false, + onConfirm, +}: SelectionPopupProps) { + const [accounts, setAccounts] = useState([]); + const [searchQuery, setSearchQuery] = useState(""); + const [currentPage, setCurrentPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + const [totalAccounts, setTotalAccounts] = useState(0); + const [loading, setLoading] = useState(false); + const [tempSelectedOptions, setTempSelectedOptions] = useState( + [], + ); + + // 累积已加载过的账号,确保确认时能返回更完整的对象 + const loadedAccountMapRef = useRef>(new Map()); + + const pageSize = 20; + + const fetchAccounts = async (page: number, keyword: string = "") => { + setLoading(true); + try { + const params: any = { page, limit: pageSize }; + if (keyword.trim()) params.keyword = keyword.trim(); + + const response = await getAccountList(params); + if (response && response.list) { + setAccounts(response.list); + const total: number = response.total || response.list.length || 0; + setTotalAccounts(total); + setTotalPages(Math.max(1, Math.ceil(total / pageSize))); + + // 累积到映射表 + response.list.forEach((acc: AccountItem) => { + loadedAccountMapRef.current.set(acc.id, acc); + }); + } else { + setAccounts([]); + setTotalAccounts(0); + setTotalPages(1); + } + } catch (error) { + console.error("获取账号列表失败:", error); + } finally { + setLoading(false); + } + }; + + const handleAccountToggle = (account: AccountItem) => { + if (readonly || !onSelect) return; + const isSelected = tempSelectedOptions.some(opt => opt.id === account.id); + const next = isSelected + ? tempSelectedOptions.filter(opt => opt.id !== account.id) + : tempSelectedOptions.concat(account); + setTempSelectedOptions(next); + }; + + // 全选当前页 + const handleSelectAllCurrentPage = (checked: boolean) => { + if (readonly) return; + + if (checked) { + // 全选:添加当前页面所有未选中的账号 + const currentPageAccounts = accounts.filter( + account => !tempSelectedOptions.some(a => a.id === account.id), + ); + setTempSelectedOptions(prev => [...prev, ...currentPageAccounts]); + } else { + // 取消全选:移除当前页面的所有账号 + const currentPageAccountIds = accounts.map(a => a.id); + setTempSelectedOptions(prev => + prev.filter(a => !currentPageAccountIds.includes(a.id)), + ); + } + }; + + // 检查当前页是否全选 + const isCurrentPageAllSelected = + accounts.length > 0 && + accounts.every(account => + tempSelectedOptions.some(a => a.id === account.id), + ); + + const handleConfirm = () => { + if (onConfirm) { + onConfirm(tempSelectedOptions); + } + if (onSelect) { + onSelect(tempSelectedOptions); + } + onVisibleChange(false); + }; + + // 弹窗打开时初始化数据 + useEffect(() => { + if (visible) { + setCurrentPage(1); + setSearchQuery(""); + loadedAccountMapRef.current.clear(); + // 复制一份selectedOptions到临时变量 + setTempSelectedOptions([...selectedOptions]); + fetchAccounts(1, ""); + } + }, [visible, selectedOptions]); + + // 搜索防抖 + useEffect(() => { + if (!visible) return; + if (searchQuery === "") return; + const timer = setTimeout(() => { + setCurrentPage(1); + fetchAccounts(1, searchQuery); + }, 500); + return () => clearTimeout(timer); + }, [searchQuery, visible]); + + // 页码变化 + useEffect(() => { + if (!visible) return; + fetchAccounts(currentPage, searchQuery); + }, [currentPage, visible, searchQuery]); + + const selectedIdSet = useMemo( + () => new Set(tempSelectedOptions.map(opt => opt.id)), + [tempSelectedOptions], + ); + + return ( + onVisibleChange(false)} + position="bottom" + bodyStyle={{ height: "100vh" }} + > + fetchAccounts(currentPage, searchQuery)} + /> + } + footer={ + onVisibleChange(false)} + onConfirm={handleConfirm} + isAllSelected={isCurrentPageAllSelected} + onSelectAll={handleSelectAllCurrentPage} + /> + } + > +
+ {loading ? ( +
+
加载中...
+
+ ) : accounts.length > 0 ? ( +
+ {accounts.map(acc => ( + + ))} +
+ ) : ( +
+
+ {searchQuery + ? `没有找到包含"${searchQuery}"的账号` + : "没有找到账号"} +
+
+ )} +
+
+
+ ); +} diff --git a/Touchkebao/src/components/AndroidCompatibilityCheck.tsx b/Touchkebao/src/components/AndroidCompatibilityCheck.tsx new file mode 100644 index 00000000..55bc0cc0 --- /dev/null +++ b/Touchkebao/src/components/AndroidCompatibilityCheck.tsx @@ -0,0 +1,228 @@ +import React, { useEffect, useState } from "react"; + +interface AndroidCompatibilityInfo { + isAndroid: boolean; + androidVersion: number; + chromeVersion: number; + webViewVersion: number; + issues: string[]; + suggestions: string[]; +} + +const AndroidCompatibilityCheck: React.FC = () => { + const [compatibility, setCompatibility] = useState({ + isAndroid: false, + androidVersion: 0, + chromeVersion: 0, + webViewVersion: 0, + issues: [], + suggestions: [], + }); + + useEffect(() => { + const checkAndroidCompatibility = () => { + const ua = navigator.userAgent; + const issues: string[] = []; + const suggestions: string[] = []; + let isAndroid = false; + let androidVersion = 0; + let chromeVersion = 0; + let webViewVersion = 0; + + // 检测Android系统 + if (ua.indexOf("Android") > -1) { + isAndroid = true; + const androidMatch = ua.match(/Android\s+(\d+)/); + if (androidMatch) { + androidVersion = parseInt(androidMatch[1]); + } + + // 检测Chrome版本 + const chromeMatch = ua.match(/Chrome\/(\d+)/); + if (chromeMatch) { + chromeVersion = parseInt(chromeMatch[1]); + } + + // 检测WebView版本 + const webViewMatch = ua.match(/Version\/\d+\.\d+/); + if (webViewMatch) { + const versionMatch = webViewMatch[0].match(/\d+/); + if (versionMatch) { + webViewVersion = parseInt(versionMatch[0]); + } + } + + // Android 7 (API 24) 兼容性检查 + if (androidVersion === 7) { + issues.push("Android 7 系统对ES6+特性支持不完整"); + suggestions.push("建议升级到Android 8+或使用最新版Chrome"); + } + + // Android 6 (API 23) 兼容性检查 + if (androidVersion === 6) { + issues.push("Android 6 系统对现代Web特性支持有限"); + suggestions.push("强烈建议升级系统或使用最新版Chrome"); + } + + // Chrome版本检查 + if (chromeVersion > 0 && chromeVersion < 50) { + issues.push(`Chrome版本过低 (${chromeVersion}),建议升级到50+`); + suggestions.push("请在Google Play商店更新Chrome浏览器"); + } + + // WebView版本检查 + if (webViewVersion > 0 && webViewVersion < 50) { + issues.push(`WebView版本过低 (${webViewVersion}),可能影响应用功能`); + suggestions.push("建议使用Chrome浏览器或更新系统WebView"); + } + + // 检测特定问题 + const features = { + Promise: typeof Promise !== "undefined", + fetch: typeof fetch !== "undefined", + "Array.from": typeof Array.from !== "undefined", + "Object.assign": typeof Object.assign !== "undefined", + "String.includes": typeof String.prototype.includes !== "undefined", + "Array.includes": typeof Array.prototype.includes !== "undefined", + requestAnimationFrame: typeof requestAnimationFrame !== "undefined", + IntersectionObserver: typeof IntersectionObserver !== "undefined", + ResizeObserver: typeof ResizeObserver !== "undefined", + URLSearchParams: typeof URLSearchParams !== "undefined", + TextEncoder: typeof TextEncoder !== "undefined", + AbortController: typeof AbortController !== "undefined", + }; + + Object.entries(features).forEach(([feature, supported]) => { + if (!supported) { + issues.push(`${feature} 特性不支持`); + } + }); + + // 微信内置浏览器检测 + if (ua.indexOf("MicroMessenger") > -1) { + issues.push("微信内置浏览器对某些Web特性支持有限"); + suggestions.push("建议在系统浏览器中打开以获得最佳体验"); + } + + // QQ内置浏览器检测 + if (ua.indexOf("QQ/") > -1) { + issues.push("QQ内置浏览器对某些Web特性支持有限"); + suggestions.push("建议在系统浏览器中打开以获得最佳体验"); + } + } + + setCompatibility({ + isAndroid, + androidVersion, + chromeVersion, + webViewVersion, + issues, + suggestions, + }); + }; + + checkAndroidCompatibility(); + }, []); + + if (!compatibility.isAndroid || compatibility.issues.length === 0) { + return null; + } + + return ( +
+
+ 🚨 Android 兼容性警告 +
+ +
+ 系统版本: Android {compatibility.androidVersion} + {compatibility.chromeVersion > 0 && + ` | Chrome: ${compatibility.chromeVersion}`} + {compatibility.webViewVersion > 0 && + ` | WebView: ${compatibility.webViewVersion}`} +
+ +
+
+ 检测到的问题: +
+
+ {compatibility.issues.map((issue, index) => ( +
+ • {issue} +
+ ))} +
+
+ + {compatibility.suggestions.length > 0 && ( +
+
+ 建议解决方案: +
+
+ {compatibility.suggestions.map((suggestion, index) => ( +
+ • {suggestion} +
+ ))} +
+
+ )} + +
+ 💡 应用已启用兼容模式,但建议升级系统以获得最佳体验 +
+ + +
+ ); +}; + +export default AndroidCompatibilityCheck; diff --git a/Touchkebao/src/components/CompatibilityCheck.tsx b/Touchkebao/src/components/CompatibilityCheck.tsx new file mode 100644 index 00000000..563d4242 --- /dev/null +++ b/Touchkebao/src/components/CompatibilityCheck.tsx @@ -0,0 +1,125 @@ +import React, { useEffect, useState } from "react"; + +interface CompatibilityInfo { + isCompatible: boolean; + browser: string; + version: string; + issues: string[]; +} + +const CompatibilityCheck: React.FC = () => { + const [compatibility, setCompatibility] = useState({ + isCompatible: true, + browser: "", + version: "", + issues: [], + }); + + useEffect(() => { + const checkCompatibility = () => { + const ua = navigator.userAgent; + const issues: string[] = []; + let browser = "Unknown"; + let version = "Unknown"; + + // 检测浏览器类型和版本 + if (ua.indexOf("Chrome") > -1) { + browser = "Chrome"; + const match = ua.match(/Chrome\/(\d+)/); + version = match ? match[1] : "Unknown"; + if (parseInt(version) < 50) { + issues.push("Chrome版本过低,建议升级到50+"); + } + } else if (ua.indexOf("Firefox") > -1) { + browser = "Firefox"; + const match = ua.match(/Firefox\/(\d+)/); + version = match ? match[1] : "Unknown"; + if (parseInt(version) < 50) { + issues.push("Firefox版本过低,建议升级到50+"); + } + } else if (ua.indexOf("Safari") > -1 && ua.indexOf("Chrome") === -1) { + browser = "Safari"; + const match = ua.match(/Version\/(\d+)/); + version = match ? match[1] : "Unknown"; + if (parseInt(version) < 10) { + issues.push("Safari版本过低,建议升级到10+"); + } + } else if (ua.indexOf("MSIE") > -1 || ua.indexOf("Trident") > -1) { + browser = "Internet Explorer"; + const match = ua.match(/(?:MSIE |rv:)(\d+)/); + version = match ? match[1] : "Unknown"; + issues.push("Internet Explorer不受支持,建议使用现代浏览器"); + } else if (ua.indexOf("Edge") > -1) { + browser = "Edge"; + const match = ua.match(/Edge\/(\d+)/); + version = match ? match[1] : "Unknown"; + if (parseInt(version) < 12) { + issues.push("Edge版本过低,建议升级到12+"); + } + } + + // 检测ES6+特性支持 + const features = { + Promise: typeof Promise !== "undefined", + fetch: typeof fetch !== "undefined", + "Array.from": typeof Array.from !== "undefined", + "Object.assign": typeof Object.assign !== "undefined", + "String.includes": typeof String.prototype.includes !== "undefined", + "Array.includes": typeof Array.prototype.includes !== "undefined", + }; + + Object.entries(features).forEach(([feature, supported]) => { + if (!supported) { + issues.push(`${feature} 特性不支持`); + } + }); + + setCompatibility({ + isCompatible: issues.length === 0, + browser, + version, + issues, + }); + }; + + checkCompatibility(); + }, []); + + if (compatibility.isCompatible) { + return null; // 兼容时不需要显示 + } + + return ( +
+
+ 浏览器兼容性警告 +
+
+ 当前浏览器: {compatibility.browser} {compatibility.version} +
+
+ {compatibility.issues.map((issue, index) => ( +
{issue}
+ ))} +
+
+ 建议使用 Chrome 50+、Firefox 50+、Safari 10+ 或 Edge 12+ +
+
+ ); +}; + +export default CompatibilityCheck; diff --git a/Touchkebao/src/components/ContentSelection/api.ts b/Touchkebao/src/components/ContentSelection/api.ts new file mode 100644 index 00000000..a4d4bf3e --- /dev/null +++ b/Touchkebao/src/components/ContentSelection/api.ts @@ -0,0 +1,5 @@ +import request from "@/api/request"; + +export function getContentLibraryList(params: any) { + return request("/v1/content/library/list", params, "GET"); +} diff --git a/Touchkebao/src/components/ContentSelection/data.ts b/Touchkebao/src/components/ContentSelection/data.ts new file mode 100644 index 00000000..1bf8fdb8 --- /dev/null +++ b/Touchkebao/src/components/ContentSelection/data.ts @@ -0,0 +1,21 @@ +// 内容库接口类型 +export interface ContentItem { + id: number; + name: string; + [key: string]: any; +} + +// 组件属性接口 +export interface ContentSelectionProps { + selectedOptions: ContentItem[]; + onSelect: (selectedItems: ContentItem[]) => void; + placeholder?: string; + className?: string; + visible?: boolean; + onVisibleChange?: (visible: boolean) => void; + selectedListMaxHeight?: number; + showInput?: boolean; + showSelectedList?: boolean; + readonly?: boolean; + onConfirm?: (selectedItems: ContentItem[]) => void; +} diff --git a/Touchkebao/src/components/ContentSelection/index.module.scss b/Touchkebao/src/components/ContentSelection/index.module.scss new file mode 100644 index 00000000..f877360a --- /dev/null +++ b/Touchkebao/src/components/ContentSelection/index.module.scss @@ -0,0 +1,117 @@ +.inputWrapper { + position: relative; +} +.selectedListWindow { + margin-top: 8px; + border: 1px solid #e5e6eb; + border-radius: 8px; + background: #fff; +} +.selectedListRow { + display: flex; + align-items: center; + padding: 4px 8px; + border-bottom: 1px solid #f0f0f0; + font-size: 14px; +} +.libraryList { + flex: 1; + overflow-y: auto; +} +.libraryListInner { + display: flex; + flex-direction: column; + gap: 12px; + padding: 16px; +} +.libraryItem { + display: flex; + align-items: flex-start; + gap: 12px; + padding: 16px; + border-radius: 12px; + border: 1px solid #f0f0f0; + background: #fff; + cursor: pointer; + transition: background 0.2s; + &:hover { + background: #f5f6fa; + } +} +.checkboxWrapper { + margin-top: 4px; +} +.checkboxSelected { + width: 20px; + height: 20px; + border-radius: 4px; + background: #1677ff; + display: flex; + align-items: center; + justify-content: center; +} +.checkboxUnselected { + width: 20px; + height: 20px; + border-radius: 4px; + border: 1px solid #e5e6eb; + background: #fff; +} +.checkboxDot { + width: 12px; + height: 12px; + border-radius: 2px; + background: #fff; +} +.libraryInfo { + flex: 1; +} +.libraryHeader { + display: flex; + align-items: center; + justify-content: space-between; +} +.libraryName { + font-weight: 500; + font-size: 16px; + color: #222; +} +.typeTag { + font-size: 12px; + color: #1677ff; + border: 1px solid #1677ff; + border-radius: 12px; + padding: 2px 10px; + margin-left: 8px; + background: #f4f8ff; + font-weight: 500; +} +.libraryMeta { + font-size: 12px; + color: #888; +} +.libraryDesc { + font-size: 13px; + color: #888; + margin-top: 4px; +} +.loadingBox { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} +.loadingText { + color: #888; + font-size: 15px; +} +.emptyBox { + display: flex; + align-items: center; + justify-content: center; + height: 100px; +} +.emptyText { + color: #888; + font-size: 15px; +} diff --git a/Touchkebao/src/components/ContentSelection/index.tsx b/Touchkebao/src/components/ContentSelection/index.tsx new file mode 100644 index 00000000..eadb64bc --- /dev/null +++ b/Touchkebao/src/components/ContentSelection/index.tsx @@ -0,0 +1,145 @@ +import React, { useState } from "react"; +import { SearchOutlined, DeleteOutlined } from "@ant-design/icons"; +import { Button, Input } from "antd"; +import style from "./index.module.scss"; +import { ContentItem, ContentSelectionProps } from "./data"; +import SelectionPopup from "./selectionPopup"; + +const ContentSelection: React.FC = ({ + selectedOptions, + onSelect, + placeholder = "选择内容库", + className = "", + visible, + onVisibleChange, + selectedListMaxHeight = 300, + showInput = true, + showSelectedList = true, + readonly = false, + onConfirm, +}) => { + // 弹窗控制 + const [popupVisible, setPopupVisible] = useState(false); + const realVisible = visible !== undefined ? visible : popupVisible; + const setRealVisible = (v: boolean) => { + if (onVisibleChange) onVisibleChange(v); + if (visible === undefined) setPopupVisible(v); + }; + + // 打开弹窗 + const openPopup = () => { + if (readonly) return; + setRealVisible(true); + }; + + // 获取显示文本 + const getDisplayText = () => { + if (selectedOptions.length === 0) return ""; + return `已选择 ${selectedOptions.length} 个内容库`; + }; + + // 删除已选内容库 + const handleRemoveLibrary = (id: number) => { + if (readonly) return; + onSelect(selectedOptions.filter(c => c.id !== id)); + }; + + // 清除所有已选内容库 + const handleClearAll = () => { + if (readonly) return; + onSelect([]); + }; + + return ( + <> + {/* 输入框 */} + {showInput && ( +
+ } + allowClear={!readonly} + onClear={handleClearAll} + size="large" + readOnly={readonly} + disabled={readonly} + style={ + readonly ? { background: "#f5f5f5", cursor: "not-allowed" } : {} + } + /> +
+ )} + {/* 已选内容库列表窗口 */} + {showSelectedList && selectedOptions.length > 0 && ( +
+ {selectedOptions.map(item => ( +
+
+ {item.name || item.id} +
+ {!readonly && ( +
+ ))} +
+ )} + {/* 弹窗 */} + setRealVisible(false)} + selectedOptions={selectedOptions} + onSelect={onSelect} + onConfirm={onConfirm} + /> + + ); +}; + +export default ContentSelection; diff --git a/Touchkebao/src/components/ContentSelection/selectionPopup.tsx b/Touchkebao/src/components/ContentSelection/selectionPopup.tsx new file mode 100644 index 00000000..e5ee7a31 --- /dev/null +++ b/Touchkebao/src/components/ContentSelection/selectionPopup.tsx @@ -0,0 +1,257 @@ +import React, { useState, useEffect } from "react"; +import { Checkbox, Popup } from "antd-mobile"; +import { getContentLibraryList } from "./api"; +import style from "./index.module.scss"; +import Layout from "@/components/Layout/Layout"; +import PopupHeader from "@/components/PopuLayout/header"; +import PopupFooter from "@/components/PopuLayout/footer"; +import { ContentItem } from "./data"; + +interface SelectionPopupProps { + visible: boolean; + onClose: () => void; + selectedOptions: ContentItem[]; + onSelect: (libraries: ContentItem[]) => void; + onConfirm?: (libraries: ContentItem[]) => void; +} + +const PAGE_SIZE = 10; + +// 类型标签文本 +const getTypeText = (type?: number) => { + if (type === 1) return "文本"; + if (type === 2) return "图片"; + if (type === 3) return "视频"; + return "未知"; +}; + +// 时间格式化 +const formatDate = (dateStr?: string) => { + if (!dateStr) return "-"; + const d = new Date(dateStr); + if (isNaN(d.getTime())) return "-"; + return `${d.getFullYear()}/${(d.getMonth() + 1) + .toString() + .padStart(2, "0")}/${d.getDate().toString().padStart(2, "0")} ${d + .getHours() + .toString() + .padStart(2, "0")}:${d.getMinutes().toString().padStart(2, "0")}:${d + .getSeconds() + .toString() + .padStart(2, "0")}`; +}; + +const SelectionPopup: React.FC = ({ + visible, + onClose, + selectedOptions, + onSelect, + onConfirm, +}) => { + // 内容库数据 + const [libraries, setLibraries] = useState([]); + const [searchQuery, setSearchQuery] = useState(""); + const [loading, setLoading] = useState(true); // 默认设置为加载中状态 + const [currentPage, setCurrentPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + const [totalLibraries, setTotalLibraries] = useState(0); + const [tempSelectedOptions, setTempSelectedOptions] = useState( + [], + ); + + // 获取内容库列表,支持keyword和分页 + const fetchLibraries = async (page: number, keyword: string = "") => { + setLoading(true); + try { + const params: any = { + page, + limit: PAGE_SIZE, + }; + if (keyword.trim()) { + params.keyword = keyword.trim(); + } + const response = await getContentLibraryList(params); + if (response && response.list) { + setLibraries(response.list); + setTotalLibraries(response.total || 0); + setTotalPages(Math.ceil((response.total || 0) / PAGE_SIZE)); + } else { + // 如果没有返回列表数据,设置为空数组 + setLibraries([]); + setTotalLibraries(0); + setTotalPages(1); + } + } catch (error) { + console.error("获取内容库列表失败:", error); + // 请求失败时,设置为空数组 + setLibraries([]); + setTotalLibraries(0); + setTotalPages(1); + } finally { + setTimeout(() => { + setLoading(false); + }); + } + }; + + // 打开弹窗时获取第一页 + useEffect(() => { + if (visible) { + setSearchQuery(""); + setCurrentPage(1); + // 复制一份selectedOptions到临时变量 + setTempSelectedOptions([...selectedOptions]); + // 设置loading状态,避免显示空内容 + setLoading(true); + fetchLibraries(1, ""); + } else { + // 关闭弹窗时重置加载状态,确保下次打开时显示加载中 + setLoading(true); + } + }, [visible, selectedOptions]); + + // 搜索处理函数 + const handleSearch = (query: string) => { + if (!visible) return; + setCurrentPage(1); + fetchLibraries(1, query); + }; + + // 搜索输入变化时的处理 + const handleSearchChange = (query: string) => { + setSearchQuery(query); + }; + + // 翻页处理函数 + const handlePageChange = (page: number) => { + if (!visible || page === currentPage) return; + setCurrentPage(page); + fetchLibraries(page, searchQuery); + }; + + // 处理内容库选择 + const handleLibraryToggle = (library: ContentItem) => { + const newSelected = tempSelectedOptions.some(c => c.id === library.id) + ? tempSelectedOptions.filter(c => c.id !== library.id) + : [...tempSelectedOptions, library]; + setTempSelectedOptions(newSelected); + }; + + // 全选当前页 + const handleSelectAllCurrentPage = (checked: boolean) => { + if (checked) { + // 全选:添加当前页面所有未选中的内容库 + const currentPageLibraries = libraries.filter( + library => !tempSelectedOptions.some(l => l.id === library.id), + ); + setTempSelectedOptions(prev => [...prev, ...currentPageLibraries]); + } else { + // 取消全选:移除当前页面的所有内容库 + const currentPageLibraryIds = libraries.map(l => l.id); + setTempSelectedOptions(prev => + prev.filter(l => !currentPageLibraryIds.includes(l.id)), + ); + } + }; + + // 检查当前页是否全选 + const isCurrentPageAllSelected = + libraries.length > 0 && + libraries.every(library => + tempSelectedOptions.some(l => l.id === library.id), + ); + + // 确认选择 + const handleConfirm = () => { + // 用户点击确认时,才更新实际的selectedOptions + onSelect(tempSelectedOptions); + if (onConfirm) { + onConfirm(tempSelectedOptions); + } + onClose(); + }; + // 渲染内容库列表或空状态提示 + const OptionsList = () => { + return libraries.length > 0 ? ( +
+ {libraries.map(item => ( + + ))} +
+ ) : ( +
+
数据为空
+
+ ); + }; + + return ( + + fetchLibraries(currentPage, searchQuery)} + /> + } + footer={ + + } + > +
+ {loading ? ( +
+
加载中...
+
+ ) : ( + OptionsList() + )} +
+
+
+ ); +}; + +export default SelectionPopup; diff --git a/Touchkebao/src/components/DeviceSelection/api.ts b/Touchkebao/src/components/DeviceSelection/api.ts new file mode 100644 index 00000000..db175dda --- /dev/null +++ b/Touchkebao/src/components/DeviceSelection/api.ts @@ -0,0 +1,10 @@ +import request from "@/api/request"; + +// 获取设备列表 +export function getDeviceList(params: { + page: number; + limit: number; + keyword?: string; +}) { + return request("/v1/devices", params, "GET"); +} diff --git a/Touchkebao/src/components/DeviceSelection/data.ts b/Touchkebao/src/components/DeviceSelection/data.ts new file mode 100644 index 00000000..d002905c --- /dev/null +++ b/Touchkebao/src/components/DeviceSelection/data.ts @@ -0,0 +1,29 @@ +// 设备选择项接口 +export interface DeviceSelectionItem { + id: number; + memo: string; + imei: string; + wechatId: string; + status: "online" | "offline"; + wxid?: string; + nickname?: string; + usedInPlans?: number; + avatar?: string; + totalFriend?: number; +} + +// 组件属性接口 +export interface DeviceSelectionProps { + selectedOptions: DeviceSelectionItem[]; + onSelect: (devices: DeviceSelectionItem[]) => void; + placeholder?: string; + className?: string; + mode?: "input" | "dialog"; // 新增,默认input + open?: boolean; // 仅mode=dialog时生效 + onOpenChange?: (open: boolean) => void; // 仅mode=dialog时生效 + selectedListMaxHeight?: number; // 新增,已选列表最大高度,默认500 + showInput?: boolean; // 新增 + showSelectedList?: boolean; // 新增 + readonly?: boolean; // 新增 + deviceGroups?: any[]; // 传递设备组数据 +} diff --git a/Touchkebao/src/components/DeviceSelection/index.module.scss b/Touchkebao/src/components/DeviceSelection/index.module.scss new file mode 100644 index 00000000..8d004a48 --- /dev/null +++ b/Touchkebao/src/components/DeviceSelection/index.module.scss @@ -0,0 +1,274 @@ +.inputWrapper { + position: relative; +} +.inputIcon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: #bdbdbd; + z-index: 10; + font-size: 18px; +} +.input { + padding-left: 38px !important; + height: 56px; + border-radius: 16px !important; + border: 1px solid #e5e6eb !important; + font-size: 16px; + background: #f8f9fa; +} + +.popupHeader { + padding: 16px; + border-bottom: 1px solid #f0f0f0; +} +.popupTitle { + font-size: 20px; + font-weight: 600; + text-align: center; +} +.popupSearchRow { + display: flex; + align-items: center; + gap: 16px; + padding: 16px; +} +.popupSearchInputWrap { + position: relative; + flex: 1; +} +.popupSearchInput { + padding-left: 36px !important; + border-radius: 12px !important; + height: 44px; + font-size: 15px; + border: 1px solid #e5e6eb !important; + background: #f8f9fa; +} +.statusSelect { + width: 120px; + height: 40px; + border-radius: 8px; + border: 1px solid #e5e6eb; + font-size: 15px; + padding: 0 10px; + background: #fff; +} +.deviceList { + flex: 1; + overflow-y: auto; +} +.deviceListInner { + display: flex; + flex-direction: column; + gap: 12px; + padding: 16px; +} +.deviceItem { + display: flex; + flex-direction: column; + padding: 12px; + background: #fff; + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + transition: all 0.2s ease; + border: 1px solid #f5f5f5; + + &:hover { + transform: translateY(-1px); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); + } +} + +.headerRow { + display: flex; + align-items: center; + gap: 8px; +} + +.checkboxContainer { + flex-shrink: 0; +} + +.imeiText { + font-size: 13px; + color: #666; + font-family: monospace; + flex: 1; +} + +.mainContent { + display: flex; + align-items: center; + gap: 12px; + cursor: pointer; + padding: 8px; + border-radius: 8px; + transition: background-color 0.2s ease; + + &:hover { + background-color: #f8f9fa; + } +} +.deviceCheckbox { + flex-shrink: 0; +} +.deviceInfo { + flex: 1; + min-width: 0; + display: flex; + align-items: center; + gap: 12px; +} +.deviceAvatar { + width: 64px; + height: 64px; + border-radius: 6px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + box-shadow: 0 2px 8px rgba(102, 126, 234, 0.25); + flex-shrink: 0; + + img { + width: 100%; + height: 100%; + object-fit: cover; + } + + .avatarText { + font-size: 18px; + color: #fff; + font-weight: 700; + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + } +} + +.deviceContent { + flex: 1; + min-width: 0; +} + +.deviceInfoRow { + display: flex; + align-items: center; + gap: 6px; + margin-bottom: 6px; +} +.deviceName { + font-size: 16px; + font-weight: 600; + color: #1a1a1a; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.statusOnline { + font-size: 11px; + padding: 1px 6px; + border-radius: 8px; + color: #52c41a; + background: #f6ffed; + border: 1px solid #b7eb8f; + font-weight: 500; +} +.statusOffline { + font-size: 11px; + padding: 1px 6px; + border-radius: 8px; + color: #ff4d4f; + background: #fff2f0; + border: 1px solid #ffccc7; + font-weight: 500; +} +.deviceInfoDetail { + display: flex; + flex-direction: column; + gap: 4px; +} + +.infoItem { + display: flex; + align-items: center; + gap: 8px; +} + +.infoLabel { + font-size: 13px; + color: #666; + min-width: 50px; +} + +.infoValue { + font-size: 13px; + color: #333; + + &.imei { + font-family: monospace; + } + + &.friendCount { + font-weight: 500; + } +} +.loadingBox { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} +.loadingText { + color: #888; + font-size: 15px; +} +.popupFooter { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px; + border-top: 1px solid #f0f0f0; + background: #fff; +} +.selectedCount { + font-size: 14px; + color: #888; +} +.footerBtnGroup { + display: flex; + gap: 12px; +} +.refreshBtn { + width: 36px; + height: 36px; +} +.paginationRow { + border-top: 1px solid #f0f0f0; + padding: 16px; + display: flex; + align-items: center; + justify-content: space-between; + background: #fff; +} +.totalCount { + font-size: 14px; + color: #888; +} +.paginationControls { + display: flex; + align-items: center; + gap: 8px; +} +.pageBtn { + padding: 0 8px; + height: 32px; + min-width: 32px; + border-radius: 16px; +} +.pageInfo { + font-size: 14px; + color: #222; + margin: 0 8px; +} diff --git a/Touchkebao/src/components/DeviceSelection/index.tsx b/Touchkebao/src/components/DeviceSelection/index.tsx new file mode 100644 index 00000000..ba6952cd --- /dev/null +++ b/Touchkebao/src/components/DeviceSelection/index.tsx @@ -0,0 +1,187 @@ +import React, { useState } from "react"; +import { SearchOutlined } from "@ant-design/icons"; +import { Input, Button } from "antd"; +import { DeleteOutlined } from "@ant-design/icons"; +import { DeviceSelectionProps } from "./data"; +import SelectionPopup from "./selectionPopup"; +import style from "./index.module.scss"; + +const DeviceSelection: React.FC = ({ + selectedOptions, + onSelect, + placeholder = "选择设备", + className = "", + mode = "input", + open, + onOpenChange, + selectedListMaxHeight = 300, // 默认300 + showInput = true, + showSelectedList = true, + readonly = false, +}) => { + // 弹窗控制 + const [popupVisible, setPopupVisible] = useState(false); + const isDialog = mode === "dialog"; + const realVisible = isDialog ? !!open : popupVisible; + const setRealVisible = (v: boolean) => { + if (isDialog && onOpenChange) onOpenChange(v); + if (!isDialog) setPopupVisible(v); + }; + + // 打开弹窗 + const openPopup = () => { + if (readonly) return; + setRealVisible(true); + }; + + // 获取显示文本 + const getDisplayText = () => { + if (selectedOptions.length === 0) return ""; + return `已选择 ${selectedOptions.length} 个设备`; + }; + + // 删除已选设备 + const handleRemoveDevice = (id: number) => { + if (readonly) return; + onSelect(selectedOptions.filter(v => v.id !== id)); + }; + + // 清除所有已选设备 + const handleClearAll = () => { + if (readonly) return; + onSelect([]); + }; + + return ( + <> + {/* mode=input 显示输入框,mode=dialog不显示 */} + {mode === "input" && showInput && ( +
+ } + allowClear={!readonly} + onClear={handleClearAll} + size="large" + readOnly={readonly} + disabled={readonly} + style={ + readonly ? { background: "#f5f5f5", cursor: "not-allowed" } : {} + } + /> +
+ )} + {/* 已选设备列表窗口 */} + {mode === "input" && showSelectedList && selectedOptions.length > 0 && ( +
+ {selectedOptions.map(device => ( +
+ {/* 头像 */} +
+ {device.avatar ? ( + 头像 + ) : ( + + {(device.memo || device.wechatId || "设")[0]} + + )} +
+ +
+ {device.memo} - {device.wechatId} +
+ {!readonly && ( +
+ ))} +
+ )} + {/* 弹窗 */} + setRealVisible(false)} + selectedOptions={selectedOptions} + onSelect={onSelect} + /> + + ); +}; + +export default DeviceSelection; diff --git a/Touchkebao/src/components/DeviceSelection/selectionPopup.tsx b/Touchkebao/src/components/DeviceSelection/selectionPopup.tsx new file mode 100644 index 00000000..7a839f70 --- /dev/null +++ b/Touchkebao/src/components/DeviceSelection/selectionPopup.tsx @@ -0,0 +1,274 @@ +import React, { useState, useEffect, useCallback } from "react"; +import { Checkbox, Popup } from "antd-mobile"; +import { getDeviceList } from "./api"; +import style from "./index.module.scss"; +import Layout from "@/components/Layout/Layout"; +import PopupHeader from "@/components/PopuLayout/header"; +import PopupFooter from "@/components/PopuLayout/footer"; +import { DeviceSelectionItem } from "./data"; + +interface SelectionPopupProps { + visible: boolean; + onClose: () => void; + selectedOptions: DeviceSelectionItem[]; + onSelect: (devices: DeviceSelectionItem[]) => void; +} + +const PAGE_SIZE = 20; + +const SelectionPopup: React.FC = ({ + visible, + onClose, + selectedOptions, + onSelect, +}) => { + // 设备数据 + const [devices, setDevices] = useState([]); + const [searchQuery, setSearchQuery] = useState(""); + const [statusFilter, setStatusFilter] = useState("all"); + const [loading, setLoading] = useState(false); + const [currentPage, setCurrentPage] = useState(1); + const [total, setTotal] = useState(0); + const [tempSelectedOptions, setTempSelectedOptions] = useState< + DeviceSelectionItem[] + >([]); + + // 获取设备列表,支持keyword和分页 + const fetchDevices = useCallback( + async (keyword: string = "", page: number = 1) => { + setLoading(true); + try { + const res = await getDeviceList({ + page, + limit: PAGE_SIZE, + keyword: keyword.trim() || undefined, + }); + if (res && Array.isArray(res.list)) { + setDevices( + res.list.map((d: any) => ({ + id: d.id?.toString() || "", + memo: d.memo || d.imei || "", + imei: d.imei || "", + wechatId: d.wechatId || "", + status: d.alive === 1 ? "online" : "offline", + wxid: d.wechatId || "", + nickname: d.nickname || "", + usedInPlans: d.usedInPlans || 0, + avatar: d.avatar || "", + totalFriend: d.totalFriend || 0, + })), + ); + setTotal(res.total || 0); + } + } catch (error) { + console.error("获取设备列表失败:", error); + } finally { + setLoading(false); + } + }, + [], + ); + + // 打开弹窗时获取第一页 + useEffect(() => { + if (visible) { + setSearchQuery(""); + setCurrentPage(1); + // 复制一份selectedOptions到临时变量 + setTempSelectedOptions([...selectedOptions]); + fetchDevices("", 1); + } + }, [visible, fetchDevices, selectedOptions]); + + // 搜索防抖 + useEffect(() => { + if (!visible) return; + const timer = setTimeout(() => { + setCurrentPage(1); + fetchDevices(searchQuery, 1); + }, 500); + return () => clearTimeout(timer); + }, [searchQuery, visible, fetchDevices]); + + // 翻页时重新请求 + useEffect(() => { + if (!visible) return; + fetchDevices(searchQuery, currentPage); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [currentPage]); + + // 过滤设备(只保留状态过滤) + const filteredDevices = devices.filter(device => { + const matchesStatus = + statusFilter === "all" || + (statusFilter === "online" && device.status === "online") || + (statusFilter === "offline" && device.status === "offline"); + return matchesStatus; + }); + + const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE)); + + // 处理设备选择 + const handleDeviceToggle = (device: DeviceSelectionItem) => { + if (tempSelectedOptions.some(v => v.id === device.id)) { + setTempSelectedOptions( + tempSelectedOptions.filter(v => v.id !== device.id), + ); + } else { + const newSelectedOptions = [...tempSelectedOptions, device]; + setTempSelectedOptions(newSelectedOptions); + } + }; + + // 全选当前页 + const handleSelectAllCurrentPage = (checked: boolean) => { + if (checked) { + // 全选:添加当前页面所有未选中的设备 + const currentPageDevices = filteredDevices.filter( + device => !tempSelectedOptions.some(d => d.id === device.id), + ); + setTempSelectedOptions(prev => [...prev, ...currentPageDevices]); + } else { + // 取消全选:移除当前页面的所有设备 + const currentPageDeviceIds = filteredDevices.map(d => d.id); + setTempSelectedOptions(prev => + prev.filter(d => !currentPageDeviceIds.includes(d.id)), + ); + } + }; + + // 检查当前页是否全选 + const isCurrentPageAllSelected = + filteredDevices.length > 0 && + filteredDevices.every(device => + tempSelectedOptions.some(d => d.id === device.id), + ); + + return ( + + fetchDevices(searchQuery, currentPage)} + showTabs={true} + tabsConfig={{ + activeKey: statusFilter, + onChange: setStatusFilter, + tabs: [ + { title: "全部", key: "all" }, + { title: "在线", key: "online" }, + { title: "离线", key: "offline" }, + ], + }} + /> + } + footer={ + { + // 用户点击确认时,才更新实际的selectedOptions + onSelect(tempSelectedOptions); + onClose(); + }} + isAllSelected={isCurrentPageAllSelected} + onSelectAll={handleSelectAllCurrentPage} + /> + } + > +
+ {loading ? ( +
+
加载中...
+
+ ) : ( +
+ {filteredDevices.map(device => ( +
+ {/* 顶部行:选择框和IMEI */} +
+
+ v.id === device.id, + )} + onChange={() => handleDeviceToggle(device)} + className={style.deviceCheckbox} + /> +
+ + IMEI: {device.imei?.toUpperCase()} + +
+ + {/* 主要内容区域:头像和详细信息 */} +
+ {/* 头像 */} +
+ {device.avatar ? ( + 头像 + ) : ( + + {(device.memo || device.wechatId || "设")[0]} + + )} +
+ + {/* 设备信息 */} +
+
+ {device.memo} +
+ {device.status === "online" ? "在线" : "离线"} +
+
+
+
+ 微信号: + + {device.wechatId} + +
+
+ 好友数: + + {device.totalFriend ?? "-"} + +
+
+
+
+
+ ))} +
+ )} +
+
+
+ ); +}; + +export default SelectionPopup; diff --git a/Touchkebao/src/components/EmojiSeclection/EmojiPicker.css b/Touchkebao/src/components/EmojiSeclection/EmojiPicker.css new file mode 100644 index 00000000..cd70ffe3 --- /dev/null +++ b/Touchkebao/src/components/EmojiSeclection/EmojiPicker.css @@ -0,0 +1,167 @@ +/* 表情选择器容器 */ +.emoji-picker-container { + position: relative; + display: inline-block; +} + +/* 默认触发器按钮 */ +.emoji-picker-trigger { + background: none; + font-size: 16px; + padding: 5px; + cursor: pointer; + transition: all 0.2s ease; + border-radius: 5px; +} + +.emoji-picker-trigger:hover { + background-color: #e9e9e9; + border-color: #d0d0d0; +} + +/* 表情选择器面板 */ +.emoji-picker-panel { + position: absolute; + bottom: 100%; + left: 0; + z-index: 1000; + background: white; + border: 1px solid #e0e0e0; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + width: 320px; + max-height: 400px; + overflow: hidden; + margin-bottom: 4px; +} + +/* 分类标签栏 */ +.emoji-categories { + display: flex; + background-color: #f8f9fa; + border-bottom: 1px solid #e0e0e0; + padding: 8px; + gap: 4px; +} + +.category-btn { + background: none; + border: none; + padding: 8px 12px; + border-radius: 6px; + font-size: 16px; + cursor: pointer; + transition: background-color 0.2s ease; + flex: 1; + text-align: center; +} + +.category-btn:hover { + background-color: #e9ecef; +} + +.category-btn.active { + background-color: #007bff; + color: white; +} + +/* 表情网格 */ +.emoji-grid { + display: grid; + grid-template-columns: repeat(8, 1fr); + gap: 4px; + padding: 12px; + max-height: 280px; + overflow-y: auto; +} + +/* 表情项 */ +.emoji-item { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 4px; + cursor: pointer; + transition: background-color 0.2s ease; +} + +.emoji-item:hover { + background-color: #f0f0f0; +} + +.emoji-image { + width: 24px; + height: 24px; + object-fit: contain; +} + +/* 空状态 */ +.emoji-empty { + text-align: center; + padding: 40px 20px; + color: #999; + font-size: 14px; +} + +/* 滚动条样式 */ +.emoji-grid::-webkit-scrollbar { + width: 6px; +} + +.emoji-grid::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 3px; +} + +.emoji-grid::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 3px; +} + +.emoji-grid::-webkit-scrollbar-thumb:hover { + background: #a8a8a8; +} + +/* 响应式设计 */ +@media (max-width: 480px) { + .emoji-picker-panel { + width: 280px; + } + + .emoji-grid { + grid-template-columns: repeat(7, 1fr); + } +} + +/* 深色模式支持 */ +@media (prefers-color-scheme: dark) { + .emoji-picker-panel { + background: #2d3748; + border-color: #4a5568; + color: white; + } + + .emoji-categories { + background-color: #1a202c; + border-bottom-color: #4a5568; + } + + .category-btn:hover { + background-color: #4a5568; + } + + .emoji-item:hover { + background-color: #4a5568; + } + + .emoji-picker-trigger { + border-color: #4a5568; + color: white; + } + + .emoji-picker-trigger:hover { + background-color: #4a5568; + } +} diff --git a/Touchkebao/src/components/EmojiSeclection/EmojiPicker.tsx b/Touchkebao/src/components/EmojiSeclection/EmojiPicker.tsx new file mode 100644 index 00000000..6b5f4932 --- /dev/null +++ b/Touchkebao/src/components/EmojiSeclection/EmojiPicker.tsx @@ -0,0 +1,115 @@ +import React, { useState, useRef, useEffect } from "react"; +import { EmojiCategory, EmojiInfo, getEmojisByCategory } from "./wechatEmoji"; +import "./EmojiPicker.css"; + +interface EmojiPickerProps { + onEmojiSelect: (emoji: EmojiInfo) => void; + trigger?: React.ReactNode; + className?: string; +} + +const EmojiPicker: React.FC = ({ + onEmojiSelect, + trigger, + className = "", +}) => { + const [isOpen, setIsOpen] = useState(false); + const [activeCategory, setActiveCategory] = useState( + EmojiCategory.FACE, + ); + const pickerRef = useRef(null); + + // 分类配置 + const categories = [ + { key: EmojiCategory.FACE, label: "😊", title: "人脸" }, + { key: EmojiCategory.GESTURE, label: "👋", title: "手势" }, + { key: EmojiCategory.ANIMAL, label: "🐷", title: "动物" }, + { key: EmojiCategory.BLESSING, label: "🎉", title: "祝福" }, + { key: EmojiCategory.OTHER, label: "❤️", title: "其他" }, + ]; + + // 获取当前分类的表情 + const currentEmojis = getEmojisByCategory(activeCategory); + + // 点击外部关闭 + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if ( + pickerRef.current && + !pickerRef.current.contains(event.target as Node) + ) { + setIsOpen(false); + } + }; + + if (isOpen) { + document.addEventListener("mousedown", handleClickOutside); + } + + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [isOpen]); + + // 处理表情选择 + const handleEmojiClick = (emoji: EmojiInfo) => { + onEmojiSelect(emoji); + setIsOpen(false); + }; + + // 默认触发器 + const defaultTrigger = 😊; + + return ( +
+ {/* 触发器 */} +
setIsOpen(!isOpen)}>{trigger || defaultTrigger}
+ + {/* 表情选择器面板 */} + {isOpen && ( +
+ {/* 分类标签 */} +
+ {categories.map(category => ( + + ))} +
+ + {/* 表情网格 */} +
+ {currentEmojis.map(emoji => ( +
handleEmojiClick(emoji)} + title={emoji.name} + > + {emoji.name} +
+ ))} +
+ + {/* 空状态 */} + {currentEmojis.length === 0 && ( +
暂无表情
+ )} +
+ )} +
+ ); +}; + +export default EmojiPicker; diff --git a/Touchkebao/src/components/EmojiSeclection/index.ts b/Touchkebao/src/components/EmojiSeclection/index.ts new file mode 100644 index 00000000..d4aa499f --- /dev/null +++ b/Touchkebao/src/components/EmojiSeclection/index.ts @@ -0,0 +1,18 @@ +// 导出主要组件 +export { default as EmojiPicker } from "./EmojiPicker"; + +// 导出表情数据和类型 +export { + EmojiCategory, + type EmojiInfo, + type EmojiName, + getAllEmojis, + getEmojisByCategory, + getEmojiInfo, + getEmojiPath, + searchEmojis, + EMOJI_CATEGORIES, +} from "./wechatEmoji"; + +// 默认导出 +export { default } from "./EmojiPicker"; diff --git a/Touchkebao/src/components/EmojiSeclection/wechatEmoji.ts b/Touchkebao/src/components/EmojiSeclection/wechatEmoji.ts new file mode 100644 index 00000000..5e2f3807 --- /dev/null +++ b/Touchkebao/src/components/EmojiSeclection/wechatEmoji.ts @@ -0,0 +1,858 @@ +/** + * 微信表情包 TypeScript 模块 + * 提供类型安全的表情访问和图片路径获取功能 + */ + +/** + * 表情类别枚举 + */ +export enum EmojiCategory { + /** 人脸表情 */ + FACE = "face", + /** 手势表情 */ + GESTURE = "gesture", + /** 动物表情 */ + ANIMAL = "animal", + /** 祝福表情 */ + BLESSING = "blessing", + /** 其他表情 */ + OTHER = "other", +} + +/** + * 表情信息接口 + */ +export interface EmojiInfo { + /** 表情名称 */ + name: string; + /** 表情类别 */ + category: EmojiCategory; + /** 图片文件路径 */ + path: string; + /** 英文名称(可选) */ + englishName?: string; +} + +/** + * 表情名称类型 + */ +export type EmojiName = + // 人脸表情 + | "微笑" + | "撇嘴" + | "色" + | "发呆" + | "得意" + | "流泪" + | "害羞" + | "闭嘴" + | "睡" + | "大哭" + | "尴尬" + | "发怒" + | "调皮" + | "呲牙" + | "惊讶" + | "难过" + | "囧" + | "抓狂" + | "吐" + | "偷笑" + | "愉快" + | "白眼" + | "傲慢" + | "困" + | "惊恐" + | "憨笑" + | "悠闲" + | "咒骂" + | "疑问" + | "嘘" + | "晕" + | "衰" + | "骷髅" + | "敲打" + | "再见" + | "擦汗" + | "抠鼻" + | "鼓掌" + | "坏笑" + | "右哼哼" + | "鄙视" + | "委屈" + | "快哭了" + | "阴险" + | "亲亲" + | "可怜" + | "笑脸" + | "生病" + | "脸红" + | "破涕为笑" + | "恐惧" + | "失望" + | "无语" + | "嘿哈" + | "捂脸" + | "机智" + | "皱眉" + | "耶" + | "吃瓜" + | "加油" + | "汗" + | "天啊" + | "Emm" + | "社会社会" + | "旺柴" + | "好的" + | "打脸" + | "哇" + | "翻白眼" + | "666" + | "让我看看" + | "叹气" + | "苦涩" + | "裂开" + | "奸笑" + // 手势表情 + | "握手" + | "胜利" + | "抱拳" + | "勾引" + | "拳头" + | "OK" + | "合十" + | "强" + | "拥抱" + | "弱" + // 动物表情 + | "猪头" + | "跳跳" + | "发抖" + | "转圈" + // 祝福表情 + | "庆祝" + | "礼物" + | "红包" + | "發" + | "福" + | "烟花" + | "爆竹" + // 其他表情 + | "嘴唇" + | "爱心" + | "心碎" + | "啤酒" + | "咖啡" + | "蛋糕" + | "凋谢" + | "菜刀" + | "炸弹" + | "便便" + | "太阳" + | "月亮" + | "玫瑰"; + +/** + * 表情数据映射 + * 将表情名称映射到完整的表情信息 + */ +const EMOJI_DATA: Record = { + // 人脸表情 + 微笑: { + name: "微笑", + category: EmojiCategory.FACE, + path: "/assets/face/微笑.png", + }, + 撇嘴: { + name: "撇嘴", + category: EmojiCategory.FACE, + path: "/assets/face/撇嘴.png", + }, + 色: { name: "色", category: EmojiCategory.FACE, path: "/assets/face/色.png" }, + 发呆: { + name: "发呆", + category: EmojiCategory.FACE, + path: "/assets/face/发呆.png", + }, + 得意: { + name: "得意", + category: EmojiCategory.FACE, + path: "/assets/face/得意.png", + }, + 流泪: { + name: "流泪", + category: EmojiCategory.FACE, + path: "/assets/face/流泪.png", + }, + 害羞: { + name: "害羞", + category: EmojiCategory.FACE, + path: "/assets/face/害羞.png", + }, + 闭嘴: { + name: "闭嘴", + category: EmojiCategory.FACE, + path: "/assets/face/闭嘴.png", + }, + 睡: { name: "睡", category: EmojiCategory.FACE, path: "/assets/face/睡.png" }, + 大哭: { + name: "大哭", + category: EmojiCategory.FACE, + path: "/assets/face/大哭.png", + }, + 尴尬: { + name: "尴尬", + category: EmojiCategory.FACE, + path: "/assets/face/尴尬.png", + }, + 发怒: { + name: "发怒", + category: EmojiCategory.FACE, + path: "/assets/face/发怒.png", + }, + 调皮: { + name: "调皮", + category: EmojiCategory.FACE, + path: "/assets/face/调皮.png", + }, + 呲牙: { + name: "呲牙", + category: EmojiCategory.FACE, + path: "/assets/face/呲牙.png", + }, + 惊讶: { + name: "惊讶", + category: EmojiCategory.FACE, + path: "/assets/face/惊讶.png", + }, + 难过: { + name: "难过", + category: EmojiCategory.FACE, + path: "/assets/face/难过.png", + }, + 囧: { name: "囧", category: EmojiCategory.FACE, path: "/assets/face/囧.png" }, + 抓狂: { + name: "抓狂", + category: EmojiCategory.FACE, + path: "/assets/face/抓狂.png", + }, + 吐: { name: "吐", category: EmojiCategory.FACE, path: "/assets/face/吐.png" }, + 偷笑: { + name: "偷笑", + category: EmojiCategory.FACE, + path: "/assets/face/偷笑.png", + }, + 愉快: { + name: "愉快", + category: EmojiCategory.FACE, + path: "/assets/face/愉快.png", + }, + 白眼: { + name: "白眼", + category: EmojiCategory.FACE, + path: "/assets/face/白眼.png", + }, + 傲慢: { + name: "傲慢", + category: EmojiCategory.FACE, + path: "/assets/face/傲慢.png", + }, + 困: { name: "困", category: EmojiCategory.FACE, path: "/assets/face/困.png" }, + 惊恐: { + name: "惊恐", + category: EmojiCategory.FACE, + path: "/assets/face/惊恐.png", + }, + 憨笑: { + name: "憨笑", + category: EmojiCategory.FACE, + path: "/assets/face/憨笑.png", + }, + 悠闲: { + name: "悠闲", + category: EmojiCategory.FACE, + path: "/assets/face/悠闲.png", + }, + 咒骂: { + name: "咒骂", + category: EmojiCategory.FACE, + path: "/assets/face/咒骂.png", + }, + 疑问: { + name: "疑问", + category: EmojiCategory.FACE, + path: "/assets/face/疑问.png", + }, + 嘘: { name: "嘘", category: EmojiCategory.FACE, path: "/assets/face/嘘.png" }, + 晕: { name: "晕", category: EmojiCategory.FACE, path: "/assets/face/晕.png" }, + 衰: { name: "衰", category: EmojiCategory.FACE, path: "/assets/face/衰.png" }, + 骷髅: { + name: "骷髅", + category: EmojiCategory.FACE, + path: "/assets/face/骷髅.png", + }, + 敲打: { + name: "敲打", + category: EmojiCategory.FACE, + path: "/assets/face/敲打.png", + }, + 再见: { + name: "再见", + category: EmojiCategory.FACE, + path: "/assets/face/再见.png", + }, + 擦汗: { + name: "擦汗", + category: EmojiCategory.FACE, + path: "/assets/face/擦汗.png", + }, + 抠鼻: { + name: "抠鼻", + category: EmojiCategory.FACE, + path: "/assets/face/抠鼻.png", + }, + 鼓掌: { + name: "鼓掌", + category: EmojiCategory.FACE, + path: "/assets/face/鼓掌.png", + }, + 坏笑: { + name: "坏笑", + category: EmojiCategory.FACE, + path: "/assets/face/坏笑.png", + }, + 右哼哼: { + name: "右哼哼", + category: EmojiCategory.FACE, + path: "/assets/face/右哼哼.png", + }, + 鄙视: { + name: "鄙视", + category: EmojiCategory.FACE, + path: "/assets/face/鄙视.png", + }, + 委屈: { + name: "委屈", + category: EmojiCategory.FACE, + path: "/assets/face/委屈.png", + }, + 快哭了: { + name: "快哭了", + category: EmojiCategory.FACE, + path: "/assets/face/快哭了.png", + }, + 阴险: { + name: "阴险", + category: EmojiCategory.FACE, + path: "/assets/face/阴险.png", + }, + 亲亲: { + name: "亲亲", + category: EmojiCategory.FACE, + path: "/assets/face/亲亲.png", + }, + 可怜: { + name: "可怜", + category: EmojiCategory.FACE, + path: "/assets/face/可怜.png", + }, + 笑脸: { + name: "笑脸", + category: EmojiCategory.FACE, + path: "/assets/face/笑脸.png", + }, + 生病: { + name: "生病", + category: EmojiCategory.FACE, + path: "/assets/face/生病.png", + }, + 脸红: { + name: "脸红", + category: EmojiCategory.FACE, + path: "/assets/face/脸红.png", + }, + 破涕为笑: { + name: "破涕为笑", + category: EmojiCategory.FACE, + path: "/assets/face/破涕为笑.png", + }, + 恐惧: { + name: "恐惧", + category: EmojiCategory.FACE, + path: "/assets/face/恐惧.png", + }, + 失望: { + name: "失望", + category: EmojiCategory.FACE, + path: "/assets/face/失望.png", + }, + 无语: { + name: "无语", + category: EmojiCategory.FACE, + path: "/assets/face/无语.png", + }, + 嘿哈: { + name: "嘿哈", + category: EmojiCategory.FACE, + path: "/assets/face/嘿哈.png", + }, + 捂脸: { + name: "捂脸", + category: EmojiCategory.FACE, + path: "/assets/face/捂脸.png", + }, + 机智: { + name: "机智", + category: EmojiCategory.FACE, + path: "/assets/face/机智.png", + }, + 皱眉: { + name: "皱眉", + category: EmojiCategory.FACE, + path: "/assets/face/皱眉.png", + }, + 耶: { name: "耶", category: EmojiCategory.FACE, path: "/assets/face/耶.png" }, + 吃瓜: { + name: "吃瓜", + category: EmojiCategory.FACE, + path: "/assets/face/吃瓜.png", + }, + 加油: { + name: "加油", + category: EmojiCategory.FACE, + path: "/assets/face/加油.png", + }, + + 汗: { name: "汗", category: EmojiCategory.FACE, path: "/assets/face/汗.png" }, + 天啊: { + name: "天啊", + category: EmojiCategory.FACE, + path: "/assets/face/天啊.png", + }, + Emm: { + name: "Emm", + category: EmojiCategory.FACE, + path: "/assets/face/Emm.png", + }, + 社会社会: { + name: "社会社会", + category: EmojiCategory.FACE, + path: "/assets/face/社会社会.png", + }, + 旺柴: { + name: "旺柴", + category: EmojiCategory.FACE, + path: "/assets/face/旺柴.png", + }, + 好的: { + name: "好的", + category: EmojiCategory.FACE, + path: "/assets/face/好的.png", + }, + 打脸: { + name: "打脸", + category: EmojiCategory.FACE, + path: "/assets/face/打脸.png", + }, + 哇: { name: "哇", category: EmojiCategory.FACE, path: "/assets/face/哇.png" }, + 翻白眼: { + name: "翻白眼", + category: EmojiCategory.FACE, + path: "/assets/face/翻白眼.png", + }, + "666": { + name: "666", + category: EmojiCategory.FACE, + path: "/assets/face/666.png", + }, + 让我看看: { + name: "让我看看", + category: EmojiCategory.FACE, + path: "/assets/face/让我看看.png", + }, + 叹气: { + name: "叹气", + category: EmojiCategory.FACE, + path: "/assets/face/叹气.png", + }, + 苦涩: { + name: "苦涩", + category: EmojiCategory.FACE, + path: "/assets/face/苦涩.png", + }, + 裂开: { + name: "裂开", + category: EmojiCategory.FACE, + path: "/assets/face/裂开.png", + }, + 奸笑: { + name: "奸笑", + category: EmojiCategory.FACE, + path: "/assets/face/奸笑.png", + }, + + // 手势表情 + 握手: { + name: "握手", + category: EmojiCategory.GESTURE, + path: "/assets/gesture/握手.png", + }, + 胜利: { + name: "胜利", + category: EmojiCategory.GESTURE, + path: "/assets/gesture/胜利.png", + }, + 抱拳: { + name: "抱拳", + category: EmojiCategory.GESTURE, + path: "/assets/gesture/抱拳.png", + }, + 勾引: { + name: "勾引", + category: EmojiCategory.GESTURE, + path: "/assets/gesture/勾引.png", + }, + 拳头: { + name: "拳头", + category: EmojiCategory.GESTURE, + path: "/assets/gesture/拳头.png", + }, + OK: { + name: "OK", + category: EmojiCategory.GESTURE, + path: "/assets/gesture/OK.png", + }, + 合十: { + name: "合十", + category: EmojiCategory.GESTURE, + path: "/assets/gesture/合十.png", + }, + 强: { + name: "强", + category: EmojiCategory.GESTURE, + path: "/assets/gesture/强.png", + }, + 拥抱: { + name: "拥抱", + category: EmojiCategory.GESTURE, + path: "/assets/gesture/拥抱.png", + }, + 弱: { + name: "弱", + category: EmojiCategory.GESTURE, + path: "/assets/gesture/弱.png", + }, + + // 动物表情 + 猪头: { + name: "猪头", + category: EmojiCategory.ANIMAL, + path: "/assets/animal/猪头.png", + }, + 跳跳: { + name: "跳跳", + category: EmojiCategory.ANIMAL, + path: "/assets/animal/跳跳.png", + }, + 发抖: { + name: "发抖", + category: EmojiCategory.ANIMAL, + path: "/assets/animal/发抖.png", + }, + 转圈: { + name: "转圈", + category: EmojiCategory.ANIMAL, + path: "/assets/animal/转圈.png", + }, + + // 祝福表情 + 庆祝: { + name: "庆祝", + category: EmojiCategory.BLESSING, + path: "/assets/blessing/庆祝.png", + }, + 礼物: { + name: "礼物", + category: EmojiCategory.BLESSING, + path: "/assets/blessing/礼物.png", + }, + 红包: { + name: "红包", + category: EmojiCategory.BLESSING, + path: "/assets/blessing/红包.png", + }, + 發: { + name: "發", + category: EmojiCategory.BLESSING, + path: "/assets/blessing/發.png", + }, + 福: { + name: "福", + category: EmojiCategory.BLESSING, + path: "/assets/blessing/福.png", + }, + 烟花: { + name: "烟花", + category: EmojiCategory.BLESSING, + path: "/assets/blessing/烟花.png", + }, + 爆竹: { + name: "爆竹", + category: EmojiCategory.BLESSING, + path: "/assets/blessing/爆竹.png", + }, + + // 其他表情 + 嘴唇: { + name: "嘴唇", + category: EmojiCategory.OTHER, + path: "/assets/other/嘴唇.png", + }, + 爱心: { + name: "爱心", + category: EmojiCategory.OTHER, + path: "/assets/other/爱心.png", + }, + 心碎: { + name: "心碎", + category: EmojiCategory.OTHER, + path: "/assets/other/心碎.png", + }, + 啤酒: { + name: "啤酒", + category: EmojiCategory.OTHER, + path: "/assets/other/啤酒.png", + }, + 咖啡: { + name: "咖啡", + category: EmojiCategory.OTHER, + path: "/assets/other/咖啡.png", + }, + 蛋糕: { + name: "蛋糕", + category: EmojiCategory.OTHER, + path: "/assets/other/蛋糕.png", + }, + 凋谢: { + name: "凋谢", + category: EmojiCategory.OTHER, + path: "/assets/other/凋谢.png", + }, + 菜刀: { + name: "菜刀", + category: EmojiCategory.OTHER, + path: "/assets/other/菜刀.png", + }, + 炸弹: { + name: "炸弹", + category: EmojiCategory.OTHER, + path: "/assets/other/炸弹.png", + }, + 便便: { + name: "便便", + category: EmojiCategory.OTHER, + path: "/assets/other/便便.png", + }, + 太阳: { + name: "太阳", + category: EmojiCategory.OTHER, + path: "/assets/other/太阳.png", + }, + 月亮: { + name: "月亮", + category: EmojiCategory.OTHER, + path: "/assets/other/月亮.png", + }, + 玫瑰: { + name: "玫瑰", + category: EmojiCategory.OTHER, + path: "/assets/other/玫瑰.png", + }, +}; + +/** + * 获取所有表情数据的辅助函数 + */ +function getAllEmojiData(): EmojiInfo[] { + const result: EmojiInfo[] = []; + for (const key in EMOJI_DATA) { + if (Object.prototype.hasOwnProperty.call(EMOJI_DATA, key)) { + result.push(EMOJI_DATA[key as EmojiName]); + } + } + return result; +} + +/** + * 按类别分组的表情数据 + */ +export const EMOJI_CATEGORIES = { + [EmojiCategory.FACE]: getAllEmojiData().filter( + emoji => emoji.category === EmojiCategory.FACE, + ), + [EmojiCategory.GESTURE]: getAllEmojiData().filter( + emoji => emoji.category === EmojiCategory.GESTURE, + ), + [EmojiCategory.ANIMAL]: getAllEmojiData().filter( + emoji => emoji.category === EmojiCategory.ANIMAL, + ), + [EmojiCategory.BLESSING]: getAllEmojiData().filter( + emoji => emoji.category === EmojiCategory.BLESSING, + ), + [EmojiCategory.OTHER]: getAllEmojiData().filter( + emoji => emoji.category === EmojiCategory.OTHER, + ), +} as const; + +/** + * 获取表情图片路径 + * @param name 表情名称 + * @returns 图片路径,如果表情不存在则返回 null + * + * @example + * ```typescript + * const path = getEmojiPath('微笑'); // 'assets/face/微笑.png' + * const invalidPath = getEmojiPath('不存在'); // null + * ``` + */ +export function getEmojiPath(name: EmojiName): string | null { + const emoji = EMOJI_DATA[name]; + return emoji ? emoji.path : null; +} + +/** + * 获取表情信息 + * @param name 表情名称 + * @returns 表情信息对象,如果表情不存在则返回 null + * + * @example + * ```typescript + * const emoji = getEmojiInfo('微笑'); + * // { name: '微笑', category: EmojiCategory.FACE, path: 'assets/face/微笑.png' } + * ``` + */ +export function getEmojiInfo(name: EmojiName): EmojiInfo | null { + return EMOJI_DATA[name] || null; +} + +/** + * 根据类别获取表情列表 + * @param category 表情类别 + * @returns 该类别下的所有表情信息 + * + * @example + * ```typescript + * const faceEmojis = getEmojisByCategory(EmojiCategory.FACE); + * ``` + */ +export function getEmojisByCategory(category: EmojiCategory): EmojiInfo[] { + return EMOJI_CATEGORIES[category]; +} + +/** + * 获取所有表情信息 + * @returns 所有表情的信息数组 + * + * @example + * ```typescript + * const allEmojis = getAllEmojis(); + * console.log(`总共有 ${allEmojis.length} 个表情`); + * ``` + */ +export function getAllEmojis(): EmojiInfo[] { + return getAllEmojiData(); +} + +/** + * 搜索表情 + * @param keyword 搜索关键词 + * @returns 匹配的表情信息数组 + * + * @example + * ```typescript + * const results = searchEmojis('笑'); + * // 返回包含 '微笑', '偷笑', '坏笑' 等的表情 + * ``` + */ +export function searchEmojis(keyword: string): EmojiInfo[] { + return getAllEmojiData().filter(emoji => emoji.name.indexOf(keyword) !== -1); +} + +/** + * 检查表情是否存在 + * @param name 表情名称 + * @returns 是否存在该表情 + * + * @example + * ```typescript + * const exists = hasEmoji('微笑'); // true + * const notExists = hasEmoji('不存在的表情'); // false + * ``` + */ +export function hasEmoji(name: EmojiName): boolean { + return name in EMOJI_DATA; +} + +/** + * 获取表情名称列表 + * @param category 可选的类别筛选 + * @returns 表情名称数组 + * + * @example + * ```typescript + * const allNames = getEmojiNames(); + * const faceNames = getEmojiNames(EmojiCategory.FACE); + * ``` + */ +export function getEmojiNames(category?: EmojiCategory): string[] { + if (category) { + return getEmojisByCategory(category).map(emoji => emoji.name); + } + const names: string[] = []; + for (const key in EMOJI_DATA) { + if (Object.prototype.hasOwnProperty.call(EMOJI_DATA, key)) { + names.push(key); + } + } + return names; +} + +/** + * 随机获取表情 + * @param category 可选的类别筛选 + * @returns 随机表情信息 + * + * @example + * ```typescript + * const randomEmoji = getRandomEmoji(); + * const randomFaceEmoji = getRandomEmoji(EmojiCategory.FACE); + * ``` + */ +export function getRandomEmoji(category?: EmojiCategory): EmojiInfo { + const emojis = category ? getEmojisByCategory(category) : getAllEmojis(); + const randomIndex = Math.floor(Math.random() * emojis.length); + return emojis[randomIndex]; +} + +/** + * 默认导出对象,包含所有主要功能 + */ +const WeChatEmojis = { + // 枚举和类型 + EmojiCategory, + + // 数据 + EMOJI_CATEGORIES, + + // 工具函数 + getEmojiPath, + getEmojiInfo, + getEmojisByCategory, + getAllEmojis, + searchEmojis, + hasEmoji, + getEmojiNames, + getRandomEmoji, +} as const; + +export default WeChatEmojis; diff --git a/Touchkebao/src/components/FriendSelection/api.ts b/Touchkebao/src/components/FriendSelection/api.ts new file mode 100644 index 00000000..346253ef --- /dev/null +++ b/Touchkebao/src/components/FriendSelection/api.ts @@ -0,0 +1,11 @@ +import request from "@/api/request"; + +// 获取好友列表 +export function getFriendList(params: { + page: number; + limit: number; + deviceIds?: string; // 逗号分隔 + keyword?: string; +}) { + return request("/v1/friend", params, "GET"); +} diff --git a/Touchkebao/src/components/FriendSelection/data.ts b/Touchkebao/src/components/FriendSelection/data.ts new file mode 100644 index 00000000..434aa641 --- /dev/null +++ b/Touchkebao/src/components/FriendSelection/data.ts @@ -0,0 +1,27 @@ +export interface FriendSelectionItem { + id: number; + wechatId: string; + nickname: string; + avatar: string; + [key: string]: any; +} + +// 组件属性接口 +export interface FriendSelectionProps { + selectedOptions?: FriendSelectionItem[]; + onSelect: (friends: FriendSelectionItem[]) => void; + deviceIds?: number[]; + enableDeviceFilter?: boolean; + placeholder?: string; + className?: string; + visible?: boolean; // 新增 + onVisibleChange?: (visible: boolean) => void; // 新增 + selectedListMaxHeight?: number; + showInput?: boolean; + showSelectedList?: boolean; + readonly?: boolean; + onConfirm?: ( + selectedIds: number[], + selectedItems: FriendSelectionItem[], + ) => void; // 新增 +} diff --git a/Touchkebao/src/components/FriendSelection/index.module.scss b/Touchkebao/src/components/FriendSelection/index.module.scss new file mode 100644 index 00000000..f450dde2 --- /dev/null +++ b/Touchkebao/src/components/FriendSelection/index.module.scss @@ -0,0 +1,246 @@ +.inputWrapper { + position: relative; +} +.selectedListRow { + padding: 8px; + border-bottom: 1px solid #f0f0f0; + font-size: 14px; +} +.selectedListRowContent { + flex: 1; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} +.selectedListRowContentText { + flex: 1; +} +.inputIcon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: #bdbdbd; + font-size: 20px; +} +.input { + padding-left: 38px !important; + height: 48px; + border-radius: 16px !important; + border: 1px solid #e5e6eb !important; + font-size: 16px; + background: #f8f9fa; +} + +.popupContainer { + display: flex; + flex-direction: column; + height: 100vh; + background: #fff; +} +.popupHeader { + padding: 24px; +} +.popupTitle { + text-align: center; + font-size: 20px; + font-weight: 600; + margin-bottom: 24px; +} +.searchWrapper { + position: relative; + margin-bottom: 16px; +} +.searchInput { + padding-left: 40px !important; + padding-top: 8px !important; + padding-bottom: 8px !important; + border-radius: 24px !important; + border: 1px solid #e5e6eb !important; + font-size: 15px; + background: #f8f9fa; +} +.searchIcon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: #bdbdbd; + font-size: 16px; +} +.clearBtn { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + height: 24px; + width: 24px; + border-radius: 50%; + min-width: 24px; +} + +.friendList { + flex: 1; + overflow-y: auto; +} +.friendListInner { + border-top: 1px solid #f0f0f0; +} +.friendItem { + display: flex; + align-items: center; + padding: 16px 24px; + border-bottom: 1px solid #f0f0f0; + cursor: pointer; + transition: background 0.2s; + &:hover { + background: #f5f6fa; + } +} +.radioWrapper { + margin-right: 12px; + display: flex; + align-items: center; + justify-content: center; +} +.radioSelected { + width: 20px; + height: 20px; + border-radius: 50%; + border: 2px solid #1890ff; + display: flex; + align-items: center; + justify-content: center; +} +.radioUnselected { + width: 20px; + height: 20px; + border-radius: 50%; + border: 2px solid #e5e6eb; + display: flex; + align-items: center; + justify-content: center; +} +.radioDot { + width: 12px; + height: 12px; + border-radius: 50%; + background: #1890ff; +} +.friendInfo { + display: flex; + align-items: center; + gap: 12px; + flex: 1; +} +.friendAvatar { + width: 40px; + height: 40px; + border-radius: 50%; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + display: flex; + align-items: center; + justify-content: center; + color: #fff; + font-size: 14px; + font-weight: 500; + overflow: hidden; +} +.avatarImg { + width: 100%; + height: 100%; + object-fit: cover; +} +.friendDetail { + flex: 1; +} +.friendName { + font-weight: 500; + font-size: 16px; + color: #222; + margin-bottom: 2px; +} +.friendId { + font-size: 13px; + color: #888; + margin-bottom: 2px; +} +.friendCustomer { + font-size: 13px; + color: #bdbdbd; +} + +.loadingBox { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} +.loadingText { + color: #888; + font-size: 15px; +} +.emptyBox { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} +.emptyText { + color: #888; + font-size: 15px; +} + +.paginationRow { + border-top: 1px solid #f0f0f0; + padding: 16px; + display: flex; + align-items: center; + justify-content: space-between; + background: #fff; +} +.totalCount { + font-size: 14px; + color: #888; +} +.paginationControls { + display: flex; + align-items: center; + gap: 8px; +} +.pageBtn { + padding: 0 8px; + height: 32px; + min-width: 32px; +} +.pageInfo { + font-size: 14px; + color: #222; +} + +.popupFooter { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px; + border-top: 1px solid #f0f0f0; + background: #fff; +} +.selectedCount { + font-size: 14px; + color: #888; +} +.footerBtnGroup { + display: flex; + gap: 12px; +} +.cancelBtn { + padding: 0 24px; + border-radius: 24px; + border: 1px solid #e5e6eb; +} +.confirmBtn { + padding: 0 24px; + border-radius: 24px; +} diff --git a/Touchkebao/src/components/FriendSelection/index.tsx b/Touchkebao/src/components/FriendSelection/index.tsx new file mode 100644 index 00000000..63c04a45 --- /dev/null +++ b/Touchkebao/src/components/FriendSelection/index.tsx @@ -0,0 +1,140 @@ +import React, { useState } from "react"; +import { SearchOutlined, DeleteOutlined } from "@ant-design/icons"; +import { Button, Input } from "antd"; +import { Avatar } from "antd-mobile"; +import style from "./index.module.scss"; +import { FriendSelectionProps } from "./data"; +import SelectionPopup from "./selectionPopup"; + +export default function FriendSelection({ + selectedOptions = [], + onSelect, + deviceIds = [], + enableDeviceFilter = true, + placeholder = "选择微信好友", + className = "", + visible, + onVisibleChange, + selectedListMaxHeight = 300, + showInput = true, + showSelectedList = true, + readonly = false, + onConfirm, +}: FriendSelectionProps) { + const [popupVisible, setPopupVisible] = useState(false); + // 内部弹窗交给 selectionPopup 处理 + + // 受控弹窗逻辑 + const realVisible = visible !== undefined ? visible : popupVisible; + const setRealVisible = (v: boolean) => { + if (onVisibleChange) onVisibleChange(v); + if (visible === undefined) setPopupVisible(v); + }; + + // 打开弹窗 + const openPopup = () => { + if (readonly) return; + setRealVisible(true); + }; + + // 获取显示文本 + const getDisplayText = () => { + if (!selectedOptions || selectedOptions.length === 0) return ""; + return `已选择 ${selectedOptions.length} 个好友`; + }; + + // 删除已选好友 + const handleRemoveFriend = (id: number) => { + if (readonly) return; + onSelect((selectedOptions || []).filter(v => v.id !== id)); + }; + + // 弹窗确认回调 + const handleConfirm = ( + selectedIds: number[], + selectedItems: typeof selectedOptions, + ) => { + onSelect(selectedItems); + if (onConfirm) onConfirm(selectedIds, selectedItems); + setRealVisible(false); + }; + + return ( + <> + {/* 输入框 */} + {showInput && ( +
+ } + allowClear={!readonly} + size="large" + readOnly={readonly} + disabled={readonly} + style={ + readonly ? { background: "#f5f5f5", cursor: "not-allowed" } : {} + } + /> +
+ )} + {/* 已选好友列表窗口 */} + {showSelectedList && (selectedOptions || []).length > 0 && ( +
+ {(selectedOptions || []).map(friend => ( +
+
+ +
+
{friend.nickname}
+
{friend.wechatId}
+
+ {!readonly && ( +
+
+ ))} +
+ )} + {/* 弹窗 */} + + + ); +} diff --git a/Touchkebao/src/components/FriendSelection/selectionPopup.tsx b/Touchkebao/src/components/FriendSelection/selectionPopup.tsx new file mode 100644 index 00000000..71020104 --- /dev/null +++ b/Touchkebao/src/components/FriendSelection/selectionPopup.tsx @@ -0,0 +1,245 @@ +import React, { useCallback, useEffect, useState } from "react"; +import { Popup, Checkbox } from "antd-mobile"; +import Layout from "@/components/Layout/Layout"; +import PopupHeader from "@/components/PopuLayout/header"; +import PopupFooter from "@/components/PopuLayout/footer"; +import { getFriendList } from "./api"; +import style from "./index.module.scss"; +import type { FriendSelectionItem } from "./data"; + +interface SelectionPopupProps { + visible: boolean; + onVisibleChange: (visible: boolean) => void; + selectedOptions: FriendSelectionItem[]; + onSelect: (friends: FriendSelectionItem[]) => void; + deviceIds?: number[]; + enableDeviceFilter?: boolean; + readonly?: boolean; + onConfirm?: ( + selectedIds: number[], + selectedItems: FriendSelectionItem[], + ) => void; +} + +const SelectionPopup: React.FC = ({ + visible, + onVisibleChange, + selectedOptions, + onSelect, + deviceIds = [], + enableDeviceFilter = true, + readonly = false, + onConfirm, +}) => { + const [friends, setFriends] = useState([]); + const [searchQuery, setSearchQuery] = useState(""); + const [currentPage, setCurrentPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + const [totalFriends, setTotalFriends] = useState(0); + const [loading, setLoading] = useState(false); + const [tempSelectedOptions, setTempSelectedOptions] = useState< + FriendSelectionItem[] + >([]); + + // 获取好友列表API + const fetchFriends = useCallback( + async (page: number, keyword: string = "") => { + setLoading(true); + try { + const params: any = { + page, + limit: 20, + }; + + if (keyword.trim()) { + params.keyword = keyword.trim(); + } + + if (enableDeviceFilter && deviceIds.length > 0) { + params.deviceIds = deviceIds.join(","); + } + + const response = await getFriendList(params); + if (response && response.list) { + setFriends(response.list); + setTotalFriends(response.total || 0); + setTotalPages(Math.ceil((response.total || 0) / 20)); + } + } catch (error) { + console.error("获取好友列表失败:", error); + } finally { + setLoading(false); + } + }, + [deviceIds, enableDeviceFilter], + ); + + // 处理好友选择 + const handleFriendToggle = (friend: FriendSelectionItem) => { + if (readonly) return; + + const newSelectedFriends = tempSelectedOptions.some(f => f.id === friend.id) + ? tempSelectedOptions.filter(f => f.id !== friend.id) + : tempSelectedOptions.concat(friend); + + setTempSelectedOptions(newSelectedFriends); + }; + + // 全选当前页 + const handleSelectAllCurrentPage = (checked: boolean) => { + if (readonly) return; + + if (checked) { + // 全选:添加当前页面所有未选中的好友 + const currentPageFriends = friends.filter( + friend => !tempSelectedOptions.some(f => f.id === friend.id), + ); + setTempSelectedOptions(prev => [...prev, ...currentPageFriends]); + } else { + // 取消全选:移除当前页面的所有好友 + const currentPageFriendIds = friends.map(f => f.id); + setTempSelectedOptions(prev => + prev.filter(f => !currentPageFriendIds.includes(f.id)), + ); + } + }; + + // 检查当前页是否全选 + const isCurrentPageAllSelected = + friends.length > 0 && + friends.every(friend => tempSelectedOptions.some(f => f.id === friend.id)); + + // 确认选择 + const handleConfirm = () => { + if (onConfirm) { + onConfirm( + tempSelectedOptions.map(v => v.id), + tempSelectedOptions, + ); + } + // 更新实际选中的选项 + onSelect(tempSelectedOptions); + onVisibleChange(false); + }; + + // 弹窗打开时初始化 + useEffect(() => { + if (visible) { + setCurrentPage(1); + setSearchQuery(""); + // 复制一份selectedOptions到临时变量 + setTempSelectedOptions([...selectedOptions]); + fetchFriends(1, ""); + } + }, [visible, selectedOptions]); // 只在弹窗开启时请求 + + // 搜索防抖(只在弹窗打开且搜索词变化时执行) + useEffect(() => { + if (!visible || searchQuery === "") return; // 弹窗关闭或搜索词为空时不请求 + + const timer = setTimeout(() => { + setCurrentPage(1); + fetchFriends(1, searchQuery); + }, 500); + + return () => clearTimeout(timer); + }, [searchQuery, visible]); + + // 页码变化时请求数据(只在弹窗打开且页码不是1时执行) + useEffect(() => { + if (!visible) return; // 弹窗关闭或第一页时不请求 + fetchFriends(currentPage, searchQuery); + }, [currentPage, visible, searchQuery]); + + return ( + onVisibleChange(false)} + position="bottom" + bodyStyle={{ height: "100vh" }} + > + fetchFriends(currentPage, searchQuery)} + /> + } + footer={ + onVisibleChange(false)} + onConfirm={handleConfirm} + isAllSelected={isCurrentPageAllSelected} + onSelectAll={handleSelectAllCurrentPage} + /> + } + > +
+ {loading ? ( +
+
加载中...
+
+ ) : friends.length > 0 ? ( +
+ {friends.map(friend => ( +
+ f.id === friend.id)} + onChange={() => !readonly && handleFriendToggle(friend)} + disabled={readonly} + style={{ marginRight: 12 }} + /> +
+
+ {friend.avatar ? ( + {friend.nickname} + ) : ( + friend.nickname.charAt(0) + )} +
+
+
{friend.nickname}
+
+ 微信ID: {friend.wechatId} +
+ {friend.customer && ( +
+ 归属客户: {friend.customer} +
+ )} +
+
+
+ ))} +
+ ) : ( +
+
+ {deviceIds.length === 0 + ? "请先选择设备" + : searchQuery + ? `没有找到包含"${searchQuery}"的好友` + : "没有找到好友"} +
+
+ )} +
+
+
+ ); +}; + +export default SelectionPopup; diff --git a/Touchkebao/src/components/GroupSelection/api.ts b/Touchkebao/src/components/GroupSelection/api.ts new file mode 100644 index 00000000..cda84b81 --- /dev/null +++ b/Touchkebao/src/components/GroupSelection/api.ts @@ -0,0 +1,10 @@ +import request from "@/api/request"; + +// 获取群组列表 +export function getGroupList(params: { + page: number; + limit: number; + keyword?: string; +}) { + return request("/v1/chatroom", params, "GET"); +} diff --git a/Touchkebao/src/components/GroupSelection/data.ts b/Touchkebao/src/components/GroupSelection/data.ts new file mode 100644 index 00000000..b042b6d3 --- /dev/null +++ b/Touchkebao/src/components/GroupSelection/data.ts @@ -0,0 +1,43 @@ +// 群组接口类型 +export interface WechatGroup { + id: string; + chatroomId: string; + name: string; + avatar: string; + ownerWechatId: string; + ownerNickname: string; + ownerAvatar: string; +} + +export interface GroupSelectionItem { + id: string; + avatar: string; + chatroomId?: string; + createTime?: number; + identifier?: string; + name: string; + ownerAlias?: string; + ownerAvatar?: string; + ownerNickname?: string; + ownerWechatId?: string; + [key: string]: any; +} + +// 组件属性接口 +export interface GroupSelectionProps { + selectedOptions: GroupSelectionItem[]; + onSelect: (groups: GroupSelectionItem[]) => void; + onSelectDetail?: (groups: WechatGroup[]) => void; + placeholder?: string; + className?: string; + visible?: boolean; + onVisibleChange?: (visible: boolean) => void; + selectedListMaxHeight?: number; + showInput?: boolean; + showSelectedList?: boolean; + readonly?: boolean; + onConfirm?: ( + selectedIds: string[], + selectedItems: GroupSelectionItem[], + ) => void; // 新增 +} diff --git a/Touchkebao/src/components/GroupSelection/index.module.scss b/Touchkebao/src/components/GroupSelection/index.module.scss new file mode 100644 index 00000000..bedba3ef --- /dev/null +++ b/Touchkebao/src/components/GroupSelection/index.module.scss @@ -0,0 +1,206 @@ +.inputWrapper { + position: relative; +} +.inputIcon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: #bdbdbd; + font-size: 20px; +} +.input { + padding-left: 38px !important; + height: 48px; + border-radius: 16px !important; + border: 1px solid #e5e6eb !important; + font-size: 16px; + background: #f8f9fa; +} +.selectedListRow { + padding: 8px; + border-bottom: 1px solid #f0f0f0; + font-size: 14px; +} +.selectedListRowContent { + flex: 1; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} +.selectedListRowContentText { + flex: 1; +} + +.popupContainer { + display: flex; + flex-direction: column; + height: 100vh; + background: #fff; +} +.popupHeader { + padding: 24px; +} +.popupTitle { + text-align: center; + font-size: 20px; + font-weight: 600; + margin-bottom: 24px; +} +.searchWrapper { + position: relative; + margin-bottom: 16px; +} +.searchInput { + padding-left: 40px !important; + padding-top: 8px !important; + padding-bottom: 8px !important; + border-radius: 24px !important; + border: 1px solid #e5e6eb !important; + font-size: 15px; + background: #f8f9fa; +} +.searchIcon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: #bdbdbd; + font-size: 16px; +} +.clearBtn { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + height: 24px; + width: 24px; + border-radius: 50%; + min-width: 24px; +} + +.groupList { + flex: 1; + overflow-y: auto; +} +.groupListInner { + border-top: 1px solid #f0f0f0; +} +.groupItem { + display: flex; + align-items: center; + padding: 16px 24px; + border-bottom: 1px solid #f0f0f0; + transition: background 0.2s; + &:hover { + background: #f5f6fa; + } +} +.groupInfo { + display: flex; + align-items: center; + gap: 12px; + flex: 1; +} +.groupAvatar { + width: 40px; + height: 40px; + border-radius: 50%; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + display: flex; + align-items: center; + justify-content: center; + color: #fff; + font-size: 14px; + font-weight: 500; + overflow: hidden; +} +.avatarImg { + width: 100%; + height: 100%; + object-fit: cover; +} +.groupDetail { + flex: 1; +} +.groupName { + font-weight: 500; + font-size: 16px; + color: #222; + margin-bottom: 2px; +} +.groupId { + font-size: 13px; + color: #888; + margin-bottom: 2px; +} +.groupOwner { + font-size: 13px; + color: #bdbdbd; +} + +.loadingBox { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} +.loadingText { + color: #888; + font-size: 15px; +} +.emptyBox { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} +.emptyText { + color: #888; + font-size: 15px; +} + +.paginationRow { + border-top: 1px solid #f0f0f0; + padding: 16px; + display: flex; + align-items: center; + justify-content: space-between; + background: #fff; +} +.totalCount { + font-size: 14px; + color: #888; +} +.paginationControls { + display: flex; + align-items: center; + gap: 8px; +} +.pageBtn { + padding: 0 8px; + height: 32px; + min-width: 32px; +} +.pageInfo { + font-size: 14px; + color: #222; +} + +.popupFooter { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px; + border-top: 1px solid #f0f0f0; + background: #fff; +} +.selectedCount { + font-size: 14px; + color: #888; +} +.footerBtnGroup { + display: flex; + gap: 12px; +} diff --git a/Touchkebao/src/components/GroupSelection/index.tsx b/Touchkebao/src/components/GroupSelection/index.tsx new file mode 100644 index 00000000..fe180054 --- /dev/null +++ b/Touchkebao/src/components/GroupSelection/index.tsx @@ -0,0 +1,126 @@ +import React, { useState } from "react"; +import { SearchOutlined, DeleteOutlined } from "@ant-design/icons"; +import { Button, Input } from "antd"; +import { Avatar } from "antd-mobile"; +import style from "./index.module.scss"; +import SelectionPopup from "./selectionPopup"; +import { GroupSelectionProps } from "./data"; +export default function GroupSelection({ + selectedOptions, + onSelect, + onSelectDetail, + placeholder = "选择群聊", + className = "", + visible, + onVisibleChange, + selectedListMaxHeight = 300, + showInput = true, + showSelectedList = true, + readonly = false, + onConfirm, +}: GroupSelectionProps) { + const [popupVisible, setPopupVisible] = useState(false); + + // 删除已选群聊 + const handleRemoveGroup = (id: string) => { + if (readonly) return; + onSelect(selectedOptions.filter(g => g.id !== id)); + }; + + // 受控弹窗逻辑 + const realVisible = visible !== undefined ? visible : popupVisible; + const setRealVisible = (v: boolean) => { + if (onVisibleChange) onVisibleChange(v); + if (visible === undefined) setPopupVisible(v); + }; + + // 打开弹窗 + const openPopup = () => { + if (readonly) return; + setRealVisible(true); + }; + + // 获取显示文本 + const getDisplayText = () => { + if (selectedOptions.length === 0) return ""; + return `已选择 ${selectedOptions.length} 个群聊`; + }; + + return ( + <> + {/* 输入框 */} + {showInput && ( +
+ } + allowClear={!readonly} + size="large" + readOnly={readonly} + disabled={readonly} + style={ + readonly ? { background: "#f5f5f5", cursor: "not-allowed" } : {} + } + /> +
+ )} + {/* 已选群聊列表窗口 */} + {showSelectedList && selectedOptions.length > 0 && ( +
+ {selectedOptions.map(group => ( +
+
+ +
+
{group.name}
+
{group.chatroomId}
+
+ {!readonly && ( +
+
+ ))} +
+ )} + {/* 弹窗 */} + + + ); +} diff --git a/Touchkebao/src/components/GroupSelection/selectionPopup.tsx b/Touchkebao/src/components/GroupSelection/selectionPopup.tsx new file mode 100644 index 00000000..dd5c9927 --- /dev/null +++ b/Touchkebao/src/components/GroupSelection/selectionPopup.tsx @@ -0,0 +1,253 @@ +import React, { useState, useEffect } from "react"; +import { Popup, Checkbox } from "antd-mobile"; + +import { getGroupList } from "./api"; +import style from "./index.module.scss"; +import Layout from "@/components/Layout/Layout"; +import PopupHeader from "@/components/PopuLayout/header"; +import PopupFooter from "@/components/PopuLayout/footer"; +import { GroupSelectionItem } from "./data"; +// 群组接口类型 +interface WechatGroup { + id: string; + name: string; + avatar: string; + chatroomId?: string; + ownerWechatId?: string; + ownerNickname?: string; + ownerAvatar?: string; +} + +// 弹窗属性接口 +interface SelectionPopupProps { + visible: boolean; + onVisibleChange: (visible: boolean) => void; + selectedOptions: GroupSelectionItem[]; + onSelect: (groups: GroupSelectionItem[]) => void; + onSelectDetail?: (groups: WechatGroup[]) => void; + readonly?: boolean; + onConfirm?: ( + selectedIds: string[], + selectedItems: GroupSelectionItem[], + ) => void; +} + +export default function SelectionPopup({ + visible, + onVisibleChange, + selectedOptions, + onSelect, + onSelectDetail, + readonly = false, + onConfirm, +}: SelectionPopupProps) { + const [groups, setGroups] = useState([]); + const [searchQuery, setSearchQuery] = useState(""); + const [currentPage, setCurrentPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + const [totalGroups, setTotalGroups] = useState(0); + const [loading, setLoading] = useState(false); + const [tempSelectedOptions, setTempSelectedOptions] = useState< + GroupSelectionItem[] + >([]); + + // 获取群聊列表API + const fetchGroups = async (page: number, keyword: string = "") => { + setLoading(true); + try { + const params: any = { + page, + limit: 20, + }; + + if (keyword.trim()) { + params.keyword = keyword.trim(); + } + + const response = await getGroupList(params); + if (response && response.list) { + setGroups(response.list); + setTotalGroups(response.total || 0); + setTotalPages(Math.ceil((response.total || 0) / 20)); + } + } catch (error) { + console.error("获取群聊列表失败:", error); + } finally { + setLoading(false); + } + }; + + // 处理群聊选择 + const handleGroupToggle = (group: GroupSelectionItem) => { + if (readonly) return; + + const newSelectedGroups = tempSelectedOptions.some(g => g.id === group.id) + ? tempSelectedOptions.filter(g => g.id !== group.id) + : tempSelectedOptions.concat(group); + + setTempSelectedOptions(newSelectedGroups); + }; + + // 全选当前页 + const handleSelectAllCurrentPage = (checked: boolean) => { + if (readonly) return; + + if (checked) { + // 全选:添加当前页面所有未选中的群组 + const currentPageGroups = groups.filter( + group => !tempSelectedOptions.some(g => g.id === group.id), + ); + setTempSelectedOptions(prev => [...prev, ...currentPageGroups]); + } else { + // 取消全选:移除当前页面的所有群组 + const currentPageGroupIds = groups.map(g => g.id); + setTempSelectedOptions(prev => + prev.filter(g => !currentPageGroupIds.includes(g.id)), + ); + } + }; + + // 检查当前页是否全选 + const isCurrentPageAllSelected = + groups.length > 0 && + groups.every(group => tempSelectedOptions.some(g => g.id === group.id)); + + // 确认选择 + const handleConfirm = () => { + // 用户点击确认时,才更新实际的selectedOptions + onSelect(tempSelectedOptions); + + // 如果有 onSelectDetail 回调,传递完整的群聊对象 + if (onSelectDetail) { + const selectedGroupObjs = groups.filter(group => + tempSelectedOptions.some(g => g.id === group.id), + ); + onSelectDetail(selectedGroupObjs); + } + + if (onConfirm) { + onConfirm( + tempSelectedOptions.map(g => g.id), + tempSelectedOptions, + ); + } + onVisibleChange(false); + }; + + // 弹窗打开时初始化数据(只执行一次) + useEffect(() => { + if (visible) { + setCurrentPage(1); + setSearchQuery(""); + // 复制一份selectedOptions到临时变量 + setTempSelectedOptions([...selectedOptions]); + fetchGroups(1, ""); + } + }, [visible]); + + // 搜索防抖(只在弹窗打开且搜索词变化时执行) + useEffect(() => { + if (!visible || searchQuery === "") return; + + const timer = setTimeout(() => { + setCurrentPage(1); + fetchGroups(1, searchQuery); + }, 500); + + return () => clearTimeout(timer); + }, [searchQuery, visible]); + + // 页码变化时请求数据(只在弹窗打开且页码不是1时执行) + useEffect(() => { + if (!visible) return; + fetchGroups(currentPage, searchQuery); + }, [currentPage, visible, searchQuery]); + + return ( + onVisibleChange(false)} + position="bottom" + bodyStyle={{ height: "100vh" }} + > + fetchGroups(currentPage, searchQuery)} + /> + } + footer={ + onVisibleChange(false)} + onConfirm={handleConfirm} + isAllSelected={isCurrentPageAllSelected} + onSelectAll={handleSelectAllCurrentPage} + /> + } + > +
+ {loading ? ( +
+
加载中...
+
+ ) : groups.length > 0 ? ( +
+ {groups.map(group => ( +
+ g.id === group.id)} + onChange={() => !readonly && handleGroupToggle(group)} + disabled={readonly} + style={{ marginRight: 12 }} + /> +
+
+ {group.avatar ? ( + {group.name} + ) : ( + group.name.charAt(0) + )} +
+
+
{group.name}
+
+ 群ID: {group.chatroomId} +
+ {group.ownerNickname && ( +
+ 群主: {group.ownerNickname} +
+ )} +
+
+
+ ))} +
+ ) : ( +
+
+ {searchQuery + ? `没有找到包含"${searchQuery}"的群聊` + : "没有找到群聊"} +
+
+ )} +
+
+
+ ); +} diff --git a/Touchkebao/src/components/InfiniteList/InfiniteList.module.scss b/Touchkebao/src/components/InfiniteList/InfiniteList.module.scss new file mode 100644 index 00000000..a78dbf9e --- /dev/null +++ b/Touchkebao/src/components/InfiniteList/InfiniteList.module.scss @@ -0,0 +1,87 @@ +.listContainer { + display: flex; + flex-direction: column; + overflow: hidden; + position: relative; +} + +.listItem { + flex-shrink: 0; + width: 100%; +} + +.loadMoreButtonContainer { + display: flex; + justify-content: center; + align-items: center; + padding: 16px; + flex-shrink: 0; +} + +.noMoreText { + text-align: center; + color: #999; + font-size: 14px; + padding: 16px; + flex-shrink: 0; +} + +.emptyState { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 60px 20px; + color: #999; + flex: 1; + min-height: 200px; +} + +.emptyIcon { + font-size: 48px; + margin-bottom: 16px; + opacity: 0.5; +} + +.emptyText { + font-size: 14px; + color: #999; +} + +.pullToRefresh { + height: 100%; + overflow: auto; +} + +// 自定义滚动条样式 +.listContainer::-webkit-scrollbar { + width: 4px; +} + +.listContainer::-webkit-scrollbar-track { + background: transparent; +} + +.listContainer::-webkit-scrollbar-thumb { + background: rgba(0, 0, 0, 0.1); + border-radius: 2px; +} + +.listContainer::-webkit-scrollbar-thumb:hover { + background: rgba(0, 0, 0, 0.2); +} + +// 响应式设计 +@media (max-width: 768px) { + .listContainer { + padding: 0 8px; + } + + .loadMoreButtonContainer { + padding: 12px; + } + + .noMoreText { + padding: 12px; + } +} diff --git a/Touchkebao/src/components/InfiniteList/InfiniteList.tsx b/Touchkebao/src/components/InfiniteList/InfiniteList.tsx new file mode 100644 index 00000000..56e7369b --- /dev/null +++ b/Touchkebao/src/components/InfiniteList/InfiniteList.tsx @@ -0,0 +1,195 @@ +import React, { useState, useEffect, useRef, useCallback } from "react"; +import { + PullToRefresh, + InfiniteScroll, + Button, + SpinLoading, +} from "antd-mobile"; +import styles from "./InfiniteList.module.scss"; + +interface InfiniteListProps { + // 数据相关 + data: T[]; + loading?: boolean; + hasMore?: boolean; + loadingText?: string; + noMoreText?: string; + + // 渲染相关 + renderItem: (item: T, index: number) => React.ReactNode; + keyExtractor?: (item: T, index: number) => string | number; + + // 事件回调 + onLoadMore?: () => Promise | void; + onRefresh?: () => Promise | void; + + // 样式相关 + className?: string; + itemClassName?: string; + containerStyle?: React.CSSProperties; + + // 功能开关 + enablePullToRefresh?: boolean; + enableInfiniteScroll?: boolean; + enableLoadMoreButton?: boolean; + + // 自定义高度 + height?: string | number; + minHeight?: string | number; +} + +const InfiniteList = ({ + data, + loading = false, + hasMore = true, + loadingText = "加载中...", + noMoreText = "没有更多了", + + renderItem, + keyExtractor = (_, index) => index, + + onLoadMore, + onRefresh, + + className = "", + itemClassName = "", + containerStyle = {}, + + enablePullToRefresh = true, + enableInfiniteScroll = true, + enableLoadMoreButton = false, + + height = "100%", + minHeight = "200px", +}: InfiniteListProps) => { + const [refreshing, setRefreshing] = useState(false); + const [loadingMore, setLoadingMore] = useState(false); + const containerRef = useRef(null); + + // 处理下拉刷新 + const handleRefresh = useCallback(async () => { + if (!onRefresh) return; + + setRefreshing(true); + try { + await onRefresh(); + } catch (error) { + console.error("Refresh failed:", error); + } finally { + setRefreshing(false); + } + }, [onRefresh]); + + // 处理加载更多 + const handleLoadMore = useCallback(async () => { + if (!onLoadMore || loadingMore || !hasMore) return; + + setLoadingMore(true); + try { + await onLoadMore(); + } catch (error) { + console.error("Load more failed:", error); + } finally { + setLoadingMore(false); + } + }, [onLoadMore, loadingMore, hasMore]); + + // 点击加载更多按钮 + const handleLoadMoreClick = useCallback(() => { + handleLoadMore(); + }, [handleLoadMore]); + + // 容器样式 + const containerStyles: React.CSSProperties = { + height, + minHeight, + ...containerStyle, + }; + + // 渲染列表项 + const renderListItems = () => { + return data.map((item, index) => ( +
+ {renderItem(item, index)} +
+ )); + }; + + // 渲染加载更多按钮 + const renderLoadMoreButton = () => { + if (!enableLoadMoreButton || !hasMore) return null; + + return ( +
+ +
+ ); + }; + + // 渲染无更多数据提示 + const renderNoMoreText = () => { + if (hasMore || data.length === 0) return null; + + return
{noMoreText}
; + }; + + // 渲染空状态 + const renderEmptyState = () => { + if (data.length > 0 || loading) return null; + + return ( +
+
📝
+
暂无数据
+
+ ); + }; + + const content = ( +
+ {renderListItems()} + {renderLoadMoreButton()} + {renderNoMoreText()} + {renderEmptyState()} + + {/* 无限滚动组件 */} + {enableInfiniteScroll && ( + + )} +
+ ); + + // 如果启用下拉刷新,包装PullToRefresh + if (enablePullToRefresh && onRefresh) { + return ( + + {content} + + ); + } + + return content; +}; + +export default InfiniteList; diff --git a/Touchkebao/src/components/Layout/Layout.tsx b/Touchkebao/src/components/Layout/Layout.tsx new file mode 100644 index 00000000..160d993e --- /dev/null +++ b/Touchkebao/src/components/Layout/Layout.tsx @@ -0,0 +1,52 @@ +import React, { useEffect } from "react"; +import { SpinLoading } from "antd-mobile"; +import styles from "./layout.module.scss"; + +interface LayoutProps { + loading?: boolean; + children?: React.ReactNode; + header?: React.ReactNode; + footer?: React.ReactNode; +} + +const Layout: React.FC = ({ + children, + header, + footer, + loading = false, +}) => { + // 移动端100vh兼容 + useEffect(() => { + const setRealHeight = () => { + document.documentElement.style.setProperty( + "--real-vh", + `${window.innerHeight * 0.01}px`, + ); + }; + setRealHeight(); + window.addEventListener("resize", setRealHeight); + return () => window.removeEventListener("resize", setRealHeight); + }, []); + + return ( +
+ {header &&
{header}
} +
+ {loading ? ( +
+ +
加载中...
+
+ ) : ( + children + )} +
+ {footer &&
{footer}
} +
+ ); +}; + +export default Layout; diff --git a/Touchkebao/src/components/Layout/LayoutFiexd.tsx b/Touchkebao/src/components/Layout/LayoutFiexd.tsx new file mode 100644 index 00000000..9a5cefd5 --- /dev/null +++ b/Touchkebao/src/components/Layout/LayoutFiexd.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { SpinLoading } from "antd-mobile"; +import styles from "./layout.module.scss"; + +interface LayoutProps { + loading?: boolean; + children?: React.ReactNode; + header?: React.ReactNode; + footer?: React.ReactNode; +} + +const LayoutFiexd: React.FC = ({ + header, + children, + footer, + loading = false, +}) => { + return ( +
+
{header}
+
+ {loading ? ( +
+ +
加载中...
+
+ ) : ( + children + )} +
+
{footer}
+
+ ); +}; + +export default LayoutFiexd; diff --git a/Touchkebao/src/components/Layout/layout.module.scss b/Touchkebao/src/components/Layout/layout.module.scss new file mode 100644 index 00000000..3818f44d --- /dev/null +++ b/Touchkebao/src/components/Layout/layout.module.scss @@ -0,0 +1,28 @@ +.container { + display: flex; + height: 100vh; + flex-direction: column; + background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); +} + +.container main { + flex: 1; + overflow: auto; +} + +.loadingContainer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + min-height: 300px; + background: rgba(255, 255, 255, 0.8); +} + +.loadingText { + margin-top: 16px; + color: #666; + font-size: 14px; + text-align: center; +} diff --git a/Touchkebao/src/components/LineChart.tsx b/Touchkebao/src/components/LineChart.tsx new file mode 100644 index 00000000..bb93e512 --- /dev/null +++ b/Touchkebao/src/components/LineChart.tsx @@ -0,0 +1,53 @@ +import React from "react"; +import ReactECharts from "echarts-for-react"; + +interface LineChartProps { + title?: string; + xData: string[]; + yData: number[]; + height?: number | string; +} + +const LineChart: React.FC = ({ + title = "", + xData, + yData, + height = 200, +}) => { + const option = { + title: { + text: title, + left: "center", + textStyle: { fontSize: 16 }, + }, + tooltip: { trigger: "axis" }, + xAxis: { + type: "category", + data: xData, + boundaryGap: false, + }, + yAxis: { + type: "value", + boundaryGap: ["10%", "10%"], // 上下留白 + min: (value: any) => value.min - 10, // 下方多留一点空间 + max: (value: any) => value.max + 10, // 上方多留一点空间 + minInterval: 1, + axisLabel: { margin: 12 }, + }, + series: [ + { + data: yData, + type: "line", + smooth: true, + symbol: "circle", + lineStyle: { color: "#1677ff" }, + itemStyle: { color: "#1677ff" }, + }, + ], + grid: { left: 40, right: 24, top: 40, bottom: 32 }, + }; + + return ; +}; + +export default LineChart; diff --git a/Touchkebao/src/components/LineChart2.tsx b/Touchkebao/src/components/LineChart2.tsx new file mode 100644 index 00000000..2a6f401e --- /dev/null +++ b/Touchkebao/src/components/LineChart2.tsx @@ -0,0 +1,57 @@ +import React from "react"; +import ReactECharts from "echarts-for-react"; +import { getChartColor } from "@/utils/chartColors"; + +interface LineChartProps { + title?: string; + xData: string[]; + yData: any[]; + height?: number | string; +} + +const LineChart: React.FC = ({ + title = "", + xData, + yData, + height = 200, +}) => { + const option = { + title: { + text: title, + left: "center", + textStyle: { fontSize: 16 }, + }, + tooltip: { trigger: "axis" }, + xAxis: { + type: "category", + data: xData, + boundaryGap: false, + }, + yAxis: { + type: "value", + boundaryGap: ["10%", "10%"], // 上下留白 + min: (value: any) => value.min - 10, // 下方多留一点空间 + max: (value: any) => value.max + 10, // 上方多留一点空间 + minInterval: 1, + axisLabel: { margin: 12 }, + }, + series: [ + ...yData.map((item, index) => { + const color = getChartColor(index); + return { + data: item, + type: "line", + smooth: true, + symbol: "circle", + lineStyle: { color }, + itemStyle: { color }, + }; + }), + ], + grid: { left: 40, right: 24, top: 40, bottom: 32 }, + }; + + return ; +}; + +export default LineChart; diff --git a/Touchkebao/src/components/MeauMobile/MeauMoible.tsx b/Touchkebao/src/components/MeauMobile/MeauMoible.tsx new file mode 100644 index 00000000..da838937 --- /dev/null +++ b/Touchkebao/src/components/MeauMobile/MeauMoible.tsx @@ -0,0 +1,57 @@ +import React from "react"; +import { TabBar } from "antd-mobile"; +import { PieOutline, UserOutline } from "antd-mobile-icons"; +import { HomeOutlined, TeamOutlined } from "@ant-design/icons"; +import { useNavigate } from "react-router-dom"; + +const tabs = [ + { + key: "home", + title: "首页", + icon: , + path: "/", + }, + { + key: "scenarios", + title: "场景获客", + icon: , + path: "/scenarios", + }, + { + key: "workspace", + title: "工作台", + icon: , + path: "/workspace", + }, + { + key: "mine", + title: "我的", + icon: , + path: "/mine", + }, +]; + +interface MeauMobileProps { + activeKey: string; +} + +const MeauMobile: React.FC = ({ activeKey }) => { + const navigate = useNavigate(); + + return ( + { + const tab = tabs.find(t => t.key === key); + if (tab && tab.path) navigate(tab.path); + }} + > + {tabs.map(item => ( + + ))} + + ); +}; + +export default MeauMobile; diff --git a/Touchkebao/src/components/MemberSelection/TwoColumnMemberSelection.module.scss b/Touchkebao/src/components/MemberSelection/TwoColumnMemberSelection.module.scss new file mode 100644 index 00000000..0ff3cbe4 --- /dev/null +++ b/Touchkebao/src/components/MemberSelection/TwoColumnMemberSelection.module.scss @@ -0,0 +1,154 @@ +.twoColumnModal { + .ant-modal-body { + padding: 0; + } +} + +.container { + display: flex; + height: 500px; + border: 1px solid #e8e8e8; +} + +.leftColumn { + flex: 1; + border-right: 1px solid #e8e8e8; + display: flex; + flex-direction: column; +} + +.rightColumn { + width: 300px; + display: flex; + flex-direction: column; + background: #fafafa; +} + +.searchWrapper { + padding: 16px; + border-bottom: 1px solid #e8e8e8; + + .ant-input { + border-radius: 6px; + } +} + +.memberList { + flex: 1; + overflow-y: auto; + padding: 8px 0; +} + +.memberItem { + display: flex; + align-items: center; + padding: 12px 16px; + cursor: pointer; + transition: background-color 0.2s; + + &:hover { + background-color: #f5f5f5; + } + + &.selected { + background-color: #e6f7ff; + } + + .ant-checkbox { + margin-right: 12px; + } +} + +.memberInfo { + margin-left: 12px; + flex: 1; +} + +.memberName { + font-size: 14px; + font-weight: 500; + color: #333; + margin-bottom: 2px; +} + +.memberId { + font-size: 12px; + color: #999; +} + +.selectedHeader { + padding: 16px; + border-bottom: 1px solid #e8e8e8; + font-weight: 500; + color: #333; + background: #fff; + display: flex; + align-items: center; + justify-content: space-between; +} + +.singleTip { + font-size: 12px; + color: #999; + font-weight: normal; +} + +.selectedList { + flex: 1; + overflow-y: auto; + padding: 8px 0; +} + +.selectedItem { + display: flex; + align-items: center; + padding: 8px 16px; + background: #fff; + margin: 4px 8px; + border-radius: 6px; + border: 1px solid #e8e8e8; +} + +.selectedInfo { + margin-left: 8px; + flex: 1; +} + +.selectedName { + font-size: 13px; + color: #333; +} + +.removeBtn { + color: #999; + font-size: 16px; + padding: 0; + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + + &:hover { + color: #ff4d4f; + background: #fff2f0; + } +} + +.empty { + display: flex; + align-items: center; + justify-content: center; + height: 100px; + color: #999; + font-size: 14px; +} + +.emptySelected { + display: flex; + align-items: center; + justify-content: center; + height: 100px; + color: #999; + font-size: 14px; +} \ No newline at end of file diff --git a/Touchkebao/src/components/MemberSelection/TwoColumnMemberSelection.tsx b/Touchkebao/src/components/MemberSelection/TwoColumnMemberSelection.tsx new file mode 100644 index 00000000..ef276a68 --- /dev/null +++ b/Touchkebao/src/components/MemberSelection/TwoColumnMemberSelection.tsx @@ -0,0 +1,185 @@ +import React, { useState } from 'react'; +import { Modal, Input, Avatar, Button, Checkbox } from 'antd'; +import { SearchOutlined } from '@ant-design/icons'; +import styles from './TwoColumnMemberSelection.module.scss'; + +interface Member { + id: string; + nickname: string; + avatar: string; +} + +interface TwoColumnMemberSelectionProps { + visible: boolean; + members: Member[]; + onCancel: () => void; + onConfirm: (selectedIds: string[]) => void; + title?: string; + allowMultiple?: boolean; +} + +const TwoColumnMemberSelection: React.FC = ({ + visible, + members, + onCancel, + onConfirm, + title = '选择成员', + allowMultiple = true, +}) => { + const [selectedMembers, setSelectedMembers] = useState([]); + const [searchQuery, setSearchQuery] = useState(''); + + // 过滤成员 + const filteredMembers = members.filter(member => + member.nickname.toLowerCase().includes(searchQuery.toLowerCase()) || + member.id.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + // 处理搜索 + const handleSearch = (value: string) => { + setSearchQuery(value); + }; + + // 选择成员 + const handleSelectMember = (member: Member) => { + const isSelected = selectedMembers.some(m => m.id === member.id); + + if (allowMultiple) { + if (isSelected) { + setSelectedMembers(selectedMembers.filter(m => m.id !== member.id)); + } else { + setSelectedMembers([...selectedMembers, member]); + } + } else { + // 单选模式 + if (isSelected) { + setSelectedMembers([]); + } else { + setSelectedMembers([member]); + } + } + }; + + // 移除已选成员 + const handleRemoveMember = (memberId: string) => { + setSelectedMembers(selectedMembers.filter(m => m.id !== memberId)); + }; + + // 确认选择 + const handleConfirmSelection = () => { + const selectedIds = selectedMembers.map(m => m.id); + onConfirm(selectedIds); + setSelectedMembers([]); + setSearchQuery(''); + }; + + // 取消选择 + const handleCancelSelection = () => { + setSelectedMembers([]); + setSearchQuery(''); + onCancel(); + }; + + return ( + + 取消 + , + , + ]} + className={styles.twoColumnModal} + > +
+ {/* 左侧:成员列表 */} +
+
+ handleSearch(e.target.value)} + prefix={} + allowClear + /> +
+ +
+ {filteredMembers.length > 0 ? ( + filteredMembers.map(member => { + const isSelected = selectedMembers.some(m => m.id === member.id); + return ( +
handleSelectMember(member)} + > + + + {member.nickname?.charAt(0)} + +
+
{member.nickname}
+
{member.id}
+
+
+ ); + }) + ) : ( +
+ {searchQuery ? `没有找到包含"${searchQuery}"的成员` : '暂无成员'} +
+ )} +
+
+ + {/* 右侧:已选成员 */} +
+
+ 已选成员 ({selectedMembers.length}) + {!allowMultiple && (单选)} +
+ +
+ {selectedMembers.length > 0 ? ( + selectedMembers.map(member => ( +
+ + {member.nickname?.charAt(0)} + +
+
{member.nickname}
+
+ +
+ )) + ) : ( +
+ 暂无选择 +
+ )} +
+
+
+
+ ); +}; + +export default TwoColumnMemberSelection; \ No newline at end of file diff --git a/Touchkebao/src/components/MemberSelection/index.tsx b/Touchkebao/src/components/MemberSelection/index.tsx new file mode 100644 index 00000000..06c219b1 --- /dev/null +++ b/Touchkebao/src/components/MemberSelection/index.tsx @@ -0,0 +1,51 @@ +import React, { useState } from 'react'; +import { Modal, Checkbox, Avatar, List, Button } from 'antd'; + +interface MemberSelectionProps { + visible: boolean; + members: { id: string; nickname: string; avatar: string }[]; + onCancel: () => void; + onConfirm: (selectedIds: string[]) => void; +} + +const MemberSelection: React.FC = ({ visible, members, onCancel, onConfirm }) => { + const [selectedIds, setSelectedIds] = useState([]); + + const handleToggle = (id: string) => { + const newSelectedIds = selectedIds.includes(id) + ? selectedIds.filter(memberId => memberId !== id) + : [...selectedIds, id]; + setSelectedIds(newSelectedIds); + }; + + const handleConfirm = () => { + onConfirm(selectedIds); + setSelectedIds([]); + }; + + return ( + + ( + handleToggle(member.id)} style={{ cursor: 'pointer' }}> + } + title={member.nickname} + /> + + + )} + /> + + ); +}; + +export default MemberSelection; \ No newline at end of file diff --git a/Touchkebao/src/components/NavCommon/index.tsx b/Touchkebao/src/components/NavCommon/index.tsx new file mode 100644 index 00000000..0ee5ec1b --- /dev/null +++ b/Touchkebao/src/components/NavCommon/index.tsx @@ -0,0 +1,62 @@ +import React, { useEffect, useState } from "react"; +import { NavBar } from "antd-mobile"; +import { ArrowLeftOutlined } from "@ant-design/icons"; +import { useNavigate } from "react-router-dom"; +import { getSafeAreaHeight } from "@/utils/common"; +interface NavCommonProps { + title: string; + backFn?: () => void; + right?: React.ReactNode; + left?: React.ReactNode; +} + +const NavCommon: React.FC = ({ + title, + backFn, + right, + left, +}) => { + const navigate = useNavigate(); + const [paddingTop, setPaddingTop] = useState("0px"); + useEffect(() => { + setPaddingTop(getSafeAreaHeight() + "px"); + }, []); + + return ( +
+ + { + if (backFn) { + backFn(); + } else { + navigate(-1); + } + }} + /> +
+ ) + } + right={right} + > + + {title} + + + + ); +}; + +export default NavCommon; diff --git a/Touchkebao/src/components/PlaceholderPage.tsx b/Touchkebao/src/components/PlaceholderPage.tsx new file mode 100644 index 00000000..69a8ddde --- /dev/null +++ b/Touchkebao/src/components/PlaceholderPage.tsx @@ -0,0 +1,52 @@ +import React from "react"; +import { NavBar, Button } from "antd-mobile"; +import { PlusOutlined } from "@ant-design/icons"; +import Layout from "@/components/Layout/Layout"; + +interface PlaceholderPageProps { + title: string; + showBack?: boolean; + showAddButton?: boolean; + addButtonText?: string; +} + +const PlaceholderPage: React.FC = ({ + title, + showBack = true, + showAddButton = false, + addButtonText = "新建", +}) => { + return ( + window.history.back() : undefined} + left={ +
+ {title} +
+ } + right={ + showAddButton ? ( + + ) : undefined + } + /> + } + > +
+

{title}页面

+

此页面正在开发中...

+
+
+ ); +}; + +export default PlaceholderPage; diff --git a/Touchkebao/src/components/PoolSelection/api.ts b/Touchkebao/src/components/PoolSelection/api.ts new file mode 100644 index 00000000..552be7e6 --- /dev/null +++ b/Touchkebao/src/components/PoolSelection/api.ts @@ -0,0 +1,34 @@ +import request from "@/api/request"; + +// 请求参数接口 +export interface Request { + keyword: string; + /** + * 条数 + */ + limit: string; + /** + * 分页 + */ + page: string; + [property: string]: any; +} + +// 获取流量池包列表 +export function getPoolPackages(params: Request) { + return request("/v1/traffic/pool/getPackage", params, "GET"); +} + +// 保留原接口以兼容现有代码 +export function getPoolList(params: { + page?: string; + pageSize?: string; + keyword?: string; + addStatus?: string; + deviceId?: string; + packageId?: string; + userValue?: string; + [property: string]: any; +}) { + return request("/v1/traffic/pool", params, "GET"); +} diff --git a/Touchkebao/src/components/PoolSelection/data.ts b/Touchkebao/src/components/PoolSelection/data.ts new file mode 100644 index 00000000..95ff55d4 --- /dev/null +++ b/Touchkebao/src/components/PoolSelection/data.ts @@ -0,0 +1,61 @@ +// 流量池包接口类型 +export interface PoolPackageItem { + id: number; + name: string; + description: string; + createTime: string; + num: number; +} + +// 原流量池接口类型(保留以兼容现有代码) +export interface PoolItem { + id: number; + identifier: string; + mobile: string; + wechatId: string; + fromd: string; + status: number; + createTime: string; + companyId: number; + sourceId: string; + type: number; + nickname: string; + avatar: string; + gender: number; + phone: string; + alias: string; + packages: any[]; + tags: any[]; +} + +export interface PoolSelectionItem { + id: string; + avatar?: string; + name: string; + wechatId?: string; + mobile?: string; + nickname?: string; + createTime?: string; + description?: string; + num?: number; + [key: string]: any; +} + +// 组件属性接口 +export interface PoolSelectionProps { + selectedOptions: PoolSelectionItem[]; + onSelect: (Pools: PoolSelectionItem[]) => void; + onSelectDetail?: (Pools: PoolPackageItem[]) => void; + placeholder?: string; + className?: string; + visible?: boolean; + onVisibleChange?: (visible: boolean) => void; + selectedListMaxHeight?: number; + showInput?: boolean; + showSelectedList?: boolean; + readonly?: boolean; + onConfirm?: ( + selectedIds: string[], + selectedItems: PoolSelectionItem[], + ) => void; +} diff --git a/Touchkebao/src/components/PoolSelection/index.module.scss b/Touchkebao/src/components/PoolSelection/index.module.scss new file mode 100644 index 00000000..f0421ca1 --- /dev/null +++ b/Touchkebao/src/components/PoolSelection/index.module.scss @@ -0,0 +1,206 @@ +.inputWrapper { + position: relative; +} +.inputIcon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: #bdbdbd; + font-size: 20px; +} +.input { + padding-left: 38px !important; + height: 48px; + border-radius: 16px !important; + border: 1px solid #e5e6eb !important; + font-size: 16px; + background: #f8f9fa; +} +.selectedListRow { + padding: 8px; + border-bottom: 1px solid #f0f0f0; + font-size: 14px; +} +.selectedListRowContent { + flex: 1; + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} +.selectedListRowContentText { + flex: 1; +} + +.popupContainer { + display: flex; + flex-direction: column; + height: 100vh; + background: #fff; +} +.popupHeader { + padding: 24px; +} +.popupTitle { + text-align: center; + font-size: 20px; + font-weight: 600; + margin-bottom: 24px; +} +.searchWrapper { + position: relative; + margin-bottom: 16px; +} +.searchInput { + padding-left: 40px !important; + padding-top: 8px !important; + padding-bottom: 8px !important; + border-radius: 24px !important; + border: 1px solid #e5e6eb !important; + font-size: 15px; + background: #f8f9fa; +} +.searchIcon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: #bdbdbd; + font-size: 16px; +} +.clearBtn { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + height: 24px; + width: 24px; + border-radius: 50%; + min-width: 24px; +} + +.groupList { + flex: 1; + overflow-y: auto; +} +.groupListInner { + border-top: 1px solid #f0f0f0; +} +.groupItem { + display: flex; + align-items: center; + padding: 16px 24px; + border-bottom: 1px solid #f0f0f0; + transition: background 0.2s; + &:hover { + background: #f5f6fa; + } +} +.groupInfo { + display: flex; + align-items: center; + gap: 12px; + flex: 1; +} +.groupAvatar { + width: 40px; + height: 40px; + border-radius: 8px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + display: flex; + align-items: center; + justify-content: center; + color: #fff; + font-size: 14px; + font-weight: 500; + overflow: hidden; +} +.avatarImg { + width: 100%; + height: 100%; + object-fit: cover; +} +.groupDetail { + flex: 1; +} +.groupName { + font-weight: 500; + font-size: 16px; + color: #222; + margin-bottom: 2px; +} +.groupId { + font-size: 13px; + color: #888; + margin-bottom: 2px; +} +.groupOwner { + font-size: 13px; + color: #bdbdbd; +} + +.loadingBox { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} +.loadingText { + color: #888; + font-size: 15px; +} +.emptyBox { + display: flex; + align-items: center; + justify-content: center; + height: 100%; +} +.emptyText { + color: #888; + font-size: 15px; +} + +.paginationRow { + border-top: 1px solid #f0f0f0; + padding: 16px; + display: flex; + align-items: center; + justify-content: space-between; + background: #fff; +} +.totalCount { + font-size: 14px; + color: #888; +} +.paginationControls { + display: flex; + align-items: center; + gap: 8px; +} +.pageBtn { + padding: 0 8px; + height: 32px; + min-width: 32px; +} +.pageInfo { + font-size: 14px; + color: #222; +} + +.popupFooter { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px; + border-top: 1px solid #f0f0f0; + background: #fff; +} +.selectedCount { + font-size: 14px; + color: #888; +} +.footerBtnGroup { + display: flex; + gap: 12px; +} diff --git a/Touchkebao/src/components/PoolSelection/index.tsx b/Touchkebao/src/components/PoolSelection/index.tsx new file mode 100644 index 00000000..5118bacd --- /dev/null +++ b/Touchkebao/src/components/PoolSelection/index.tsx @@ -0,0 +1,127 @@ +import React, { useState } from "react"; +import { SearchOutlined, DeleteOutlined } from "@ant-design/icons"; +import { Button, Input } from "antd"; +import style from "./index.module.scss"; +import SelectionPopup from "./selectionPopup"; +import { PoolSelectionProps } from "./data"; +export default function PoolSelection({ + selectedOptions, + onSelect, + onSelectDetail, + placeholder = "选择流量池", + className = "", + visible, + onVisibleChange, + selectedListMaxHeight = 300, + showInput = true, + showSelectedList = true, + readonly = false, + onConfirm, +}: PoolSelectionProps) { + const [popupVisible, setPopupVisible] = useState(false); + + // 删除已选流量池项 + const handleRemoveItem = (id: string) => { + if (readonly) return; + onSelect(selectedOptions.filter(item => item.id !== id)); + }; + + // 受控弹窗逻辑 + const realVisible = visible !== undefined ? visible : popupVisible; + const setRealVisible = (v: boolean) => { + if (onVisibleChange) onVisibleChange(v); + if (visible === undefined) setPopupVisible(v); + }; + + // 打开弹窗 + const openPopup = () => { + if (readonly) return; + setRealVisible(true); + }; + + // 获取显示文本 + const getDisplayText = () => { + if (selectedOptions.length === 0) return ""; + return `已选择 ${selectedOptions.length} 个流量池项`; + }; + + return ( + <> + {/* 输入框 */} + {showInput && ( +
+ } + allowClear={!readonly} + size="large" + readOnly={readonly} + disabled={readonly} + style={ + readonly ? { background: "#f5f5f5", cursor: "not-allowed" } : {} + } + /> +
+ )} + {/* 已选流量池列表窗口 */} + {showSelectedList && selectedOptions.length > 0 && ( +
+ {selectedOptions.map(item => ( +
+
+
+ {(item.nickname || item.name || "").charAt(0)} +
+
+
{item.nickname || item.name}
+
{item.wechatId || item.mobile}
+
+ {!readonly && ( +
+
+ ))} +
+ )} + {/* 弹窗 */} + + + ); +} diff --git a/Touchkebao/src/components/PoolSelection/selectionPopup.tsx b/Touchkebao/src/components/PoolSelection/selectionPopup.tsx new file mode 100644 index 00000000..20d08bec --- /dev/null +++ b/Touchkebao/src/components/PoolSelection/selectionPopup.tsx @@ -0,0 +1,258 @@ +import React, { useState, useEffect } from "react"; +import { Popup, Checkbox } from "antd-mobile"; + +import { getPoolPackages, Request } from "./api"; +import style from "./index.module.scss"; +import Layout from "@/components/Layout/Layout"; +import PopupHeader from "@/components/PopuLayout/header"; +import PopupFooter from "@/components/PopuLayout/footer"; +import { PoolSelectionItem, PoolPackageItem } from "./data"; + +// 弹窗属性接口 +interface SelectionPopupProps { + visible: boolean; + onVisibleChange: (visible: boolean) => void; + selectedOptions: PoolSelectionItem[]; + onSelect: (items: PoolSelectionItem[]) => void; + onSelectDetail?: (items: PoolPackageItem[]) => void; + readonly?: boolean; + onConfirm?: ( + selectedIds: string[], + selectedItems: PoolSelectionItem[], + ) => void; +} + +export default function SelectionPopup({ + visible, + onVisibleChange, + selectedOptions, + onSelect, + onSelectDetail, + readonly = false, + onConfirm, +}: SelectionPopupProps) { + const [poolPackages, setPoolPackages] = useState([]); + const [searchQuery, setSearchQuery] = useState(""); + const [currentPage, setCurrentPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + const [totalItems, setTotalItems] = useState(0); + const [loading, setLoading] = useState(false); + const [tempSelectedOptions, setTempSelectedOptions] = useState< + PoolSelectionItem[] + >([]); + + // 获取流量池包列表API + const fetchPoolPackages = async (page: number, keyword: string = "") => { + setLoading(true); + try { + const params: Request = { + page: String(page), + limit: "20", + keyword: keyword.trim(), + }; + + const response = await getPoolPackages(params); + if (response && response.list) { + setPoolPackages(response.list); + setTotalItems(response.total || 0); + setTotalPages(Math.ceil((response.total || 0) / 20)); + } + } catch (error) { + console.error("获取流量池包列表失败:", error); + } finally { + setLoading(false); + } + }; + + // 处理流量池包选择 + const handlePackageToggle = (item: PoolPackageItem) => { + if (readonly) return; + + // 将PoolPackageItem转换为GroupSelectionItem格式 + const selectionItem: PoolSelectionItem = { + id: String(item.id), + name: item.name, + description: item.description, + createTime: item.createTime, + num: item.num, + // 保留原始数据 + originalData: item, + }; + + const newSelectedItems = tempSelectedOptions.some( + g => g.id === String(item.id), + ) + ? tempSelectedOptions.filter(g => g.id !== String(item.id)) + : tempSelectedOptions.concat(selectionItem); + + setTempSelectedOptions(newSelectedItems); + + // 如果有 onSelectDetail 回调,传递完整的流量池包对象 + if (onSelectDetail) { + const selectedItemObjs = poolPackages.filter(packageItem => + newSelectedItems.some(g => g.id === String(packageItem.id)), + ); + onSelectDetail(selectedItemObjs); + } + }; + + // 全选当前页 + const handleSelectAllCurrentPage = (checked: boolean) => { + if (readonly) return; + + if (checked) { + // 全选:添加当前页面所有未选中的流量池包 + const currentPagePackages = poolPackages.filter( + packageItem => + !tempSelectedOptions.some(p => p.id === String(packageItem.id)), + ); + const newSelectionItems = currentPagePackages.map(item => ({ + id: String(item.id), + name: item.name, + description: item.description, + createTime: item.createTime, + num: item.num, + originalData: item, + })); + setTempSelectedOptions(prev => [...prev, ...newSelectionItems]); + } else { + // 取消全选:移除当前页面的所有流量池包 + const currentPagePackageIds = poolPackages.map(p => String(p.id)); + setTempSelectedOptions(prev => + prev.filter(p => !currentPagePackageIds.includes(p.id)), + ); + } + }; + + // 检查当前页是否全选 + const isCurrentPageAllSelected = + poolPackages.length > 0 && + poolPackages.every(packageItem => + tempSelectedOptions.some(p => p.id === String(packageItem.id)), + ); + + // 确认选择 + const handleConfirm = () => { + if (onConfirm) { + onConfirm( + tempSelectedOptions.map(item => item.id), + tempSelectedOptions, + ); + } + // 更新实际选中的选项 + onSelect(tempSelectedOptions); + onVisibleChange(false); + }; + + // 弹窗打开时初始化数据(只执行一次) + useEffect(() => { + if (visible) { + setCurrentPage(1); + setSearchQuery(""); + // 复制一份selectedOptions到临时变量 + setTempSelectedOptions([...selectedOptions]); + fetchPoolPackages(1, ""); + } + }, [visible, selectedOptions]); + + // 搜索防抖(只在弹窗打开且搜索词变化时执行) + useEffect(() => { + if (!visible || searchQuery === "") return; + + const timer = setTimeout(() => { + setCurrentPage(1); + fetchPoolPackages(1, searchQuery); + }, 500); + + return () => clearTimeout(timer); + }, [searchQuery, visible]); + + // 页码变化时请求数据(只在弹窗打开且页码不是1时执行) + useEffect(() => { + if (!visible) return; + fetchPoolPackages(currentPage, searchQuery); + }, [currentPage, visible, searchQuery]); + + return ( + onVisibleChange(false)} + position="bottom" + bodyStyle={{ height: "100vh" }} + > + fetchPoolPackages(currentPage, searchQuery)} + /> + } + footer={ + onVisibleChange(false)} + onConfirm={handleConfirm} + isAllSelected={isCurrentPageAllSelected} + onSelectAll={handleSelectAllCurrentPage} + /> + } + > +
+ {loading ? ( +
+
加载中...
+
+ ) : poolPackages.length > 0 ? ( +
+ {poolPackages.map(item => ( +
+ g.id === String(item.id), + )} + onChange={() => !readonly && handlePackageToggle(item)} + disabled={readonly} + style={{ marginRight: 12 }} + /> +
+
+ {item.name ? item.name.charAt(0) : "?"} +
+
+
{item.name}
+
+ 描述: {item.description || "无描述"} +
+
+ 创建时间: {item.createTime} +
+
+ 包含数量: {item.num} +
+
+
+
+ ))} +
+ ) : ( +
+
+ {searchQuery + ? `没有找到包含"${searchQuery}"的流量池包` + : "没有找到流量池包"} +
+
+ )} +
+
+
+ ); +} diff --git a/Touchkebao/src/components/PopuLayout/footer.module.scss b/Touchkebao/src/components/PopuLayout/footer.module.scss new file mode 100644 index 00000000..bd1286f0 --- /dev/null +++ b/Touchkebao/src/components/PopuLayout/footer.module.scss @@ -0,0 +1,88 @@ +.popupFooter { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px; + border-top: 1px solid #f0f0f0; + background: #fff; +} + +.selectedCount { + font-size: 14px; + color: #666; + display: flex; + align-items: center; + gap: 12px; +} + +.selectAllCheckbox { + margin-right: 0; + + .ant-checkbox-wrapper { + font-size: 14px; + } + + &.ant-checkbox-wrapper-disabled { + .ant-checkbox-disabled + span { + color: #d9d9d9; + } + } +} + +.footerBtnGroup { + display: flex; + gap: 12px; +} + +.paginationRow { + border-top: 1px solid #f0f0f0; + padding: 16px; + display: flex; + align-items: center; + justify-content: space-between; + background: #fff; +} + +.totalCount { + font-size: 14px; + color: #888; +} + +.paginationControls { + display: flex; + align-items: center; + gap: 8px; +} + +.pageBtn { + padding: 0 8px; + height: 32px; + min-width: 32px; + border-radius: 16px; + border: 1px solid #d9d9d9; + color: #333; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s; + + &:hover:not(:disabled) { + border-color: #1677ff; + color: #1677ff; + } + + &:disabled { + background: #f5f5f5; + color: #ccc; + cursor: not-allowed; + } +} + +.pageInfo { + font-size: 14px; + color: #222; + margin: 0 8px; + min-width: 60px; + text-align: center; +} diff --git a/Touchkebao/src/components/PopuLayout/footer.tsx b/Touchkebao/src/components/PopuLayout/footer.tsx new file mode 100644 index 00000000..60e562be --- /dev/null +++ b/Touchkebao/src/components/PopuLayout/footer.tsx @@ -0,0 +1,78 @@ +import React from "react"; +import { Button, Checkbox } from "antd"; +import style from "./footer.module.scss"; +import { ArrowLeftOutlined, ArrowRightOutlined } from "@ant-design/icons"; + +interface PopupFooterProps { + currentPage: number; + totalPages: number; + loading: boolean; + selectedCount: number; + onPageChange: (page: number) => void; + onCancel: () => void; + onConfirm: () => void; + // 全选功能相关 + isAllSelected?: boolean; + onSelectAll?: (checked: boolean) => void; +} + +const PopupFooter: React.FC = ({ + currentPage, + totalPages, + loading, + selectedCount, + onPageChange, + onCancel, + onConfirm, + isAllSelected = false, + onSelectAll, +}) => { + return ( + <> + {/* 分页栏 */} +
+
+ onSelectAll(e.target.checked)} + className={style.selectAllCheckbox} + > + 全选当前页 + +
+
+ + + {currentPage} / {totalPages} + + +
+
+
+
已选择 {selectedCount} 条记录
+
+ + +
+
+ + ); +}; + +export default PopupFooter; diff --git a/Touchkebao/src/components/PopuLayout/header.module.scss b/Touchkebao/src/components/PopuLayout/header.module.scss new file mode 100644 index 00000000..f54cbbee --- /dev/null +++ b/Touchkebao/src/components/PopuLayout/header.module.scss @@ -0,0 +1,51 @@ +.popupHeader { + padding: 16px; + border-bottom: 1px solid #f0f0f0; +} + +.popupTitle { + font-size: 20px; + font-weight: 600; + text-align: center; +} + +.popupSearchRow { + display: flex; + align-items: center; + gap: 5px; + padding: 16px; +} + +.popupSearchInputWrap { + position: relative; + flex: 1; +} + +.inputIcon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: #bdbdbd; + z-index: 10; + font-size: 18px; +} + +.refreshBtn { + width: 36px; + height: 36px; +} + +.loadingIcon { + animation: spin 1s linear infinite; + font-size: 16px; +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/Touchkebao/src/components/PopuLayout/header.tsx b/Touchkebao/src/components/PopuLayout/header.tsx new file mode 100644 index 00000000..51cad1b2 --- /dev/null +++ b/Touchkebao/src/components/PopuLayout/header.tsx @@ -0,0 +1,89 @@ +import React from "react"; +import { SearchOutlined, ReloadOutlined } from "@ant-design/icons"; +import { Input, Button } from "antd"; +import { Tabs } from "antd-mobile"; +import style from "./header.module.scss"; + +interface PopupHeaderProps { + title: string; + searchQuery: string; + setSearchQuery: (value: string) => void; + searchPlaceholder?: string; + loading?: boolean; + onRefresh?: () => void; + onSearch?: (query: string) => void; + showRefresh?: boolean; + showSearch?: boolean; + showTabs?: boolean; + tabsConfig?: { + activeKey: string; + onChange: (key: string) => void; + tabs: Array<{ title: string; key: string }>; + }; +} + +const PopupHeader: React.FC = ({ + title, + searchQuery, + setSearchQuery, + searchPlaceholder = "搜索...", + loading = false, + onRefresh, + onSearch, + showRefresh = true, + showSearch = true, + showTabs = false, + tabsConfig, +}) => { + return ( + <> +
+
{title}
+
+ + {showSearch && ( +
+
+ setSearchQuery(e.target.value)} + onSearch={() => onSearch && onSearch(searchQuery)} + prefix={} + size="large" + /> +
+ + {showRefresh && onRefresh && ( + + )} +
+ )} + + {showTabs && tabsConfig && ( + + {tabsConfig.tabs.map(tab => ( + + ))} + + )} + + ); +}; + +export default PopupHeader; diff --git a/Touchkebao/src/components/StepIndicator/index.tsx b/Touchkebao/src/components/StepIndicator/index.tsx new file mode 100644 index 00000000..655bbafd --- /dev/null +++ b/Touchkebao/src/components/StepIndicator/index.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { Steps } from "antd-mobile"; + +interface StepIndicatorProps { + currentStep: number; + steps: { id: number; title: string; subtitle: string }[]; +} + +const StepIndicator: React.FC = ({ + currentStep, + steps, +}) => { + return ( +
+ + {steps.map((step, idx) => ( + + {step.id} +
+ } + /> + ))} + + + ); +}; + +export default StepIndicator; diff --git a/Touchkebao/src/components/TwoColumnSelection/TwoColumnSelection.module.scss b/Touchkebao/src/components/TwoColumnSelection/TwoColumnSelection.module.scss new file mode 100644 index 00000000..5f53a343 --- /dev/null +++ b/Touchkebao/src/components/TwoColumnSelection/TwoColumnSelection.module.scss @@ -0,0 +1,153 @@ +.twoColumnModal { + .ant-modal-body { + padding: 0; + } +} + +.container { + display: flex; + height: 500px; + border: 1px solid #e8e8e8; +} + +.leftColumn { + flex: 1; + border-right: 1px solid #e8e8e8; + display: flex; + flex-direction: column; +} + +.rightColumn { + width: 300px; + display: flex; + flex-direction: column; + background: #fafafa; +} + +.searchWrapper { + padding: 16px; + border-bottom: 1px solid #e8e8e8; + + .ant-input { + border-radius: 6px; + } +} + +.friendList { + flex: 1; + overflow-y: auto; + padding: 8px 0; +} + +.friendItem { + display: flex; + align-items: center; + padding: 12px 16px; + cursor: pointer; + transition: background-color 0.2s; + + &:hover { + background-color: #f5f5f5; + } + + &.selected { + background-color: #e6f7ff; + } + + .ant-checkbox { + margin-right: 12px; + } +} + +.friendInfo { + margin-left: 12px; + flex: 1; +} + +.friendName { + font-size: 14px; + font-weight: 500; + color: #333; + margin-bottom: 2px; +} + +.friendId { + font-size: 12px; + color: #999; +} + +.selectedHeader { + padding: 16px; + border-bottom: 1px solid #e8e8e8; + font-weight: 500; + color: #333; + background: #fff; +} + +.selectedList { + flex: 1; + overflow-y: auto; + padding: 8px 0; +} + +.selectedItem { + display: flex; + align-items: center; + padding: 8px 16px; + background: #fff; + margin: 4px 8px; + border-radius: 6px; + border: 1px solid #e8e8e8; +} + +.selectedInfo { + margin-left: 8px; + flex: 1; +} + +.selectedName { + font-size: 13px; + color: #333; +} + +.removeBtn { + color: #999; + font-size: 16px; + padding: 0; + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + + &:hover { + color: #ff4d4f; + background: #fff2f0; + } +} + +.loading { + display: flex; + align-items: center; + justify-content: center; + height: 100px; + color: #999; +} + +.empty { + display: flex; + align-items: center; + justify-content: center; + height: 100px; + color: #999; + font-size: 14px; +} + +.emptySelected { + display: flex; + align-items: center; + justify-content: center; + height: 100px; + color: #999; + font-size: 14px; +} diff --git a/Touchkebao/src/components/TwoColumnSelection/TwoColumnSelection.tsx b/Touchkebao/src/components/TwoColumnSelection/TwoColumnSelection.tsx new file mode 100644 index 00000000..e5400bf6 --- /dev/null +++ b/Touchkebao/src/components/TwoColumnSelection/TwoColumnSelection.tsx @@ -0,0 +1,329 @@ +import React, { useState, useCallback, useEffect, useMemo, memo } from "react"; +import { Modal, Input, Avatar, Button, Checkbox, message } from "antd"; +import { SearchOutlined } from "@ant-design/icons"; +import { getFriendList } from "../FriendSelection/api"; +import type { FriendSelectionItem } from "../FriendSelection/data"; +import styles from "./TwoColumnSelection.module.scss"; + +// 使用 React.memo 优化好友列表项组件 +const FriendListItem = memo<{ + friend: FriendSelectionItem; + isSelected: boolean; + onSelect: (friend: FriendSelectionItem) => void; +}>(({ friend, isSelected, onSelect }) => { + return ( +
onSelect(friend)} + > + + + {friend.nickname?.charAt(0)} + +
+
{friend.nickname}
+
{friend.wechatId}
+
+
+ ); +}); + +FriendListItem.displayName = "FriendListItem"; + +interface TwoColumnSelectionProps { + visible: boolean; + onCancel: () => void; + onConfirm: ( + selectedIds: string[], + selectedItems: FriendSelectionItem[], + ) => void; + title?: string; + deviceIds?: number[]; + enableDeviceFilter?: boolean; + dataSource?: FriendSelectionItem[]; +} + +const TwoColumnSelection: React.FC = ({ + visible, + onCancel, + onConfirm, + title = "选择好友", + deviceIds = [], + enableDeviceFilter = true, + dataSource, +}) => { + const [rawFriends, setRawFriends] = useState([]); + const [selectedFriends, setSelectedFriends] = useState( + [], + ); + const [searchQuery, setSearchQuery] = useState(""); + const [loading, setLoading] = useState(false); + const [currentPage, setCurrentPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + + // 使用 useMemo 缓存过滤结果,避免每次渲染都重新计算 + const filteredFriends = useMemo(() => { + const sourceData = dataSource || rawFriends; + if (!searchQuery.trim()) { + return sourceData; + } + + const query = searchQuery.toLowerCase(); + return sourceData.filter( + item => + item.name?.toLowerCase().includes(query) || + item.nickname?.toLowerCase().includes(query), + ); + }, [dataSource, rawFriends, searchQuery]); + + // 分页显示好友列表,避免一次性渲染太多项目 + const ITEMS_PER_PAGE = 50; + const [displayPage, setDisplayPage] = useState(1); + + const friends = useMemo(() => { + const startIndex = 0; + const endIndex = displayPage * ITEMS_PER_PAGE; + return filteredFriends.slice(startIndex, endIndex); + }, [filteredFriends, displayPage]); + + const hasMoreFriends = filteredFriends.length > friends.length; + + // 使用 useMemo 缓存选中状态映射,避免每次渲染都重新计算 + const selectedFriendsMap = useMemo(() => { + const map = new Map(); + selectedFriends.forEach(friend => { + map.set(friend.id, true); + }); + return map; + }, [selectedFriends]); + + // 获取好友列表 + const fetchFriends = useCallback( + async (page: number, keyword: string = "") => { + setLoading(true); + try { + const params: any = { + page, + pageSize: 20, + }; + + if (keyword) { + params.keyword = keyword; + } + + if (enableDeviceFilter && deviceIds.length > 0) { + params.deviceIds = deviceIds; + } + + const response = await getFriendList(params); + + if (response.success) { + setRawFriends(response.data.list || []); + setTotalPages(Math.ceil((response.data.total || 0) / 20)); + } else { + setRawFriends([]); + message.error(response.message || "获取好友列表失败"); + } + } catch (error) { + console.error("获取好友列表失败:", error); + message.error("获取好友列表失败"); + } finally { + setLoading(false); + } + }, + [deviceIds, enableDeviceFilter], + ); + + // 初始化数据加载 + useEffect(() => { + if (visible && !dataSource) { + // 只有在没有外部数据源时才调用 API + fetchFriends(1); + setCurrentPage(1); + } + }, [visible, dataSource, fetchFriends]); + + // 重置搜索状态 + useEffect(() => { + if (visible) { + setSearchQuery(""); + setSelectedFriends([]); + setLoading(false); + } + }, [visible]); + + // 防抖搜索处理 + const handleSearch = useCallback(() => { + let timeoutId: NodeJS.Timeout; + return (value: string) => { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + setDisplayPage(1); // 重置分页 + if (!dataSource) { + fetchFriends(1, value); + } + }, 300); + }; + }, [dataSource, fetchFriends])(); + + // API搜索处理(当没有外部数据源时) + const handleApiSearch = useCallback( + async (keyword: string) => { + if (!dataSource) { + await fetchFriends(1, keyword); + } + }, + [dataSource, fetchFriends], + ); + + // 加载更多好友 + const handleLoadMore = useCallback(() => { + setDisplayPage(prev => prev + 1); + }, []); + + // 防抖搜索 + useEffect(() => { + if (!dataSource && searchQuery.trim()) { + const timer = setTimeout(() => { + handleApiSearch(searchQuery); + }, 300); + return () => clearTimeout(timer); + } + }, [searchQuery, dataSource, handleApiSearch]); + + // 选择好友 - 使用 useCallback 优化性能 + const handleSelectFriend = useCallback((friend: FriendSelectionItem) => { + setSelectedFriends(prev => { + const isSelected = prev.some(f => f.id === friend.id); + if (isSelected) { + return prev.filter(f => f.id !== friend.id); + } else { + return [...prev, friend]; + } + }); + }, []); + + // 移除已选好友 - 使用 useCallback 优化性能 + const handleRemoveFriend = useCallback((friendId: number) => { + setSelectedFriends(prev => prev.filter(f => f.id !== friendId)); + }, []); + + // 确认选择 - 使用 useCallback 优化性能 + const handleConfirmSelection = useCallback(() => { + const selectedIds = selectedFriends.map(f => f.id.toString()); + onConfirm(selectedIds, selectedFriends); + setSelectedFriends([]); + setSearchQuery(""); + }, [selectedFriends, onConfirm]); + + // 取消选择 - 使用 useCallback 优化性能 + const handleCancel = useCallback(() => { + setSelectedFriends([]); + setSearchQuery(""); + onCancel(); + }, [onCancel]); + + return ( + + 取消 + , + , + ]} + className={styles.twoColumnModal} + > +
+ {/* 左侧:好友列表 */} +
+
+ { + const value = e.target.value; + setSearchQuery(value); // 立即更新显示 + handleSearch(value); // 防抖处理搜索 + }} + prefix={} + allowClear + /> +
+ +
+ {loading ? ( +
加载中...
+ ) : friends.length > 0 ? ( + // 使用 React.memo 优化列表项渲染 + friends.map(friend => { + const isSelected = selectedFriendsMap.has(friend.id); + return ( + + ); + }) + ) : ( +
+ {searchQuery + ? `没有找到包含"${searchQuery}"的好友` + : "暂无好友"} +
+ )} + + {hasMoreFriends && ( +
+ +
+ )} +
+
+ + {/* 右侧:已选好友 */} +
+
+ 已选联系人 ({selectedFriends.length}) +
+ +
+ {selectedFriends.length > 0 ? ( + selectedFriends.map(friend => ( +
+ + {friend.nickname?.charAt(0)} + +
+
{friend.nickname}
+
+ +
+ )) + ) : ( +
暂无选择
+ )} +
+
+
+
+ ); +}; + +export default TwoColumnSelection; diff --git a/Touchkebao/src/components/UpdateNotification/index.tsx b/Touchkebao/src/components/UpdateNotification/index.tsx new file mode 100644 index 00000000..79f37c84 --- /dev/null +++ b/Touchkebao/src/components/UpdateNotification/index.tsx @@ -0,0 +1,217 @@ +import React, { useState, useEffect } from "react"; +import { Button } from "antd-mobile"; +import { updateChecker } from "@/utils/updateChecker"; +import { ReloadOutlined } from "@ant-design/icons"; + +interface UpdateNotificationProps { + position?: "top" | "bottom"; + autoReload?: boolean; + showToast?: boolean; + forceShow?: boolean; + onClose?: () => void; +} + +const UpdateNotification: React.FC = ({ + position = "top", + autoReload = false, + showToast = true, + forceShow = false, + onClose, +}) => { + const [hasUpdate, setHasUpdate] = useState(false); + const [isVisible, setIsVisible] = useState(false); + + useEffect(() => { + // 注册更新检测回调 + const handleUpdate = (info: { hasUpdate: boolean }) => { + if (info.hasUpdate) { + setHasUpdate(true); + setIsVisible(true); + + if (autoReload) { + // 自动刷新 + setTimeout(() => { + updateChecker.forceReload(); + }, 3000); + } + } + }; + + updateChecker.onUpdate(handleUpdate); + + // 启动更新检测 + updateChecker.start(); + + return () => { + updateChecker.offUpdate(handleUpdate); + updateChecker.stop(); + }; + }, [autoReload, showToast]); + const handleReload = () => { + updateChecker.forceReload(); + }; + + const handleLater = () => { + setIsVisible(false); + onClose?.(); + // 10分钟后再次检查 + setTimeout( + () => { + updateChecker.start(); + }, + 10 * 60 * 1000, + ); + }; + + if ((!isVisible || !hasUpdate) && !forceShow) { + return null; + } + + return ( +
+
+ {/* 左侧内容 */} +
+ {/* 更新图标 */} +
+ +
+ {/* 文本信息 */} +
+
+ 发现新版本 +
+
+ 建议立即更新获得更好体验 +
+
+
+ + {/* 右侧按钮组 */} +
+ + +
+
+ + {/* 动画样式 */} + +
+ ); +}; + +export default UpdateNotification; diff --git a/Touchkebao/src/components/Upload/AudioRecorder/index.tsx b/Touchkebao/src/components/Upload/AudioRecorder/index.tsx new file mode 100644 index 00000000..1bcc705e --- /dev/null +++ b/Touchkebao/src/components/Upload/AudioRecorder/index.tsx @@ -0,0 +1,411 @@ +import React, { useState, useRef, useCallback } from "react"; +import { Button, message, Modal } from "antd"; +import { + AudioOutlined, + PlayCircleOutlined, + PauseCircleOutlined, + SendOutlined, + DeleteOutlined, +} from "@ant-design/icons"; +import { uploadFile } from "@/api/common"; + +interface AudioRecorderProps { + onAudioUploaded: (audioData: { url: string; durationMs: number }) => void; + className?: string; + disabled?: boolean; + maxDuration?: number; // 最大录音时长(秒) +} + +type RecordingState = + | "idle" + | "recording" + | "recorded" + | "playing" + | "uploading"; + +const AudioRecorder: React.FC = ({ + onAudioUploaded, + className, + disabled = false, + maxDuration = 60, +}) => { + const [visible, setVisible] = useState(false); + const [state, setState] = useState("idle"); + const [recordingTime, setRecordingTime] = useState(0); + const [audioBlob, setAudioBlob] = useState(null); + const [audioUrl, setAudioUrl] = useState(""); + + const mediaRecorderRef = useRef(null); + const audioRef = useRef(null); + const timerRef = useRef(null); + const chunksRef = useRef([]); + + // 打开弹窗 + const openRecorder = () => { + setVisible(true); + }; + + // 关闭弹窗并重置状态 + const closeRecorder = () => { + if (state === "recording") { + stopRecording(); + } + if (state === "playing") { + pauseAudio(); + } + deleteRecording(); + setVisible(false); + }; + + // 开始录音 + const startRecording = useCallback(async () => { + try { + const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + + // 尝试使用MP3格式,如果不支持则回退到WebM + const mp3Types = [ + "audio/mpeg", + "audio/mp3", + "audio/mpeg; codecs=mp3", + "audio/mp4", + "audio/mp4; codecs=mp4a.40.2", + ]; + + let mimeType = "audio/webm"; // 默认回退格式 + + // 检测并选择支持的MP3格式 + for (const type of mp3Types) { + if (MediaRecorder.isTypeSupported(type)) { + mimeType = type; + console.log(`使用音频格式: ${type}`); + break; + } + } + + if (mimeType === "audio/webm") { + console.log("浏览器不支持MP3格式,使用WebM格式"); + } + + const mediaRecorder = new MediaRecorder(stream, { mimeType }); + mediaRecorderRef.current = mediaRecorder; + chunksRef.current = []; + + mediaRecorder.ondataavailable = event => { + if (event.data.size > 0) { + chunksRef.current.push(event.data); + } + }; + + mediaRecorder.onstop = () => { + const blob = new Blob(chunksRef.current, { type: mimeType }); + setAudioBlob(blob); + const url = URL.createObjectURL(blob); + setAudioUrl(url); + setState("recorded"); + + // 停止所有音频轨道 + stream.getTracks().forEach(track => track.stop()); + }; + + mediaRecorder.start(); + setState("recording"); + setRecordingTime(0); + + // 开始计时 + timerRef.current = setInterval(() => { + setRecordingTime(prev => { + const newTime = prev + 1; + if (newTime >= maxDuration) { + stopRecording(); + } + return newTime; + }); + }, 1000); + } catch (error) { + console.error("录音失败:", error); + message.error("无法访问麦克风,请检查权限设置"); + } + }, [maxDuration]); + + // 停止录音 + const stopRecording = useCallback(() => { + if ( + mediaRecorderRef.current && + mediaRecorderRef.current.state === "recording" + ) { + mediaRecorderRef.current.stop(); + } + if (timerRef.current) { + clearInterval(timerRef.current); + timerRef.current = null; + } + }, []); + + // 播放录音 + const playAudio = useCallback(() => { + if (audioRef.current && audioUrl) { + audioRef.current.play(); + setState("playing"); + } + }, [audioUrl]); + + // 暂停播放 + const pauseAudio = useCallback(() => { + if (audioRef.current) { + audioRef.current.pause(); + setState("recorded"); + } + }, []); + + // 删除录音 + const deleteRecording = useCallback(() => { + if (audioUrl) { + URL.revokeObjectURL(audioUrl); + } + setAudioBlob(null); + setAudioUrl(""); + setRecordingTime(0); + setState("idle"); + }, [audioUrl]); + + // 发送录音 + const sendAudio = useCallback(async () => { + if (!audioBlob) return; + + try { + setState("uploading"); + + // 创建文件对象 + const timestamp = Date.now(); + const fileExtension = + audioBlob.type.includes("mp3") || + audioBlob.type.includes("mpeg") || + audioBlob.type.includes("mp4") + ? "mp3" + : "webm"; + const audioFile = new File( + [audioBlob], + `audio_${timestamp}.${fileExtension}`, + { + type: audioBlob.type, + }, + ); + + // 打印文件格式信息 + console.log("音频文件信息:", { + fileName: audioFile.name, + fileType: audioFile.type, + fileSize: audioFile.size, + fileExtension: fileExtension, + blobType: audioBlob.type, + }); + + // 上传文件 + const filePath = await uploadFile(audioFile); + + // 调用回调函数,传递音频URL和时长(毫秒) + onAudioUploaded({ + url: filePath, + durationMs: recordingTime * 1000, // 将秒转换为毫秒 + }); + + // 重置状态并关闭弹窗 + deleteRecording(); + setVisible(false); + message.success("语音发送成功"); + } catch (error) { + console.error("语音上传失败:", error); + message.error("语音发送失败,请重试"); + setState("recorded"); + } + }, [audioBlob, onAudioUploaded, deleteRecording]); + + // 格式化时间显示 + const formatTime = (seconds: number) => { + const mins = Math.floor(seconds / 60); + const secs = seconds % 60; + return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`; + }; + + // 渲染弹窗内容 + const renderModalContent = () => { + switch (state) { + case "idle": + return ( +
+
+ 点击下方按钮开始录音 +
+
+ ); + + case "recording": + return ( +
+
+
+ {formatTime(recordingTime)} +
+
+ 正在录音中... +
+
+ +
+ ); + + case "recorded": + case "playing": + return ( +
+
+
+ 录音时长: {formatTime(recordingTime)} +
+
+ {state === "playing" + ? "正在播放..." + : "录音完成,可以试听或发送"} +
+
+ +
+
+ +
+ +
+
+ ); + + case "uploading": + return ( +
+ +
+ ); + + default: + return null; + } + }; + + return ( + <> +