commit fcb43c0cbde3ef15bedc0fccc381dd372b47f203 Author: zhengw <247276359@qq.com> Date: Mon Jan 5 17:06:16 2026 +0800 新项目, antd6, react19 diff --git a/.env b/.env new file mode 100644 index 0000000..1e44210 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +VITE_APP_WS_DEV=ws://192.168.1.224:8282 +VITE_APP_WS_PRO=wss://ws.ebaozan.com:8181 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f71003b --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +/public/ver.txt +/.cfg.ts +stats.html diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..03dd2db --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,53 @@ +{ + "window.title": "免费ERP后台系统${separator}${rootName}${separator}${profileName}", + "editor.formatOnSave": true, // 确保保存文件时自动格式化 + "files.eol": "\r\n", + "files.insertFinalNewline": true, // 保存时自动添加换行符 + "files.trimTrailingWhitespace": true, // 保存时自动删除行尾空格 + "editor.rulers": [120], + "editor.codeActionsOnSave": { + "source.organizeImports.biome": "explicit", + "source.fixAll.biome": "explicit", + "source.removeUnusedImports": "explicit" + // 移除未使用的代码 + // "source.removeUnused": "explicit", + }, + "auto-close-tag.enableAutoCloseTag": true, + "[typescriptreact]": { + "editor.defaultFormatter": "biomejs.biome", + "editor.codeActionsOnSave": { + "source.organizeImports": "never" + } + }, + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome", + "editor.codeActionsOnSave": { + "source.organizeImports": "never" + } + }, + "[css]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[jsonc]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[json]": { + "editor.defaultFormatter": "vscode.json-language-features" + }, + // 配置@的路径提示 + "path-autocomplete.pathMappings": { + "@": "${folder}/src" + }, + // "editor.codeActionsOnSave": { + // "source.organizeImports": "explicit" + // }, + "search.exclude": { + "**/node_modules": true, + "**/dist": true, + "**/README.md": true, + "**/stats.html": true, + "**/pnpm-lock.yaml": true, + "**/.cfg.ts": true + }, + "cSpell.words": ["freeerp", "NUMS", "Popconfirm", "qrcode", "Sider", "valtio"] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..5485b62 --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +# React + TypeScript + Vite + Arco Design + +# 项目运行 About 组件报错, 运行一次下面命令生成.cfg.ts 文件, 后面就可以直接用 vite 命令 + +`$ npm run dev` + +# 为了权限判断, 不要使用路径参数, 请使用查询参数 + +# 路由库在 router 文件夹 + +# 错误边界库 ErrorBoundary.tsx + +# 自己搭建的没有详细文档哦 + +- [React](https://react.dev/) +- [Vite](https://cn.vitejs.dev/) +- [TypeScript](https://www.typescriptlang.org/zh/) +- [Ant Design](https://ant-design.antgroup.com/components/overview-cn/) UI 库 +- [zustand](https://github.com/pmndrs/zustand) 状态管理库 +- [dnd-kit](https://github.com/clauderic/dnd-kit) 拖拽插件 + +# vscode 安装插件 + +- 语法校验和格式化插件 + `Biome` + +# 安装 pnpm + +`$ npm install -g pnpm` + +# 安装 + +`$ pnpm install 或 pnpm i` + +# 启动 + +`$ vite` + +http://localhost:4000/ + +# 打包 + +`$ vite build` + +# 用 biomejs 替换 eslint 和 prettier, vscode 需要安装 Biome 插件 + +[biome 文档](https://biomejs.dev/guides/getting-started/) + +# 文件目录(简单) + +``` +. +├── README.md +├── ice.config.mts # The project config +├── package.json +├── public +│   ├── favicon.ico +├── src +| ├── assets +| ├── components # 组件 +| ├── index.html # 入口页面 +| ├── global.css # 全局样式 +| ├── interfaces # 定义类型 +| ├── layout # 布局 +| ├── pages # 页面 +| ├── services # 后台api +| ├── store # store文件夹 +| ├── routes # 路由文件夹(路由和左侧导航菜单) +| └── typings.d.ts +└── tsconfig.json +``` + +# 请求封装 + +- useRequest(url: string, options) 参数传地址 +- useRequest2(request: any, options) 参数传请求 + +# 多语言实现 i18next diff --git a/biome.jsonc b/biome.jsonc new file mode 100644 index 0000000..e7a0af2 --- /dev/null +++ b/biome.jsonc @@ -0,0 +1,57 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.3.10/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": false + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 120, + "attributePosition": "auto" + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "suspicious": { + "noExplicitAny": "off", + "noDoubleEquals": "off" + }, + "style": { + "noNonNullAssertion": "off" + }, + "a11y": { + "noStaticElementInteractions": "off", + "useKeyWithClickEvents": "off" + }, + "correctness": { + "useExhaustiveDependencies": "off" + } + } + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "semicolons": "always", + "arrowParentheses": "always", + "jsxQuoteStyle": "single", + "bracketSpacing": true, + "lineEnding": "crlf", + "trailingCommas": "all" + } + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + } +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..4843ab5 --- /dev/null +++ b/index.html @@ -0,0 +1,114 @@ + + + + + + + + + + + +
+ +
+
+
+
+
+
+ 正在加载系统资源,请耐心等待 +
+
+
+ + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..8128dab --- /dev/null +++ b/package.json @@ -0,0 +1,51 @@ +{ + "name": "freeerp-admin", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "node src/utils/update.js && vite", + "debug": "vite --debug", + "build": "node src/utils/update.js && vite build --mode development", + "build:pro": "node src/utils/update.js && vite build --mode production", + "// check": "运行format, lint等, 并应用安全建议(不会修改代码)", + "check": "npx @biomejs/biome check --write ./src", + "// lint": "校验文件", + "lint": "npx @biomejs/biome lint --write ./src", + "// format": "格式化文件", + "format": "npx @biomejs/biome format --write ./src", + "// write": "运行format, lint等, 并应用建议修改代码!!!!!!!!!!", + "write": "npx @biomejs/biome check --write-unsafe ./src", + "preview": "vite preview" + }, + "dependencies": { + "@ant-design/icons": "~5.6.1", + "antd": "^6.1.2", + "big.js": "^7.0.1", + "dayjs": "^1.11.19", + "qs": "^6.14.0", + "react": "^19.2.3", + "react-dom": "^19.2.3", + "valtio": "^2.2.0", + "zustand": "^5.0.9" + }, + "devDependencies": { + "@biomejs/biome": "2.3.10", + "@types/big.js": "^6.2.2", + "@types/node": "^25.0.3", + "@types/qs": "^6.14.0", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react-swc": "^4.2.2", + "globals": "^16.5.0", + "rollup-plugin-visualizer": "^6.0.5", + "typescript": "~5.9.3", + "vite": "npm:rolldown-vite@7.3.0" + }, + "overrides": { + "vite": "npm:rolldown-vite@7.3.0" + }, + "volta": { + "node": "22.18.0" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..5498f55 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2124 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@ant-design/icons': + specifier: ~5.6.1 + version: 5.6.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + antd: + specifier: ^6.1.2 + version: 6.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + big.js: + specifier: ^7.0.1 + version: 7.0.1 + dayjs: + specifier: ^1.11.19 + version: 1.11.19 + qs: + specifier: ^6.14.0 + version: 6.14.0 + react: + specifier: ^19.2.3 + version: 19.2.3 + react-dom: + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) + valtio: + specifier: ^2.2.0 + version: 2.2.0(@types/react@19.2.7)(react@19.2.3) + zustand: + specifier: ^5.0.9 + version: 5.0.9(@types/react@19.2.7)(react@19.2.3) + devDependencies: + '@biomejs/biome': + specifier: 2.3.10 + version: 2.3.10 + '@types/big.js': + specifier: ^6.2.2 + version: 6.2.2 + '@types/node': + specifier: ^25.0.3 + version: 25.0.3 + '@types/qs': + specifier: ^6.14.0 + version: 6.14.0 + '@types/react': + specifier: ^19.2.7 + version: 19.2.7 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.7) + '@vitejs/plugin-react-swc': + specifier: ^4.2.2 + version: 4.2.2(rolldown-vite@7.3.0(@types/node@25.0.3)) + globals: + specifier: ^16.5.0 + version: 16.5.0 + rollup-plugin-visualizer: + specifier: ^6.0.5 + version: 6.0.5(rolldown@1.0.0-beta.53) + typescript: + specifier: ~5.9.3 + version: 5.9.3 + vite: + specifier: npm:rolldown-vite@7.3.0 + version: rolldown-vite@7.3.0(@types/node@25.0.3) + +packages: + + '@ant-design/colors@7.2.1': + resolution: {integrity: sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==} + + '@ant-design/colors@8.0.0': + resolution: {integrity: sha512-6YzkKCw30EI/E9kHOIXsQDHmMvTllT8STzjMb4K2qzit33RW2pqCJP0sk+hidBntXxE+Vz4n1+RvCTfBw6OErw==} + + '@ant-design/cssinjs-utils@2.0.2': + resolution: {integrity: sha512-Mq3Hm6fJuQeFNKSp3+yT4bjuhVbdrsyXE2RyfpJFL0xiYNZdaJ6oFaE3zFrzmHbmvTd2Wp3HCbRtkD4fU+v2ZA==} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + '@ant-design/cssinjs@2.0.1': + resolution: {integrity: sha512-Lw1Z4cUQxdMmTNir67gU0HCpTl5TtkKCJPZ6UBvCqzcOTl/QmMFB6qAEoj8qFl0CuZDX9qQYa3m9+rEKfaBSbA==} + 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/fast-color@3.0.0': + resolution: {integrity: sha512-eqvpP7xEDm2S7dUzl5srEQCBTXZMmY3ekf97zI+M2DHOYyKdJGH0qua0JACHTqbkRnD/KHFQP9J1uMJ/XWVzzA==} + 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/icons@6.1.0': + resolution: {integrity: sha512-KrWMu1fIg3w/1F2zfn+JlfNDU8dDqILfA5Tg85iqs1lf8ooyGlbkA+TkwfOKKgqpUmAiRY1PTFpuOU2DAIgSUg==} + engines: {node: '>=8'} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@ant-design/react-slick@2.0.0': + resolution: {integrity: sha512-HMS9sRoEmZey8LsE/Yo6+klhlzU12PisjrVcydW3So7RdklyEd2qehyU6a7Yp+OYN72mgsYs3NFCyP2lCPFVqg==} + peerDependencies: + react: ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + + '@biomejs/biome@2.3.10': + resolution: {integrity: sha512-/uWSUd1MHX2fjqNLHNL6zLYWBbrJeG412/8H7ESuK8ewoRoMPUgHDebqKrPTx/5n6f17Xzqc9hdg3MEqA5hXnQ==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.3.10': + resolution: {integrity: sha512-M6xUjtCVnNGFfK7HMNKa593nb7fwNm43fq1Mt71kpLpb+4mE7odO8W/oWVDyBVO4ackhresy1ZYO7OJcVo/B7w==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.3.10': + resolution: {integrity: sha512-Vae7+V6t/Avr8tVbFNjnFSTKZogZHFYl7MMH62P/J1kZtr0tyRQ9Fe0onjqjS2Ek9lmNLmZc/VR5uSekh+p1fg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.3.10': + resolution: {integrity: sha512-B9DszIHkuKtOH2IFeeVkQmSMVUjss9KtHaNXquYYWCjH8IstNgXgx5B0aSBQNr6mn4RcKKRQZXn9Zu1rM3O0/A==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@2.3.10': + resolution: {integrity: sha512-hhPw2V3/EpHKsileVOFynuWiKRgFEV48cLe0eA+G2wO4SzlwEhLEB9LhlSrVeu2mtSn205W283LkX7Fh48CaxA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@2.3.10': + resolution: {integrity: sha512-QTfHZQh62SDFdYc2nfmZFuTm5yYb4eO1zwfB+90YxUumRCR171tS1GoTX5OD0wrv4UsziMPmrePMtkTnNyYG3g==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@2.3.10': + resolution: {integrity: sha512-wwAkWD1MR95u+J4LkWP74/vGz+tRrIQvr8kfMMJY8KOQ8+HMVleREOcPYsQX82S7uueco60L58Wc6M1I9WA9Dw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@2.3.10': + resolution: {integrity: sha512-o7lYc9n+CfRbHvkjPhm8s9FgbKdYZu5HCcGVMItLjz93EhgJ8AM44W+QckDqLA9MKDNFrR8nPbO4b73VC5kGGQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.3.10': + resolution: {integrity: sha512-pHEFgq7dUEsKnqG9mx9bXihxGI49X+ar+UBrEIj3Wqj3UCZp1rNgV+OoyjFgcXsjCWpuEAF4VJdkZr3TrWdCbQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@emnapi/core@1.7.1': + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} + + '@emnapi/runtime@1.7.1': + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@emotion/hash@0.8.0': + resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} + + '@emotion/unitless@0.7.5': + resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} + + '@napi-rs/wasm-runtime@1.1.0': + resolution: {integrity: sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==} + + '@oxc-project/runtime@0.101.0': + resolution: {integrity: sha512-t3qpfVZIqSiLQ5Kqt/MC4Ge/WCOGrrcagAdzTcDaggupjiGxUx4nJF2v6wUCXWSzWHn5Ns7XLv13fCJEwCOERQ==} + engines: {node: ^20.19.0 || >=22.12.0} + + '@oxc-project/types@0.101.0': + resolution: {integrity: sha512-nuFhqlUzJX+gVIPPfuE6xurd4lST3mdcWOhyK/rZO0B9XWMKm79SuszIQEnSMmmDhq1DC8WWVYGVd+6F93o1gQ==} + + '@rc-component/async-validator@5.0.4': + resolution: {integrity: sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==} + engines: {node: '>=14.x'} + + '@rc-component/cascader@1.9.0': + resolution: {integrity: sha512-2jbthe1QZrMBgtCvNKkJFjZYC3uKl4N/aYm5SsMvO3T+F+qRT1CGsSM9bXnh1rLj7jDk/GK0natShWF/jinhWQ==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/checkbox@1.0.1': + resolution: {integrity: sha512-08yTH8m+bSm8TOqbybbJ9KiAuIATti6bDs2mVeSfu4QfEnyeF6X0enHVvD1NEAyuBWEAo56QtLe++MYs2D9XiQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/collapse@1.1.2': + resolution: {integrity: sha512-ilBYk1dLLJHu5Q74dF28vwtKUYQ42ZXIIDmqTuVy4rD8JQVvkXOs+KixVNbweyuIEtJYJ7+t+9GVD9dPc6N02w==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/color-picker@3.0.3': + resolution: {integrity: sha512-V7gFF9O7o5XwIWafdbOtqI4BUUkEUkgdBwp6favy3xajMX/2dDqytFaiXlcwrpq6aRyPLp5dKLAG5RFKLXMeGA==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/context@2.0.1': + resolution: {integrity: sha512-HyZbYm47s/YqtP6pKXNMjPEMaukyg7P0qVfgMLzr7YiFNMHbK2fKTAGzms9ykfGHSfyf75nBbgWw+hHkp+VImw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/dialog@1.5.1': + resolution: {integrity: sha512-by4Sf/a3azcb89WayWuwG19/Y312xtu8N81HoVQQtnsBDylfs+dog98fTAvLinnpeoWG52m/M7QLRW6fXR3l1g==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/drawer@1.3.0': + resolution: {integrity: sha512-rE+sdXEmv2W25VBQ9daGbnb4J4hBIEKmdbj0b3xpY+K7TUmLXDIlSnoXraIbFZdGyek9WxxGKK887uRnFgI+pQ==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/dropdown@1.0.2': + resolution: {integrity: sha512-6PY2ecUSYhDPhkNHHb4wfeAya04WhpmUSKzdR60G+kMNVUCX2vjT/AgTS0Lz0I/K6xrPMJ3enQbwVpeN3sHCgg==} + peerDependencies: + react: '>=16.11.0' + react-dom: '>=16.11.0' + + '@rc-component/form@1.6.0': + resolution: {integrity: sha512-A7vrN8kExtw4sW06mrsgCb1rowhvBFFvQU6Bk/NL0Fj6Wet/5GF0QnGCxBu/sG3JI9FEhsJWES0D44BW2d0hzg==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/image@1.5.3': + resolution: {integrity: sha512-/NR7QW9uCN8Ugar+xsHZOPvzPySfEhcW2/vLcr7VPRM+THZMrllMRv7LAUgW7ikR+Z67Ab67cgPp5K5YftpJsQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/input-number@1.6.2': + resolution: {integrity: sha512-Gjcq7meZlCOiWN1t1xCC+7/s85humHVokTBI7PJgTfoyw5OWF74y3e6P8PHX104g9+b54jsodFIzyaj6p8LI9w==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/input@1.1.2': + resolution: {integrity: sha512-Q61IMR47piUBudgixJ30CciKIy9b1H95qe7GgEKOmSJVJXvFRWJllJfQry9tif+MX2cWFXWJf/RXz4kaCeq/Fg==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@rc-component/mentions@1.6.0': + resolution: {integrity: sha512-KIkQNP6habNuTsLhUv0UGEOwG67tlmE7KNIJoQZZNggEZl5lQJTytFDb69sl5CK3TDdISCTjKP3nGEBKgT61CQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/menu@1.2.0': + resolution: {integrity: sha512-VWwDuhvYHSnTGj4n6bV3ISrLACcPAzdPOq3d0BzkeiM5cve8BEYfvkEhNoM0PLzv51jpcejeyrLXeMVIJ+QJlg==} + 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/motion@1.1.6': + resolution: {integrity: sha512-aEQobs/YA0kqRvHIPjQvOytdtdRVyhf/uXAal4chBjxDu6odHckExJzjn2D+Ju1aKK6hx3pAs6BXdV9+86xkgQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/mutate-observer@2.0.1': + resolution: {integrity: sha512-AyarjoLU5YlxuValRi+w8JRH2Z84TBbFO2RoGWz9d8bSu0FqT8DtugH3xC3BV7mUwlmROFauyWuXFuq4IFbH+w==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/notification@1.2.0': + resolution: {integrity: sha512-OX3J+zVU7rvoJCikjrfW7qOUp7zlDeFBK2eA3SFbGSkDqo63Sl4Ss8A04kFP+fxHSxMDIS9jYVEZtU1FNCFuBA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/overflow@1.0.0': + resolution: {integrity: sha512-GSlBeoE0XTBi5cf3zl8Qh7Uqhn7v8RrlJ8ajeVpEkNe94HWy5l5BQ0Mwn2TVUq9gdgbfEMUmTX7tJFAg7mz0Rw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/pagination@1.2.0': + resolution: {integrity: sha512-YcpUFE8dMLfSo6OARJlK6DbHHvrxz7pMGPGmC/caZSJJz6HRKHC1RPP001PRHCvG9Z/veD039uOQmazVuLJzlw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/picker@1.9.0': + resolution: {integrity: sha512-OLisdk8AWVCG9goBU1dWzuH5QlBQk8jktmQ6p0/IyBFwdKGwyIZOSjnBYo8hooHiTdl0lU+wGf/OfMtVBw02KQ==} + engines: {node: '>=12.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-component/portal@2.0.1': + resolution: {integrity: sha512-46KYuA7Udb1LAaLIdDrfmDz3wzyeEZxIURJCn+heoQVbhtW5PQkhBSQtRus+DUdsknmTFQulxSnqrbX3CI4yXw==} + engines: {node: '>=12.x'} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/progress@1.0.2': + resolution: {integrity: sha512-WZUnH9eGxH1+xodZKqdrHke59uyGZSWgj5HBM5Kwk5BrTMuAORO7VJ2IP5Qbm9aH3n9x3IcesqHHR0NWPBC7fQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/qrcode@1.1.1': + resolution: {integrity: sha512-LfLGNymzKdUPjXUbRP+xOhIWY4jQ+YMj5MmWAcgcAq1Ij8XP7tRmAXqyuv96XvLUBE/5cA8hLFl9eO1JQMujrA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/rate@1.0.1': + resolution: {integrity: sha512-bkXxeBqDpl5IOC7yL7GcSYjQx9G8H+6kLYQnNZWeBYq2OYIv1MONd6mqKTjnnJYpV0cQIU2z3atdW0j1kttpTw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/resize-observer@1.0.1': + resolution: {integrity: sha512-r+w+Mz1EiueGk1IgjB3ptNXLYSLZ5vnEfKHH+gfgj7JMupftyzvUUl3fRcMZe5uMM04x0n8+G2o/c6nlO2+Wag==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/segmented@1.3.0': + resolution: {integrity: sha512-5J/bJ01mbDnoA6P/FW8SxUvKn+OgUSTZJPzCNnTBntG50tzoP7DydGhqxp7ggZXZls7me3mc2EQDXakU3iTVFg==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@rc-component/select@1.3.6': + resolution: {integrity: sha512-CzbJ9TwmWcF5asvTMZ9BMiTE9CkkrigeOGRPpzCNmeZP7KBwwmYrmOIiKh9tMG7d6DyGAEAQ75LBxzPx+pGTHA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '*' + react-dom: '*' + + '@rc-component/slider@1.0.1': + resolution: {integrity: sha512-uDhEPU1z3WDfCJhaL9jfd2ha/Eqpdfxsn0Zb0Xcq1NGQAman0TWaR37OWp2vVXEOdV2y0njSILTMpTfPV1454g==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/steps@1.2.2': + resolution: {integrity: sha512-/yVIZ00gDYYPHSY0JP+M+s3ZvuXLu2f9rEjQqiUDs7EcYsUYrpJ/1bLj9aI9R7MBR3fu/NGh6RM9u2qGfqp+Nw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/switch@1.0.3': + resolution: {integrity: sha512-Jgi+EbOBquje/XNdofr7xbJQZPYJP+BlPfR0h+WN4zFkdtB2EWqEfvkXJWeipflwjWip0/17rNbxEAqs8hVHfw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/table@1.9.0': + resolution: {integrity: sha512-cq3P9FkD+F3eglkFYhBuNlHclg+r4jY8+ZIgK7zbEFo6IwpnA77YL/Gq4ensLw9oua3zFCTA6JDu6YgBei0TxA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/tabs@1.7.0': + resolution: {integrity: sha512-J48cs2iBi7Ho3nptBxxIqizEliUC+ExE23faspUQKGQ550vaBlv3aGF8Epv/UB1vFWeoJDTW/dNzgIU0Qj5i/w==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/textarea@1.1.2': + resolution: {integrity: sha512-9rMUEODWZDMovfScIEHXWlVZuPljZ2pd1LKNjslJVitn4SldEzq5vO1CL3yy3Dnib6zZal2r2DPtjy84VVpF6A==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/tooltip@1.4.0': + resolution: {integrity: sha512-8Rx5DCctIlLI4raR0I0xHjVTf1aF48+gKCNeAAo5bmF5VoR5YED+A/XEqzXv9KKqrJDRcd3Wndpxh2hyzrTtSg==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/tour@2.2.1': + resolution: {integrity: sha512-BUCrVikGJsXli38qlJ+h2WyDD6dYxzDA9dV3o0ij6gYhAq6ooT08SUMWOikva9v4KZ2BEuluGl5bPcsjrSoBgQ==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/tree-select@1.4.0': + resolution: {integrity: sha512-I3UAlO2hNqy9CSKc8EBaESgnmKk2QaRzuZ2XHZGFCgsSMkGl06mdF97sVfROM02YIb64ocgLKefsjE0Ch4ocwQ==} + peerDependencies: + react: '*' + react-dom: '*' + + '@rc-component/tree@1.1.0': + resolution: {integrity: sha512-HZs3aOlvFgQdgrmURRc/f4IujiNBf4DdEeXUlkS0lPoLlx9RoqsZcF0caXIAMVb+NaWqKtGQDnrH8hqLCN5zlA==} + engines: {node: '>=10.x'} + peerDependencies: + react: '*' + react-dom: '*' + + '@rc-component/trigger@3.7.2': + resolution: {integrity: sha512-25x+D2k9SAkaK/MNMNmv2Nlv8FH1D9RtmjoMoLEw1Cid+sMV4pAAT5k49ku59UeXaOA1qwLUVrBUMq4A6gUSsQ==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/upload@1.1.0': + resolution: {integrity: sha512-LIBV90mAnUE6VK5N4QvForoxZc4XqEYZimcp7fk+lkE4XwHHyJWxpIXQQwMU8hJM+YwBbsoZkGksL1sISWHQxw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/util@1.6.2': + resolution: {integrity: sha512-RPQASdThk6oKCaV0wUA4s5nh8Scs+0nSywHASc+XsHrmBUZ284LyvVYpd6Dq+JZSN+IyhY0b/TSfgjPf+Qzicg==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/virtual-list@1.0.2': + resolution: {integrity: sha512-uvTol/mH74FYsn5loDGJxo+7kjkO4i+y4j87Re1pxJBs0FaeuMuLRzQRGaXwnMcV1CxpZLi2Z56Rerj2M00fjQ==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rolldown/binding-android-arm64@1.0.0-beta.53': + resolution: {integrity: sha512-Ok9V8o7o6YfSdTTYA/uHH30r3YtOxLD6G3wih/U9DO0ucBBFq8WPt/DslU53OgfteLRHITZny9N/qCUxMf9kjQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0-beta.53': + resolution: {integrity: sha512-yIsKqMz0CtRnVa6x3Pa+mzTihr4Ty+Z6HfPbZ7RVbk1Uxnco4+CUn7Qbm/5SBol1JD/7nvY8rphAgyAi7Lj6Vg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-beta.53': + resolution: {integrity: sha512-GTXe+mxsCGUnJOFMhfGWmefP7Q9TpYUseHvhAhr21nCTgdS8jPsvirb0tJwM3lN0/u/cg7bpFNa16fQrjKrCjQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-beta.53': + resolution: {integrity: sha512-9Tmp7bBvKqyDkMcL4e089pH3RsjD3SUungjmqWtyhNOxoQMh0fSmINTyYV8KXtE+JkxYMPWvnEt+/mfpVCkk8w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.53': + resolution: {integrity: sha512-a1y5fiB0iovuzdbjUxa7+Zcvgv+mTmlGGC4XydVIsyl48eoxgaYkA3l9079hyTyhECsPq+mbr0gVQsFU11OJAQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.53': + resolution: {integrity: sha512-bpIGX+ov9PhJYV+wHNXl9rzq4F0QvILiURn0y0oepbQx+7stmQsKA0DhPGwmhfvF856wq+gbM8L92SAa/CBcLg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.53': + resolution: {integrity: sha512-bGe5EBB8FVjHBR1mOLOPEFg1Lp3//7geqWkU5NIhxe+yH0W8FVrQ6WRYOap4SUTKdklD/dC4qPLREkMMQ855FA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.53': + resolution: {integrity: sha512-qL+63WKVQs1CMvFedlPt0U9PiEKJOAL/bsHMKUDS6Vp2Q+YAv/QLPu8rcvkfIMvQ0FPU2WL0aX4eWwF6e/GAnA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.53': + resolution: {integrity: sha512-VGl9JIGjoJh3H8Mb+7xnVqODajBmrdOOb9lxWXdcmxyI+zjB2sux69br0hZJDTyLJfvBoYm439zPACYbCjGRmw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-openharmony-arm64@1.0.0-beta.53': + resolution: {integrity: sha512-B4iIserJXuSnNzA5xBLFUIjTfhNy7d9sq4FUMQY3GhQWGVhS2RWWzzDnkSU6MUt7/aHUrep0CdQfXUJI9D3W7A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.53': + resolution: {integrity: sha512-BUjAEgpABEJXilGq/BPh7jeU3WAJ5o15c1ZEgHaDWSz3LB881LQZnbNJHmUiM4d1JQWMYYyR1Y490IBHi2FPJg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.53': + resolution: {integrity: sha512-s27uU7tpCWSjHBnxyVXHt3rMrQdJq5MHNv3BzsewCIroIw3DJFjMH1dzCPPMUFxnh1r52Nf9IJ/eWp6LDoyGcw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.53': + resolution: {integrity: sha512-cjWL/USPJ1g0en2htb4ssMjIycc36RvdQAx1WlXnS6DpULswiUTVXPDesTifSKYSyvx24E0YqQkEm0K/M2Z/AA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-beta.47': + resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==} + + '@rolldown/pluginutils@1.0.0-beta.53': + resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} + + '@swc/core-darwin-arm64@1.15.3': + resolution: {integrity: sha512-AXfeQn0CvcQ4cndlIshETx6jrAM45oeUrK8YeEY6oUZU/qzz0Id0CyvlEywxkWVC81Ajpd8TQQ1fW5yx6zQWkQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.15.3': + resolution: {integrity: sha512-p68OeCz1ui+MZYG4wmfJGvcsAcFYb6Sl25H9TxWl+GkBgmNimIiRdnypK9nBGlqMZAcxngNPtnG3kEMNnvoJ2A==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.15.3': + resolution: {integrity: sha512-Nuj5iF4JteFgwrai97mUX+xUOl+rQRHqTvnvHMATL/l9xE6/TJfPBpd3hk/PVpClMXG3Uvk1MxUFOEzM1JrMYg==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.15.3': + resolution: {integrity: sha512-2Nc/s8jE6mW2EjXWxO/lyQuLKShcmTrym2LRf5Ayp3ICEMX6HwFqB1EzDhwoMa2DcUgmnZIalesq2lG3krrUNw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-arm64-musl@1.15.3': + resolution: {integrity: sha512-j4SJniZ/qaZ5g8op+p1G9K1z22s/EYGg1UXIb3+Cg4nsxEpF5uSIGEE4mHUfA70L0BR9wKT2QF/zv3vkhfpX4g==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-x64-gnu@1.15.3': + resolution: {integrity: sha512-aKttAZnz8YB1VJwPQZtyU8Uk0BfMP63iDMkvjhJzRZVgySmqt/apWSdnoIcZlUoGheBrcqbMC17GGUmur7OT5A==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-linux-x64-musl@1.15.3': + resolution: {integrity: sha512-oe8FctPu1gnUsdtGJRO2rvOUIkkIIaHqsO9xxN0bTR7dFTlPTGi2Fhk1tnvXeyAvCPxLIcwD8phzKg6wLv9yug==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-win32-arm64-msvc@1.15.3': + resolution: {integrity: sha512-L9AjzP2ZQ/Xh58e0lTRMLvEDrcJpR7GwZqAtIeNLcTK7JVE+QineSyHp0kLkO1rttCHyCy0U74kDTj0dRz6raA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.15.3': + resolution: {integrity: sha512-B8UtogMzErUPDWUoKONSVBdsgKYd58rRyv2sHJWKOIMCHfZ22FVXICR4O/VwIYtlnZ7ahERcjayBHDlBZpR0aw==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.15.3': + resolution: {integrity: sha512-SpZKMR9QBTecHeqpzJdYEfgw30Oo8b/Xl6rjSzBt1g0ZsXyy60KLXrp6IagQyfTYqNYE/caDvwtF2FPn7pomog==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.15.3': + resolution: {integrity: sha512-Qd8eBPkUFL4eAONgGjycZXj1jFCBW8Fd+xF0PzdTlBCWQIV1xnUT7B93wUANtW3KGjl3TRcOyxwSx/u/jyKw/Q==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '>=0.5.17' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/types@0.1.25': + resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/big.js@6.2.2': + resolution: {integrity: sha512-e2cOW9YlVzFY2iScnGBBkplKsrn2CsObHQ2Hiw4V1sSyiGbgWL8IyqE3zFi1Pt5o1pdAtYkDAIsF3KKUPjdzaA==} + + '@types/node@25.0.3': + resolution: {integrity: sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==} + + '@types/qs@6.14.0': + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.7': + resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} + + '@vitejs/plugin-react-swc@4.2.2': + resolution: {integrity: sha512-x+rE6tsxq/gxrEJN3Nv3dIV60lFflPj94c90b+NNo6n1QV1QQUTLoL0MpaOVasUZ0zqVBn7ead1B5ecx1JAGfA==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^4 || ^5 || ^6 || ^7 + + 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@6.1.2: + resolution: {integrity: sha512-pqYaZECL/7TBiNxxz+LieLiPCem6DaEzudqN44EZ3SvJjixLP7K41n6clo0zxe/2HiOUe9KxTMxGN+icOkL6Tw==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + big.js@7.0.1: + resolution: {integrity: sha512-iFgV784tD8kq4ccF1xtNMZnXeZzVuXWWM+ERFzKQjv+A5G9HC8CY3DuV45vgzFFcW+u2tIvmF95+AzWgs6BjCg==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + 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==} + + compute-scroll-into-view@3.1.1: + resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + dayjs@1.11.19: + resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + 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-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + 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==} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + 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'} + + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} + engines: {node: '>=18'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-mobile@5.0.0: + resolution: {integrity: sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + json2mq@0.2.0: + resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} + + lightningcss-android-arm64@1.30.2: + resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.30.2: + resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.2: + resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.2: + resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.2: + resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.2: + resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.2: + resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.2: + resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.2: + resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.2: + resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.2: + resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.2: + resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} + engines: {node: '>= 12.0.0'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + proxy-compare@3.0.1: + resolution: {integrity: sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + rc-util@5.44.4: + resolution: {integrity: sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + react-dom@19.2.3: + resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} + peerDependencies: + react: ^19.2.3 + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react@19.2.3: + resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} + engines: {node: '>=0.10.0'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + rolldown-vite@7.3.0: + resolution: {integrity: sha512-5hI5NCJwKBGtzWtdKB3c2fOEpI77Iaa0z4mSzZPU1cJ/OqrGbFafm90edVCd7T9Snz+Sh09TMAv4EQqyVLzuEg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + esbuild: ^0.27.0 + jiti: '>=1.21.0' + less: ^4.0.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 + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + rolldown@1.0.0-beta.53: + resolution: {integrity: sha512-Qd9c2p0XKZdgT5AYd+KgAMggJ8ZmCs3JnS9PTMWkyUfteKlfmKtxJbWTHkVakxwXs1Ub7jrRYVeFeF7N0sQxyw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rollup-plugin-visualizer@6.0.5: + resolution: {integrity: sha512-9+HlNgKCVbJDs8tVtjQ43US12eqaiHyyiLMdBwQ7vSZPiHMysGNo2E88TAp1si5wx8NAoYriI2A5kuKfIakmJg==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + rolldown: 1.x || ^1.0.0-beta + rollup: 2.x || 3.x || 4.x + peerDependenciesMeta: + rolldown: + optional: true + rollup: + optional: true + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + scroll-into-view-if-needed@3.1.0: + resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} + + 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'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + string-convert@0.2.1: + resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + stylis@4.3.6: + resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} + + throttle-debounce@5.0.2: + resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} + engines: {node: '>=12.22'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + valtio@2.2.0: + resolution: {integrity: sha512-l/zzQahUIm+dfUUP9fIecNVEWJLea9shMC1Bb1aK+v4XNOEzoq796Qax+yzMemmqpltuxfH7kPJy62FVGJDEtw==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + react: '>=18.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + zustand@5.0.9: + resolution: {integrity: sha512-ALBtUj0AfjJt3uNRQoL1tL2tMvj6Gp/6e39dnfT6uzpelGru8v1tPOGBzayOWbPJvujM8JojDk3E1LxeFisBNg==} + 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: + + '@ant-design/colors@7.2.1': + dependencies: + '@ant-design/fast-color': 2.0.6 + + '@ant-design/colors@8.0.0': + dependencies: + '@ant-design/fast-color': 3.0.0 + + '@ant-design/cssinjs-utils@2.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@ant-design/cssinjs': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@babel/runtime': 7.28.4 + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@ant-design/cssinjs@2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.4 + '@emotion/hash': 0.8.0 + '@emotion/unitless': 0.7.5 + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + csstype: 3.2.3 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + stylis: 4.3.6 + + '@ant-design/fast-color@2.0.6': + dependencies: + '@babel/runtime': 7.28.4 + + '@ant-design/fast-color@3.0.0': {} + + '@ant-design/icons-svg@4.4.2': {} + + '@ant-design/icons@5.6.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@ant-design/colors': 7.2.1 + '@ant-design/icons-svg': 4.4.2 + '@babel/runtime': 7.28.4 + classnames: 2.5.1 + rc-util: 5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@ant-design/icons@6.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@ant-design/colors': 8.0.0 + '@ant-design/icons-svg': 4.4.2 + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@ant-design/react-slick@2.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.4 + clsx: 2.1.1 + json2mq: 0.2.0 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + throttle-debounce: 5.0.2 + + '@babel/runtime@7.28.4': {} + + '@biomejs/biome@2.3.10': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.3.10 + '@biomejs/cli-darwin-x64': 2.3.10 + '@biomejs/cli-linux-arm64': 2.3.10 + '@biomejs/cli-linux-arm64-musl': 2.3.10 + '@biomejs/cli-linux-x64': 2.3.10 + '@biomejs/cli-linux-x64-musl': 2.3.10 + '@biomejs/cli-win32-arm64': 2.3.10 + '@biomejs/cli-win32-x64': 2.3.10 + + '@biomejs/cli-darwin-arm64@2.3.10': + optional: true + + '@biomejs/cli-darwin-x64@2.3.10': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.3.10': + optional: true + + '@biomejs/cli-linux-arm64@2.3.10': + optional: true + + '@biomejs/cli-linux-x64-musl@2.3.10': + optional: true + + '@biomejs/cli-linux-x64@2.3.10': + optional: true + + '@biomejs/cli-win32-arm64@2.3.10': + optional: true + + '@biomejs/cli-win32-x64@2.3.10': + optional: true + + '@emnapi/core@1.7.1': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.7.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emotion/hash@0.8.0': {} + + '@emotion/unitless@0.7.5': {} + + '@napi-rs/wasm-runtime@1.1.0': + dependencies: + '@emnapi/core': 1.7.1 + '@emnapi/runtime': 1.7.1 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@oxc-project/runtime@0.101.0': {} + + '@oxc-project/types@0.101.0': {} + + '@rc-component/async-validator@5.0.4': + dependencies: + '@babel/runtime': 7.28.4 + + '@rc-component/cascader@1.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/select': 1.3.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/tree': 1.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/checkbox@1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/collapse@1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/color-picker@3.0.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@ant-design/fast-color': 3.0.0 + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/context@2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/dialog@1.5.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/portal': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/drawer@1.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/portal': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/dropdown@1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/trigger': 3.7.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/form@1.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/async-validator': 5.0.4 + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/image@1.5.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/portal': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/input-number@1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/mini-decimal': 1.1.0 + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/input@1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/mentions@1.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/input': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/menu': 1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/textarea': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/trigger': 3.7.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/menu@1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/overflow': 1.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/trigger': 3.7.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/mini-decimal@1.1.0': + dependencies: + '@babel/runtime': 7.28.4 + + '@rc-component/motion@1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/mutate-observer@2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/notification@1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/overflow@1.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/resize-observer': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/pagination@1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/picker@1.9.0(dayjs@1.11.19)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/overflow': 1.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/resize-observer': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/trigger': 3.7.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + dayjs: 1.11.19 + + '@rc-component/portal@2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/progress@1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/qrcode@1.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.4 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/rate@1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/resize-observer@1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/segmented@1.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/select@1.3.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/overflow': 1.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/trigger': 3.7.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/virtual-list': 1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/slider@1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/steps@1.2.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/switch@1.0.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/table@1.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/context': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/resize-observer': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/virtual-list': 1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/tabs@1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/dropdown': 1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/menu': 1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/resize-observer': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/textarea@1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/input': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/resize-observer': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/tooltip@1.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/trigger': 3.7.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/tour@2.2.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/portal': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/trigger': 3.7.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/tree-select@1.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/select': 1.3.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/tree': 1.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/tree@1.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/virtual-list': 1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/trigger@3.7.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/portal': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/resize-observer': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/upload@1.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rc-component/util@1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + is-mobile: 5.0.0 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-is: 18.3.1 + + '@rc-component/virtual-list@1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@babel/runtime': 7.28.4 + '@rc-component/resize-observer': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@rolldown/binding-android-arm64@1.0.0-beta.53': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-beta.53': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-beta.53': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-beta.53': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.53': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.53': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.53': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.53': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.53': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-beta.53': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.53': + dependencies: + '@napi-rs/wasm-runtime': 1.1.0 + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.53': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.53': + optional: true + + '@rolldown/pluginutils@1.0.0-beta.47': {} + + '@rolldown/pluginutils@1.0.0-beta.53': {} + + '@swc/core-darwin-arm64@1.15.3': + optional: true + + '@swc/core-darwin-x64@1.15.3': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.15.3': + optional: true + + '@swc/core-linux-arm64-gnu@1.15.3': + optional: true + + '@swc/core-linux-arm64-musl@1.15.3': + optional: true + + '@swc/core-linux-x64-gnu@1.15.3': + optional: true + + '@swc/core-linux-x64-musl@1.15.3': + optional: true + + '@swc/core-win32-arm64-msvc@1.15.3': + optional: true + + '@swc/core-win32-ia32-msvc@1.15.3': + optional: true + + '@swc/core-win32-x64-msvc@1.15.3': + optional: true + + '@swc/core@1.15.3': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.25 + optionalDependencies: + '@swc/core-darwin-arm64': 1.15.3 + '@swc/core-darwin-x64': 1.15.3 + '@swc/core-linux-arm-gnueabihf': 1.15.3 + '@swc/core-linux-arm64-gnu': 1.15.3 + '@swc/core-linux-arm64-musl': 1.15.3 + '@swc/core-linux-x64-gnu': 1.15.3 + '@swc/core-linux-x64-musl': 1.15.3 + '@swc/core-win32-arm64-msvc': 1.15.3 + '@swc/core-win32-ia32-msvc': 1.15.3 + '@swc/core-win32-x64-msvc': 1.15.3 + + '@swc/counter@0.1.3': {} + + '@swc/types@0.1.25': + dependencies: + '@swc/counter': 0.1.3 + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/big.js@6.2.2': {} + + '@types/node@25.0.3': + dependencies: + undici-types: 7.16.0 + + '@types/qs@6.14.0': {} + + '@types/react-dom@19.2.3(@types/react@19.2.7)': + dependencies: + '@types/react': 19.2.7 + + '@types/react@19.2.7': + dependencies: + csstype: 3.2.3 + + '@vitejs/plugin-react-swc@4.2.2(rolldown-vite@7.3.0(@types/node@25.0.3))': + dependencies: + '@rolldown/pluginutils': 1.0.0-beta.47 + '@swc/core': 1.15.3 + vite: rolldown-vite@7.3.0(@types/node@25.0.3) + transitivePeerDependencies: + - '@swc/helpers' + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + antd@6.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@ant-design/colors': 8.0.0 + '@ant-design/cssinjs': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@ant-design/cssinjs-utils': 2.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@ant-design/fast-color': 3.0.0 + '@ant-design/icons': 6.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@ant-design/react-slick': 2.0.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@babel/runtime': 7.28.4 + '@rc-component/cascader': 1.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/checkbox': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/collapse': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/color-picker': 3.0.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/dialog': 1.5.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/drawer': 1.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/dropdown': 1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/form': 1.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/image': 1.5.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/input': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/input-number': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/mentions': 1.6.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/menu': 1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/motion': 1.1.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/mutate-observer': 2.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/notification': 1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/pagination': 1.2.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/picker': 1.9.0(dayjs@1.11.19)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/progress': 1.0.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/qrcode': 1.1.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/rate': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/resize-observer': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/segmented': 1.3.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/select': 1.3.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/slider': 1.0.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/steps': 1.2.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/switch': 1.0.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/table': 1.9.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/tabs': 1.7.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/textarea': 1.1.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/tooltip': 1.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/tour': 2.2.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/tree': 1.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/tree-select': 1.4.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/trigger': 3.7.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/upload': 1.1.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rc-component/util': 1.6.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + clsx: 2.1.1 + dayjs: 1.11.19 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + scroll-into-view-if-needed: 3.1.0 + throttle-debounce: 5.0.2 + transitivePeerDependencies: + - date-fns + - luxon + - moment + + big.js@7.0.1: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + classnames@2.5.1: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + compute-scroll-into-view@3.1.1: {} + + csstype@3.2.3: {} + + dayjs@1.11.19: {} + + define-lazy-prop@2.0.0: {} + + detect-libc@2.1.2: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + emoji-regex@8.0.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + escalade@3.2.0: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + get-caller-file@2.0.5: {} + + 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 + + globals@16.5.0: {} + + gopd@1.2.0: {} + + has-symbols@1.1.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + is-docker@2.2.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-mobile@5.0.0: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + json2mq@0.2.0: + dependencies: + string-convert: 0.2.1 + + lightningcss-android-arm64@1.30.2: + optional: true + + lightningcss-darwin-arm64@1.30.2: + optional: true + + lightningcss-darwin-x64@1.30.2: + optional: true + + lightningcss-freebsd-x64@1.30.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.2: + optional: true + + lightningcss-linux-arm64-gnu@1.30.2: + optional: true + + lightningcss-linux-arm64-musl@1.30.2: + optional: true + + lightningcss-linux-x64-gnu@1.30.2: + optional: true + + lightningcss-linux-x64-musl@1.30.2: + optional: true + + lightningcss-win32-arm64-msvc@1.30.2: + optional: true + + lightningcss-win32-x64-msvc@1.30.2: + optional: true + + lightningcss@1.30.2: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.30.2 + lightningcss-darwin-arm64: 1.30.2 + lightningcss-darwin-x64: 1.30.2 + lightningcss-freebsd-x64: 1.30.2 + lightningcss-linux-arm-gnueabihf: 1.30.2 + lightningcss-linux-arm64-gnu: 1.30.2 + lightningcss-linux-arm64-musl: 1.30.2 + lightningcss-linux-x64-gnu: 1.30.2 + lightningcss-linux-x64-musl: 1.30.2 + lightningcss-win32-arm64-msvc: 1.30.2 + lightningcss-win32-x64-msvc: 1.30.2 + + math-intrinsics@1.1.0: {} + + nanoid@3.3.11: {} + + object-inspect@1.13.4: {} + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + picocolors@1.1.1: {} + + picomatch@4.0.3: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + proxy-compare@3.0.1: {} + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + rc-util@5.44.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@babel/runtime': 7.28.4 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-is: 18.3.1 + + react-dom@19.2.3(react@19.2.3): + dependencies: + react: 19.2.3 + scheduler: 0.27.0 + + react-is@18.3.1: {} + + react@19.2.3: {} + + require-directory@2.1.1: {} + + rolldown-vite@7.3.0(@types/node@25.0.3): + dependencies: + '@oxc-project/runtime': 0.101.0 + fdir: 6.5.0(picomatch@4.0.3) + lightningcss: 1.30.2 + picomatch: 4.0.3 + postcss: 8.5.6 + rolldown: 1.0.0-beta.53 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 25.0.3 + fsevents: 2.3.3 + + rolldown@1.0.0-beta.53: + dependencies: + '@oxc-project/types': 0.101.0 + '@rolldown/pluginutils': 1.0.0-beta.53 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-beta.53 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.53 + '@rolldown/binding-darwin-x64': 1.0.0-beta.53 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.53 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.53 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.53 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.53 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.53 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.53 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.53 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.53 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.53 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.53 + + rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-beta.53): + dependencies: + open: 8.4.2 + picomatch: 4.0.3 + source-map: 0.7.6 + yargs: 17.7.2 + optionalDependencies: + rolldown: 1.0.0-beta.53 + + scheduler@0.27.0: {} + + scroll-into-view-if-needed@3.1.0: + dependencies: + compute-scroll-into-view: 3.1.1 + + 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 + + source-map-js@1.2.1: {} + + source-map@0.7.6: {} + + string-convert@0.2.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + stylis@4.3.6: {} + + throttle-debounce@5.0.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tslib@2.8.1: + optional: true + + typescript@5.9.3: {} + + undici-types@7.16.0: {} + + valtio@2.2.0(@types/react@19.2.7)(react@19.2.3): + dependencies: + proxy-compare: 3.0.1 + optionalDependencies: + '@types/react': 19.2.7 + react: 19.2.3 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + y18n@5.0.8: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + zustand@5.0.9(@types/react@19.2.7)(react@19.2.3): + optionalDependencies: + '@types/react': 19.2.7 + react: 19.2.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..4216cb8 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +onlyBuiltDependencies: + - '@swc/core' diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..5adaa09 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,72 @@ +import { App as AntdApp, ConfigProvider, notification } from 'antd'; +import type { ArgsProps } from 'antd/es/notification'; +import zhCN from 'antd/locale/zh_CN'; +import { useEffect } from 'react'; +import { headerHeight } from './configs/config'; +import { routes } from './configs/routes'; +import Router from './router/Router'; +import { getDevice } from './utils/common'; +import { notificationEventBus } from './utils/EventBus'; + +function App() { + const [api, contextHolder] = notification.useNotification({ + stack: false, + }); + + useEffect(() => { + window.dfConfig = { + isPhone: getDevice() == 'phone', + tableStickyOffsetHeader: headerHeight, + language: 'zh-cn', + vhUnit: CSS.supports('height', '100dvh') ? 'dvh' : 'vh', + }; + }); + + useEffect(() => { + notificationEventBus.onOpen((data) => { + api.open(data as ArgsProps); + }); + notificationEventBus.onClose((key: string) => { + api.destroy(key); + }); + }, [api]); + + return ( + + {contextHolder} + + + + + ); +} + +export default App; diff --git a/src/assets/loginBg.jpg b/src/assets/loginBg.jpg new file mode 100644 index 0000000..39e5925 Binary files /dev/null and b/src/assets/loginBg.jpg differ diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Footer/MonitorUpdate.tsx b/src/components/Footer/MonitorUpdate.tsx new file mode 100644 index 0000000..5577351 --- /dev/null +++ b/src/components/Footer/MonitorUpdate.tsx @@ -0,0 +1,73 @@ +import { Button } from 'antd'; +import { type FC, useEffect, useRef } from 'react'; +import { Colors } from '@/configs/config'; +import { notificationEventBus } from '@/utils/EventBus'; +import { GapBox } from '../GapBox'; + +/** 监听网站版本更新 */ +const MonitorUpdate: FC = () => { + const timerRef = useRef(null); + const version = useRef(''); + + useEffect(() => { + timerRef.current = setInterval(() => { + if (import.meta.env.DEV) { + return; + } + fetch('/ver.txt') + .then((res) => res.text()) + .then(async (res) => { + if (version.current == '') { + version.current = res; + } + if (res != version.current) { + clearInterval(timerRef.current); + notificationEventBus.emit({ + key: 'MonitorUpdate', + title: '有新版本', + description: ( +
+
发现系统版本更新,请刷新页面
+
+ 刷新前,请先保存页面数据!!! +
+ + + + +
+ ), + duration: 0, + }); + } + }); + }, 60000); + + return () => { + clearInterval(timerRef.current); + }; + }, []); + + return null; +}; +export default MonitorUpdate; diff --git a/src/components/Footer/index.module.css b/src/components/Footer/index.module.css new file mode 100644 index 0000000..98e48ec --- /dev/null +++ b/src/components/Footer/index.module.css @@ -0,0 +1,13 @@ +.footer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 8px 0; + font-size: 12px; +} + +.footer a { + text-decoration: none; + color: #1677ff; +} diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx new file mode 100644 index 0000000..b47364d --- /dev/null +++ b/src/components/Footer/index.tsx @@ -0,0 +1,34 @@ +import type { CSSProperties, FC } from 'react'; +import { DefaultERPName } from '@/configs/config'; +import styles from './index.module.css'; +import MonitorUpdate from './MonitorUpdate'; + +type IProps = { + style?: CSSProperties; +}; + +const filings: any = { + 'd.com': '闽ICP备14007219号-9', +}; + +const Footer: FC = (props) => { + const currentYear = new Date().getFullYear(); + + return ( +
+
+ + +
+
+ © {`${currentYear} ${DefaultERPName}`}  + + {filings[location.hostname] || '闽ICP备14007219号-15'} + +
+ +
+ ); +}; + +export default Footer; diff --git a/src/components/FormPlugin/index.module.css b/src/components/FormPlugin/index.module.css new file mode 100644 index 0000000..bfef852 --- /dev/null +++ b/src/components/FormPlugin/index.module.css @@ -0,0 +1,39 @@ +.item { + margin-bottom: var(--item-margin-bottom, 12px); +} + +.itemLabel { + flex-shrink: 0; + white-space: nowrap; + display: flex; + justify-content: flex-end; + align-items: center; + min-height: 32px; + width: var(--labelWidth, 84px); +} + +.itemLabel.required::before { + color: #ff4d4f; + width: 12px; + font-family: SimSun, sans-serif; + content: "*"; + display: inline-block; + flex-shrink: 0; +} + +.itemLabel.colon::after { + content: ":"; + display: inline-block; + flex-shrink: 0; +} +.itemControl { + display: flex; + min-height: 32px; + align-items: flex-start; + flex-direction: column; + justify-content: center; +} + +.itemControl > div { + width: 100%; +} diff --git a/src/components/FormPlugin/index.tsx b/src/components/FormPlugin/index.tsx new file mode 100644 index 0000000..3487f9c --- /dev/null +++ b/src/components/FormPlugin/index.tsx @@ -0,0 +1,114 @@ +import { Col, Row } from 'antd'; +import type React from 'react'; +import type { FC, PropsWithChildren } from 'react'; +import { Colors } from '@/configs/config'; +import { isNumber } from '@/utils/common.ts'; +import styles from './index.module.css'; + +type ISearchFormPluginProps = { + style?: React.CSSProperties & { + '--labelWidth'?: string; + '--item-margin-bottom'?: string; + }; + children?: React.ReactNode; + labelWidth?: number | string; + itemMarginBottom?: number | string; + gutter?: number; +}; + +/** 响应式栅格 FormItemPlugin默认值 xs = 24, sm = 24, md = 12, lg = 8, xl = 6, xxl = 6*/ +export type ICol = { + /** 窗口宽度 < 576px */ + xs?: number; + /** 窗口宽度 ≥ 576px */ + sm?: number; + /** 窗口宽度 ≥ 768px */ + md?: number; + /** 窗口宽度 ≥ 992px */ + lg?: number; + /** 窗口宽度 ≥ 1200px */ + xl?: number; + /** 窗口宽度 ≥ 1600px */ + xxl?: number; +}; + +/** 单纯表单样式简化版插件 */ +export const FormPlugin: FC> = (props) => { + const { style, children, labelWidth, gutter = 0, itemMarginBottom } = props; + + return ( + + {children} + + ); +}; + +type SearchFormPluginItemProps = { + col?: ICol; + /** 统一设置 col */ + allCol?: number; + style?: React.CSSProperties; + /** 是否显示冒号, 默认为 true */ + colon?: boolean; + label?: React.ReactNode; + labelStyle?: React.CSSProperties; + controlStyle?: React.CSSProperties; + labelWidth?: number | string; + /** 是否必填显示星号 */ + required?: boolean; + errorMsg?: React.ReactNode; + // name?: string; +}; + +export const FormItemPlugin: FC> = (props) => { + const { + col = {}, + style, + colon = true, + label, + labelStyle, + labelWidth, + children, + required, + allCol, + controlStyle, + } = props; + const { xs = 24, sm = 24, md = 12, lg = 8, xl = 6, xxl = 6 } = col; + + return ( + + {label ? ( + + ) : null} +
+ {children} +
+ {props.errorMsg} +
+
+ + ); +}; diff --git a/src/components/GapBox.tsx b/src/components/GapBox.tsx new file mode 100644 index 0000000..625c88d --- /dev/null +++ b/src/components/GapBox.tsx @@ -0,0 +1,28 @@ +import type React from 'react'; +import type { PropsWithChildren } from 'react'; + +type IProps = { + style?: React.CSSProperties; + gap?: number; // 间距 + onClick?: React.MouseEventHandler; + title?: string; +}; + +export const GapBox: React.FC> = ({ children, onClick, style, title, gap }) => { + return ( +
+ {children} +
+ ); +}; diff --git a/src/components/Header/HeaderUserInfo.tsx b/src/components/Header/HeaderUserInfo.tsx new file mode 100644 index 0000000..c2ab48f --- /dev/null +++ b/src/components/Header/HeaderUserInfo.tsx @@ -0,0 +1,23 @@ +import { Button, Popconfirm } from 'antd'; +import { useUserStore } from '@/store/UserStore'; +import { GapBox } from '../GapBox'; + +export const HeaderUserInfo: React.FC = () => { + const userInfo = useUserStore().user; + + return ( + +
{userInfo.login_name}
+ { + location.hash = '#/login'; + }} + > + + +
+ ); +}; diff --git a/src/components/ModalPlugin.tsx b/src/components/ModalPlugin.tsx new file mode 100644 index 0000000..74c1b30 --- /dev/null +++ b/src/components/ModalPlugin.tsx @@ -0,0 +1,52 @@ +import { App, Modal } from 'antd'; +import type { ModalProps } from 'antd/es/modal/interface'; +import type React from 'react'; + +type IProps = ModalProps & { + /** 关闭是否需要确认弹框 boolean */ + confirm?: boolean; + /** 关闭确认弹框内容 React.ReactNode */ + confirmContent?: React.ReactNode; +}; + +/** Modal对话框简单封装, 支持确认退出 + * + * @注意 destroyOnHidden 默认为true与antd文档相反 + * @注意 centered 默认为true与antd文档相反 + * @注意 maskClosable 默认为true与antd文档相反 + * @param confirm 关闭是否需要确认弹框 boolean + * @param confirmContent 关闭确认弹框内容 React.ReactNode + */ +const ModalPlugin: React.FC = (props) => { + const { modal } = App.useApp(); + return ( + { + if (props.confirm) { + modal.confirm({ + transitionName: 'ant-fade', + title: '系统提示', + content: <>{props.confirmContent || '确认关闭?'}, + width: 300, + centered: true, + onOk: async () => { + if (props.onCancel) { + await props.onCancel(event); + } + }, + }); + } else { + props.onCancel?.(event); + } + }} + > + {props.children} + + ); +}; +export default ModalPlugin; diff --git a/src/components/PageContainer/BreadcrumbPlugin.tsx b/src/components/PageContainer/BreadcrumbPlugin.tsx new file mode 100644 index 0000000..992e5b8 --- /dev/null +++ b/src/components/PageContainer/BreadcrumbPlugin.tsx @@ -0,0 +1,77 @@ +import { Breadcrumb } from 'antd'; +import type React from 'react'; +import { GapBox } from '../GapBox'; + +type IProps = { + /** 面包屑内容 */ + items?: string[]; + /** 容器样式 */ + contentStyle?: React.CSSProperties; + /** 面包屑样式 */ + breadcrumbStyle?: React.CSSProperties; +}; + +const BreadcrumbPlugin: React.FC = (props) => { + // const [src, setSrc] = useState(''); + // const user_id = localStorage.getItem('user_id'); + // const company_id = localStorage.getItem('company_id'); + // const storageKey = `likeMenus_c${company_id}_u${user_id}`; + // const [likeMenus, setLikeMenus] = useState(toArray(jsonParse(localStorage.getItem(storageKey)))); + // useEffect(() => { + // aa: for (const el of asideMenuConfig) { + // for (const ell of toArray(el.children)) { + // if (ell.path == lo.pathname) { + // // ! 打开文档跳转 + // setSrc(ell.docUrl); + // break aa; + // } + // } + // } + // }, []); + + // const save = (type: 'add' | 'remove') => { + // if (type == 'add') { + // if (!likeMenus.includes(lo.pathname)) { + // likeMenus.push(lo.pathname); + // } + // } else { + // likeMenus.splice(likeMenus.indexOf(lo.pathname), 1); + // } + // localStorage.setItem(storageKey, JSON.stringify(likeMenus)); + // setLikeMenus([...likeMenus]); + // }; + + return Array.isArray(props.items) ? ( + + ({ title: item }))} + style={{ minHeight: 22, ...props.breadcrumbStyle }} + /> + {/* {!!src && ( + + + } + /> + )} + + ); +}; + +export default PageContainerPlugin; diff --git a/src/components/PaginationPlugin.tsx b/src/components/PaginationPlugin.tsx new file mode 100644 index 0000000..353cc06 --- /dev/null +++ b/src/components/PaginationPlugin.tsx @@ -0,0 +1,64 @@ +import { Pagination } from 'antd'; +import type React from 'react'; + +interface IProps { + current: number | undefined; + pageSize: number | undefined; + total: number | undefined; + onChange: (page: number) => void; + style?: React.CSSProperties; + size?: 'small' | 'default'; + hideOnSinglePage?: boolean; +} + +export const HeaderPagination: React.FC = (props) => { + return ( + { + props.onChange(page); + }} + hideOnSinglePage={props.hideOnSinglePage} + simple + showSizeChanger={false} + size={props.size ?? 'small'} + showTotal={(total) => `共 ${total} 条`} + /> + ); +}; + +interface IProps2 { + current: number | undefined; + pageSize: number | undefined; + total: number | undefined; + onChange: (page: number, pageSize: number) => void; + // onShowSizeChange: (pageSize: number) => void; + size?: 'small' | 'default'; + showSizeChanger?: boolean; + style?: React.CSSProperties; +} + +export const FooterPagination: React.FC = (props) => { + return ( + { + props.onChange(pageSize != props.pageSize ? 1 : page, pageSize); + }} + // onShowSizeChange={(_current, size) => { + // props.onShowSizeChange(size); + // }} + // size={!isPhone ? 'small' : 'default'} + showTotal={(total) => `共 ${total} 条`} + /> + ); +}; diff --git a/src/components/SearchButton.tsx b/src/components/SearchButton.tsx new file mode 100644 index 0000000..cc5a9bb --- /dev/null +++ b/src/components/SearchButton.tsx @@ -0,0 +1,89 @@ +import { DownOutlined, UpOutlined } from '@ant-design/icons'; +import { Button, Input } from 'antd'; +import type React from 'react'; +import { useEffect, useState } from 'react'; + +type IProps = { + style?: React.CSSProperties; + onClick?: () => void; + loading?: boolean; + onlyIcon?: boolean; + type?: 'link' | 'text' | 'primary' | 'default' | 'dashed'; +}; + +export const SearchButton: React.FC = (props) => { + const title = '搜索' as string; + return ( + + ); +}; + +export const ResetButton: React.FC = (props) => { + const title = '重置' as string; + return ( + + ); +}; + +export const MoreSearchButton: React.FC = (props) => { + const title = (props.show ? '收起' : '展开') as string; + return ( + + ); +}; + +interface ISearchInput { + placeholder?: string; + value?: string; + onEnd?: (newValue?: string, oldValue?: string) => void; + onPressEnter?: (newValue?: string, oldValue?: string) => void; +} + +export const SearchInputPlugin: React.FC = (props) => { + const { placeholder, value, onPressEnter, onEnd } = props; + const [val, setVal] = useState(); + + useEffect(() => { + setVal(value); + }, [value]); + + return ( + { + setVal(v.target.value); + }} + // onClear={() => { + // onEnd?.('', value); + // }} + onBlur={() => { + onEnd?.(val, value); + }} + onPressEnter={() => { + onEnd?.(val, value); + onPressEnter?.(val, value); + }} + /> + ); +}; diff --git a/src/components/SiderMenu/NavMenu.tsx b/src/components/SiderMenu/NavMenu.tsx new file mode 100644 index 0000000..5c66a74 --- /dev/null +++ b/src/components/SiderMenu/NavMenu.tsx @@ -0,0 +1,113 @@ +import { Menu, type MenuProps } from 'antd'; +import type React from 'react'; +import { useEffect, useState } from 'react'; +import { asideMenuConfig } from '@/configs/menuConfig'; +import { getHash, navigate } from '@/router/routerUtils'; +import { useAuthStore } from '@/store/AuthStore'; +import { useCompanyStore } from '@/store/CompanyStore'; +import { isArray, toArray } from '@/utils/common'; + +interface IProps { + onCallback?: () => void; +} + +const NavMenu: React.FC = (props) => { + const [openKeys, setOpenKeys] = useState([]); + const auth = useAuthStore().auth; + const company = useCompanyStore().company; + // 新窗口打开的链接 + const [newWindowUrl] = useState<{ [key: string]: { target: string } }>({}); + const [hash, setHash] = useState(getHash()); + type MenuItem = Required['items'][number]; + /** 导航菜单 数据处理 */ + const [menuOptions, setMenuOptions] = useState([]); + + useEffect(() => { + const arr: MenuItem[] = []; + asideMenuConfig.forEach((item) => { + const itemName = item.name; + const obj: any = { + key: item.path || itemName, + icon: item.icon, + label: itemName, + title: itemName, + }; + if (isArray(item.children)) { + obj.children = []; + item.children?.forEach((el: any) => { + // ! 添加权限判断 + if (!el.hideInMenu && (!el.auth || (auth && el.auth.split(',').some((key: string) => auth?.[key.trim()])))) { + if (!el.auth && (company.staff_type == '3' || company.staff_type == '4')) { + // + } else { + const elName = el.name; + if (el.target && el.path) { + newWindowUrl[el.path] = { target: el.target }; + } + obj.children.push({ key: el.path, icon: el.icon, label: elName, title: elName }); + } + } + }); + } + if (obj.children.length) { + arr.push(obj); + } + }); + // console.log(arr); + setMenuOptions(arr); + + const hashChange = () => { + const hashStr = getHash() || '/home/index'; + let name = ''; + let title = ''; + asideMenuConfig.forEach((item) => { + const itemName = item.name as string; + if (item.path == hashStr) { + name = itemName; + title = itemName; + } else { + toArray(item.children).forEach((el) => { + if (el.path == hashStr) { + name = itemName; + title = el.name as string; + } + }); + } + }); + setOpenKeys([name]); + document.title = title; + setHash(hashStr); + }; + + hashChange(); + + window.addEventListener('hashchange', hashChange); + + return () => { + window.removeEventListener('hashchange', hashChange); + }; + }, []); + + return ( + { + if (newWindowUrl[info.key]) { + window.open(`#${info.key}`, newWindowUrl[info.key].target); + } else { + navigate(info.key); + } + props.onCallback?.(); + }} + onOpenChange={(openKeys) => { + setOpenKeys(openKeys); + }} + // style={{ width: '100%' }} + selectedKeys={[hash]} + openKeys={openKeys} + // key={`${openKeys[0]}_${lo.pathname}`} + mode='inline' + items={menuOptions} + /> + ); +}; +export default NavMenu; diff --git a/src/components/SiderMenu/NavigateMenuDrawer.tsx b/src/components/SiderMenu/NavigateMenuDrawer.tsx new file mode 100644 index 0000000..7d6c8cf --- /dev/null +++ b/src/components/SiderMenu/NavigateMenuDrawer.tsx @@ -0,0 +1,29 @@ +import { MenuUnfoldOutlined } from '@ant-design/icons'; +import { Button, Drawer } from 'antd'; +import type React from 'react'; +import { useState } from 'react'; +import { getDevice } from '@/utils/common'; +import NavMenu from './NavMenu'; + +export const NavigateMenuDrawer: React.FC = () => { + const [open, setOpen] = useState(false); + + if (getDevice() === 'phone') { + return ( + <> + +// +//
表格列配置
+// +// +// +// } +// onClose={() => { +// setOpen(false); +// }}> +// +// el.columnName)} strategy={verticalListSortingStrategy}> +// +// +// +//
+// +// ); +// }; diff --git a/src/components/TablePlugin.tsx b/src/components/TablePlugin.tsx new file mode 100644 index 0000000..97bf2c9 --- /dev/null +++ b/src/components/TablePlugin.tsx @@ -0,0 +1,53 @@ +import { ConfigProvider, Table } from 'antd'; +import type { TableProps } from 'antd/lib/table'; +import type React from 'react'; +import { isObject } from '@/utils/common'; + +export const formatTableSort = (sort: any) => { + if (isObject(sort) && sort.order) { + return `${sort.field} ${sort.order.replace('end', '')}`; + } + return undefined; +}; + +/** + * 排序顺序格式化 + * @param dataIndex 列数据索引 + * @param order 排序顺序 + * @returns 'ascend' | 'descend' | undefined + */ +export const formatTableSortOrder = (dataIndex: string, order?: string): 'ascend' | 'descend' | undefined => { + if (order) { + const [name, sort] = order.split(' '); + if (name == dataIndex) { + return sort == 'asc' ? 'ascend' : 'descend'; + } + } + return undefined; +}; + +type IProps = TableProps; +/** Table简单封装 + * + * @props size="small" + * @props tableLayout="fixed" + * @props showSorterTooltip={false} + * @props pagination={false} + * @props bordered={true} + * + */ +export const TablePlugin: React.FC = (props) => { + return ( + '暂无数据'}> + + + ); +}; diff --git a/src/configs/config.ts b/src/configs/config.ts new file mode 100644 index 0000000..a26b2f5 --- /dev/null +++ b/src/configs/config.ts @@ -0,0 +1,128 @@ +import type React from 'react'; + +/** 默认错误图片 */ +// export const BreakImgUrl = pathAddApiString('/static/images/break_img.svg'); +// export const DefaultAvatar = pathAddApiString('/static/img/avatar.svg'); + +/** 头部高度 */ +export const headerHeight = 40; + +export const OSSBaseUrl = 'https://cdn.fzcfkj.com/'; + +/** 头部导航高度 */ +export const headerTabNavHeight = 32; + +export const DefaultERPName = (() => { + return '易宝赞普惠版后台系统'; +})(); + +export const Colors = { + primary: 'rgb(22, 93, 255)', + success: 'rgb(0, 180, 42)', + successBg: 'rgb(175, 240, 181)', + black: 'rgb(0, 0, 0)', + warning: 'rgb(255, 125, 0)', + error: 'rgb(245, 63, 63)', + danger: 'rgb(245, 63, 63)', +} as const; + +export const styleConfig = { + borderRadius: 2, +} as const; + +export const FlexCenter: React.CSSProperties = { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', +}; + +export const FlexCenterInLine: React.CSSProperties = { + display: 'inline-flex', + justifyContent: 'center', + alignItems: 'center', +}; + +/** 撤回码标记 */ +export const RecallCodeMarking = '-'; + +/** 菜单房间 */ +export const RoomName = [ + { label: '主卧', value: '主卧' }, + { label: '次卧', value: '次卧' }, + { label: '老人房', value: '老人房' }, + { label: '儿童房', value: '儿童房' }, + { label: '阳台', value: '阳台' }, + { label: '客厅', value: '客厅' }, + { label: '厨房', value: '厨房' }, + { label: '卫生间', value: '卫生间' }, +]; + +export const OrderState = { + /** 未审核 */ + ERP_ORDER_STATE_NOT: 110, + /** 部分审核 */ + ERP_ORDER_STATE_PART: 120, + /** 全部审核 */ + ERP_ORDER_STATE_ALL: 130, + /** 已关闭 */ + ERP_ORDER_STATE_CLOSE: 140, + /** 部分销售 */ + ERP_ORDER_STATE_PART_SALE: 150, + /** 完成销售 */ + ERP_ORDER_STATE_ALL_SALE: 160, +} as const; + +export const OrderStateColors = { + /** 未审核 */ + 110: 'red', + /** 部分审核 */ + 120: 'orange', + /** 全部审核 */ + 130: 'green', + /** 已关闭 */ + 140: 'gray', + /** 部分销售 */ + 150: 'blue', + /** 完成销售 */ + 160: 'arcoblue', +} as const; + +export const BonusType = { 1: '计件', 2: '计时' }; + +/** 生产订单状态 */ +export const ProduceState = { + /** 未审核 */ + PRODUCE_ORDER_STATE_NOT: 110, + /** 部分审核 */ + PRODUCE_ORDER_STATE_PART: 120, + /** 全部审核 */ + PRODUCE_ORDER_STATE_ALL: 130, + /** 已关闭 */ + PRODUCE_ORDER_STATE_CLOSE: 140, + /** 加工中 */ + PRODUCE_ORDER_STATE_WORKING: 150, + /** 已完工 */ + PRODUCE_ORDER_STATE_WORKED: 160, +} as const; + +export const ProduceStateObj: Record = { + /** 未审核 */ + 110: { label: '未审核', color: 'red' }, + /** 部分审核 */ + 120: { label: '部分审核', color: 'orange' }, + /** 全部审核 */ + 130: { label: '全部审核', color: 'green' }, + /** 已关闭 */ + 140: { label: '已关闭', color: 'gray' }, + /** 加工中 */ + 150: { label: '加工中', color: 'blue' }, + /** 已完工 */ + 160: { label: '已完工', color: 'arcoblue' }, +} as const; + +/** 工序状态 */ +export const ProcessState: Record = { + 0: { label: '未开始', color: '#0fc6c2' }, + 1: { label: '进行中', color: '#7bc616' }, + 2: { label: '已完成', color: '#86909c' }, +}; diff --git a/src/configs/menuConfig.tsx b/src/configs/menuConfig.tsx new file mode 100644 index 0000000..1a66861 --- /dev/null +++ b/src/configs/menuConfig.tsx @@ -0,0 +1,50 @@ +import { DashboardOutlined, UserOutlined } from '@ant-design/icons'; +import type React from 'react'; + +/* cspell:disable */ +const iconStyle: React.CSSProperties = { fontSize: 18 }; + +interface MenuDataItem { + name: string; + icon?: React.ReactNode; + path?: string; + hideInMenu?: boolean; + auth?: string; + children?: MenuDataItem[]; + docUrl?: string; +} + +//const docUrl = 'https://docs.qq.com/aio/DS2NCRFFseG9Ma3Ja?p='; + +const userMenu: MenuDataItem = { + name: '用户管理', + icon: , + children: [ + { name: '用户管理', path: '/user/list', auth: '' }, + // { name: '岗位角色', path: '/user/group', auth: 'SF_ERP_GROUP_VIEW' }, + ], +}; +const staffMenu: MenuDataItem = { + name: '后台用户', + icon: , + children: [ + { name: '组织架构', path: '/staff/dep', auth: 'SF_ERP_DEPART_VIEW' }, + { name: '岗位角色', path: '/staff/group', auth: 'SF_ERP_GROUP_VIEW' }, + { name: '员工管理', path: '/staff/list', auth: 'SF_ERP_STAFF_VIEW' }, + { name: '我的权限', path: '/staff/my', auth: 'SF_MY_RIGHT_VIEW' }, + // { name: '日志管理', path: '/system/record', auth: 'SF_ERP_LOG_VIEW' }, + ], +}; + +const asideMenuConfig: MenuDataItem[] = [ + { + name: '系统看板', + // path: '/', + icon: , + children: [{ name: '系统主页', path: '/home/index', auth: '' }], + }, + userMenu, + staffMenu, +]; + +export { asideMenuConfig }; diff --git a/src/configs/routes.ts b/src/configs/routes.ts new file mode 100644 index 0000000..39f0d89 --- /dev/null +++ b/src/configs/routes.ts @@ -0,0 +1,46 @@ +import { lazy } from 'react'; +import AppLayout from '@/layouts/AppLayout'; +import EmptyLayout from '@/layouts/EmptyLayout'; +import ErrorPage from '@/pages/Error'; +import Index from '@/pages/Index'; +import Dep from '@/pages/Staff/dep'; +import Group from '@/pages/Staff/group'; +import UserList from '@/pages/User/List'; +import type { IRouteItem } from '@/router/types'; + +export const routes: IRouteItem[] = [ + { + path: '/home', + Layout: AppLayout, + children: [ + { path: '/index', Component: Index }, + // { path: '/index', Component: lazy(() => import('@/pages/Index')) }, + // { path: '/home', Component: Index, }, + ], + }, + { + path: '/user', + Layout: AppLayout, + children: [ + { path: '/list', Component: UserList }, + // { path: '/group', Component: lazy(() => import('@/pages/Staff/group')) }, + ], + }, + { + path: '/staff', + Layout: AppLayout, + children: [ + { path: '/dep', Component: Dep }, + { path: '/group', Component: Group }, + ], + }, + { + path: '/login', + Layout: EmptyLayout, + children: [ + { path: '/index', Component: lazy(() => import('@/pages/Login')) }, + // + ], + }, + { path: '*', Layout: ErrorPage, children: [] }, +]; diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..758ed6f --- /dev/null +++ b/src/index.css @@ -0,0 +1,10 @@ +body { + margin: 0; + min-height: 100vh; +} + +@supports (height: 100dvh) { + body { + min-height: 100vh; + } +} diff --git a/src/interfaces/common.ts b/src/interfaces/common.ts new file mode 100644 index 0000000..7587377 --- /dev/null +++ b/src/interfaces/common.ts @@ -0,0 +1,25 @@ +import type { ForwardedRef } from 'react'; + +/** params的基础类型 */ +export type IParamsBase = { + curr_page: number; + page_count: number; + order?: string; +}; + +/** ajax返回的基础类型 */ +export type IAjaxDataBase = { + count: number | undefined; + err_msg?: string; + err_code?: number; + [key: string]: any; +}; + +export type IRef = { + ref: ForwardedRef; +}; + +export type IOption = { + value: string | number; + label: string; +}; diff --git a/src/interfaces/finance.ts b/src/interfaces/finance.ts new file mode 100644 index 0000000..03f3654 --- /dev/null +++ b/src/interfaces/finance.ts @@ -0,0 +1,56 @@ +/** 供应商 type */ +export type ISupplierType = { + supplier_id?: number; + name?: string; + phone?: string; + tax_no?: string; + invoice_title?: string; + bank_name?: string; + bank_no?: string; + finance_phone?: string; + address?: string; + tax_rate?: number | string; + create_date?: string; + comments?: string; +}; + +/** 经销商 type */ +export type ISaleInfo = { + sms_code?: string; + sale_id?: number; + name?: string; + phone?: string; + level?: number | string; + level_name?: string; + default_discount?: number | string; + last_discount?: number; + tax_no?: string; + invoice_title?: string; + bank_name?: string; + bank_no?: string; + finance_phone?: string; + address?: string; + tax_rate?: number; + pay_ratio?: number; + signed_date?: string; + create_date?: string; + comments?: string; + is_staff?: any; + login_name?: string; // 业务员信息 + bind_login_name?: string; // 绑定账号信息 + bind_user_phone?: string; // 绑定账号信息 + bind_user_id?: any; // 绑定账号信息 + staff_user_id?: any; + staff_name?: string; + nick_name?: string; +}; + +/** 折扣 type */ +export type IDiscountInfo = { + discount_id?: number; + name?: string; + code?: string; + discount_value?: number; + comments?: string; + create_date?: string; +}; diff --git a/src/interfaces/staff.ts b/src/interfaces/staff.ts new file mode 100644 index 0000000..a399bd8 --- /dev/null +++ b/src/interfaces/staff.ts @@ -0,0 +1,86 @@ +/** 部门 */ +export type IDepartment = { + comments?: string; + company_id?: number; + create_date?: string; + department_id: number; + name: string; + state?: number; + dept_type?: number; +}; + +export type IGroup = { + comments?: string; + company_id?: number; + create_date?: string; + group_id: number; + name: string; + state?: number; + rights?: any; +}; + +export type IRightsItem3 = { + comments: null | string; + function_ch_name: string; + function_en_name: string; + function_id: number; + function_sort: number; + function_type: number; + menu_id: number; + checked?: boolean; +}; + +export type IRightsItem2 = { + comments: null | string; + menu_ch_name: string; + menu_en_name: string; + menu_icon: null | string; + menu_id: number; + menu_sort: number; + menu_type: number; + menu_url: string; + parent_menu_id: number; + state: number; + children: IRightsItem3[]; +}; + +export type IRightsItem1 = { + comments: null | string; + menu_ch_name: string; + menu_en_name: string; + menu_icon: null | string; + menu_id: number; + menu_sort: number; + menu_type: number; + menu_url: string; + parent_menu_id: number; + state: number; + children: IRightsItem2[]; +}; + +export type IRights = IRightsItem1[]; + +export type IStaff = { + group_type: any; + comments: null | string; + create_date: string; + dep_name: string; + department_id: number; + group_id: string; + staff_id: number; + group_name: string; + last_login_time: null | string; + login_name: string; + logo: null | string; + nick_name: null | string; + staff_type: number; + state: number; + user_id: number; + user_phone: string; + user_sex: number; + visit_times: number; + staff_state: number; + wx_open_id?: string; + wx_nick_name?: string; + wx_head_img?: string; +}; diff --git a/src/interfaces/user.ts b/src/interfaces/user.ts new file mode 100644 index 0000000..87a1e6e --- /dev/null +++ b/src/interfaces/user.ts @@ -0,0 +1,40 @@ +export interface UserInfo { + login_name?: string; + logo?: string; + last_login_time?: string; + nick_name?: string; + state?: number; + user_id?: number; + user_sex?: number; + user_phone?: string; + visit_times?: string; + create_date?: string; + logoKey?: number; + wx_head_img?: string; + wx_nick_name?: string; + wx_open_id?: string; + self_account?: number; +} + +export interface CompanyInfo { + company_address?: string; + company_desc?: string; + company_domain?: string; + company_id?: number; + company_logo?: string; + company_name?: string; + company_state?: string; + create_date?: string; + eff_date?: string; + exp_date?: string; + erp_name?: string; + last_login_name?: string; + user_id?: string; + domain?: string; + desc?: string; + company_list?: any[]; + sale_id?: any; + staff_type?: any; + erp_version?: number; + session_id?: string; +} diff --git a/src/layouts/AppLayout.tsx b/src/layouts/AppLayout.tsx new file mode 100644 index 0000000..ff96c70 --- /dev/null +++ b/src/layouts/AppLayout.tsx @@ -0,0 +1,129 @@ +import { Layout, Spin } from 'antd'; +import { Content, Header } from 'antd/es/layout/layout'; +import Sider from 'antd/es/layout/Sider'; +import { useEffect, useState } from 'react'; +import Footer from '@/components/Footer'; +import { GapBox } from '@/components/GapBox'; +import { HeaderUserInfo } from '@/components/Header/HeaderUserInfo'; +import { NavigateMenuDrawer } from '@/components/SiderMenu/NavigateMenuDrawer'; +import NavMenu from '@/components/SiderMenu/NavMenu'; +import TabNavPlugin from '@/components/TabNavPlugin'; +import { DefaultERPName, headerHeight } from '@/configs/config'; +import Outlet from '@/router/Outlet'; +import { useAuthStore } from '@/store/AuthStore'; +import { useUserStore } from '@/store/UserStore'; +import { getDevice, toObject } from '@/utils/common'; +import { loginState } from './base'; +import ErrorBoundary from './ErrorBoundary'; + +const AppLayout = () => { + const [loading, setLoading] = useState(true); + const isPhone = getDevice() == 'phone'; + const userStore = useUserStore(); + const authStore = useAuthStore(); + + useEffect(() => { + setLoading(true); + loginState().then((res) => { + // console.log(res); + userStore.updateUser(toObject(res.user_info)); + authStore.updateAuth(toObject(res.auth_info) as any); + localStorage.setItem('admin_user_id', res?.user_info?.user_id); + setLoading(false); + }); + }, []); + + return ( + <> + {!loading ? ( + +
+ + {isPhone ? ( + + ) : ( +
{DefaultERPName}
+ )} + {Date.now()} +
+
+ +
+
+ + {isPhone ? null : ( + { + // setCollapsed(collapsed); + // }} + style={{ + background: '#fff', + overflow: 'auto', + // height: `calc(100vh - ${headerHeight}px)`, + position: 'sticky', + // zIndex: 1000, + left: 0, + // top: headerHeight, + }} + width={200} + // width={window?.dfConfig?.language == 'zh-cn' ? 100 : 240} + // collapsed={collapsed} + // collapsedWidth={60} + > + + {/* */} + + )} + + + {/* */} +
+ + + +
+
+ + + + ) : ( + 加载数据...} + > +   + + )} + + ); +}; +export default AppLayout; diff --git a/src/layouts/EmptyLayout.tsx b/src/layouts/EmptyLayout.tsx new file mode 100644 index 0000000..6983f33 --- /dev/null +++ b/src/layouts/EmptyLayout.tsx @@ -0,0 +1,11 @@ +import Outlet from '@/router/Outlet'; +import ErrorBoundary from './ErrorBoundary'; + +const EmptyLayout = () => { + return ( + + + + ); +}; +export default EmptyLayout; diff --git a/src/layouts/ErrorBoundary.tsx b/src/layouts/ErrorBoundary.tsx new file mode 100644 index 0000000..d2daf65 --- /dev/null +++ b/src/layouts/ErrorBoundary.tsx @@ -0,0 +1,81 @@ +import { Button, Result } from 'antd'; +import React from 'react'; + +type IProps = { + fallback?: (error: any, info?: any) => React.ReactNode; + children?: React.ReactNode; +}; + +/** 错误边界组件 */ +class ErrorBoundary extends React.Component { + state: { hasError: boolean; errorMsg?: React.ReactNode }; + + constructor(props: IProps) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError(_error: any) { + // console.log("error", error); + // 更新状态,以便下一次渲染将显示后备 UI。 + return { hasError: true }; + } + + componentDidCatch(error: any, info: any) { + // console.log(error, info); + this.setState({ errorMsg: `${error}${info.componentStack}` }); + } + + render() { + if (this.state.hasError) { + // 你可以渲染任何自定义后备 UI + return ( + this.props?.fallback?.(this.state.errorMsg) || ( + +
页面发生异常,错误内容已上报!
+
开发人员紧急修复中,敬请耐心等待!
+ + } + subTitle={ +
+                {this.state.errorMsg}
+              
+ } + style={{ + display: 'flex', + alignItems: 'center', + flexDirection: 'column', + minHeight: 'calc(100vh - 180px)', + }} + extra={[ + , + ]} + /> + ) + ); + } + + return this.props?.children; + } +} + +export default ErrorBoundary; diff --git a/src/layouts/base.ts b/src/layouts/base.ts new file mode 100644 index 0000000..6638eed --- /dev/null +++ b/src/layouts/base.ts @@ -0,0 +1,13 @@ +import { requestLite } from '@/utils/useRequest'; + +export const loginState = async () => { + return new Promise((resolve, reject) => { + requestLite('/Users/loginState').then((res) => { + if (res?.err_code == 0) { + resolve(res); + } else { + reject(null); + } + }); + }); +}; diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..f329669 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,9 @@ +import { createRoot } from 'react-dom/client'; +import './index.css'; +import App from './App.tsx'; + +createRoot(document.getElementById('root')!).render( + // + , + // , +); diff --git a/src/pages/Error/index.tsx b/src/pages/Error/index.tsx new file mode 100644 index 0000000..fd3ea8d --- /dev/null +++ b/src/pages/Error/index.tsx @@ -0,0 +1,26 @@ +import { Button, Result } from 'antd'; + +const ErrorPage = () => { + document.title = '404 - 抱歉,您访问的页面不存在'; + return ( +
+ { + location.href = '/'; + }} + > + {'返回首页'} + + } + /> +
+ ); +}; + +export default ErrorPage; diff --git a/src/pages/Index/index.tsx b/src/pages/Index/index.tsx new file mode 100644 index 0000000..e0d2c80 --- /dev/null +++ b/src/pages/Index/index.tsx @@ -0,0 +1,34 @@ +const Index = () => { + return ( +
+ 开发中... + {/* Index */} + {/* + + + login */} + {/* {undefined.map()} */} +
+ ); +}; + +export default Index; diff --git a/src/pages/Login/index.tsx b/src/pages/Login/index.tsx new file mode 100644 index 0000000..24a1a07 --- /dev/null +++ b/src/pages/Login/index.tsx @@ -0,0 +1,86 @@ +import { Button, Input } from 'antd'; +import { stringify } from 'qs'; +import { useState } from 'react'; +import loginBg from '@/assets/loginBg.jpg'; +import { FormItemPlugin, FormPlugin } from '@/components/FormPlugin'; +import { DefaultERPName } from '@/configs/config'; +import { notificationEventBus } from '@/utils/EventBus'; +import { useRequest } from '@/utils/useRequest'; + +const Login = () => { + const [userInfo, setUserInfo] = useState({ login_name: 'zhengw', password: '123456', login_type: 1 }); + const { request, loading } = useRequest('Users/login', { + onSuccessCodeZero() { + notificationEventBus.emit({ description: '登录成功' }); + location.href = '#/'; + }, + }); + + document.title = '登录'; + + const login = () => { + request(stringify(userInfo)); + }; + + return ( +
+
+
{DefaultERPName}后台登录
+ + + { + userInfo.login_name = e.target.value; + setUserInfo({ ...userInfo }); + }} + onPressEnter={login} + /> + + + { + userInfo.password = e.target.value; + setUserInfo({ ...userInfo }); + }} + onPressEnter={login} + /> + + + + + +
+
+ ); +}; + +export default Login; diff --git a/src/pages/Staff/dep/index.tsx b/src/pages/Staff/dep/index.tsx new file mode 100644 index 0000000..20418c2 --- /dev/null +++ b/src/pages/Staff/dep/index.tsx @@ -0,0 +1,17 @@ +import PageContainerPlugin from '@/components/PageContainer/PageContainerPlugin'; + +const DepContent = () => { + return ( +
+
Dep
+
+ ); +}; + +const Dep = () => ( + + + +); + +export default Dep; diff --git a/src/pages/Staff/group/index.tsx b/src/pages/Staff/group/index.tsx new file mode 100644 index 0000000..bc86d32 --- /dev/null +++ b/src/pages/Staff/group/index.tsx @@ -0,0 +1,17 @@ +import PageContainerPlugin from '@/components/PageContainer/PageContainerPlugin'; + +const GroupContent = () => { + return ( +
+
Group
+
+ ); +}; + +const Group = () => ( + + + +); + +export default Group; diff --git a/src/pages/User/List/components/UserEditModal.tsx b/src/pages/User/List/components/UserEditModal.tsx new file mode 100644 index 0000000..1069174 --- /dev/null +++ b/src/pages/User/List/components/UserEditModal.tsx @@ -0,0 +1,26 @@ +import type React from 'react'; +import { useImperativeHandle, useState } from 'react'; +import ModalPlugin from '@/components/ModalPlugin'; +import type { IRef } from '@/utils/type'; + +interface IProps extends IRef {} + +export type IUserEditModalType = { + show: () => void; +}; + +export const UserEditModal: React.FC = (props) => { + console.log(props.ref); + const [open, setOpen] = useState(false); + + useImperativeHandle(props.ref, () => ({ + show: () => { + setOpen(true); + }, + })); + return ( + setOpen(false)}> + 11111 + + ); +}; diff --git a/src/pages/User/List/components/state.ts b/src/pages/User/List/components/state.ts new file mode 100644 index 0000000..413fd64 --- /dev/null +++ b/src/pages/User/List/components/state.ts @@ -0,0 +1,89 @@ +import { proxy } from 'valtio'; +import { deepClone } from 'valtio/utils'; +import type { IOption, IParamsBase } from '@/interfaces/common'; +import { UserServices } from '@/services/UserServices'; +import { toArray, toNumber, toObject } from '@/utils/common'; +import { requestLite } from '@/utils/useRequest2'; + +const defaultParams = { curr_page: 1, page_count: 20 }; + +type IParams = IParamsBase & { + order_no?: string; + custom_order_no?: string; + custom_name?: string; + custom_phone?: string; + end_user_address?: string; + end_user_phone?: string; + end_user_name?: string; + payed_state?: string; + document_dateL?: string; + document_dateU?: string; + create_dateL?: string; + create_dateU?: string; + process_state?: any[]; + order_step?: any[]; + category_id?: any; +}; + +export const userListStateProxy = proxy<{ + params: IParams; + loading: boolean; + ajaxData: any[]; + orderStepsOptions: IOption[]; + count: number; + amount: { + tot_discount_money?: string; + tot_payed_amount?: string; + tot_tax_last_money?: string; + tot_un_payed_amount?: string; + }; + showMoreSearch: boolean; + reset: () => void; + getData: () => void; + clear: () => void; + page: (current?: number, pageSize?: number) => void; + // !分页回调函数 + pageCallback?: () => void; +}>({ + params: deepClone(defaultParams), + loading: false, + showMoreSearch: false, + ajaxData: [], + orderStepsOptions: [], + amount: {}, + count: 0, + reset() { + this.params = deepClone(defaultParams); + this.getData(); + }, + page(current, pageSize) { + if (!this.loading) { + this.params.curr_page = current || 1; + this.params.page_count = pageSize || this.params.page_count; + this.getData(); + this.pageCallback?.(); + } + }, + async getData() { + this.loading = true; + const temp: any = deepClone(this.params); + if (temp.process_state?.length) { + temp.process_state = temp.process_state.join(','); + } + if (temp.order_step?.length) { + temp.order_step = temp.order_step.join(','); + } + const res: any = await requestLite(UserServices.list, temp); + this.loading = false; + this.ajaxData = toArray(res?.data); + if (this.ajaxData.length == 0 && this.params.curr_page > 1) { + this.page(this.params.curr_page - 1); + } + this.count = toNumber(res?.count); + this.amount = toObject(res?.amount); + }, + clear() { + this.ajaxData = []; + this.pageCallback = undefined; + }, +}); diff --git a/src/pages/User/List/index.tsx b/src/pages/User/List/index.tsx new file mode 100644 index 0000000..f00ab1c --- /dev/null +++ b/src/pages/User/List/index.tsx @@ -0,0 +1,167 @@ +import { Button } from 'antd'; +import { useRef } from 'react'; +import { useSnapshot } from 'valtio'; +import { FormItemPlugin, FormPlugin } from '@/components/FormPlugin'; +import { GapBox } from '@/components/GapBox'; +import PageContainerPlugin from '@/components/PageContainer/PageContainerPlugin'; +import { FooterPagination, HeaderPagination } from '@/components/PaginationPlugin'; +import { MoreSearchButton, ResetButton, SearchButton, SearchInputPlugin } from '@/components/SearchButton'; +import type { ColumnsTypeUltra2 } from '@/components/TableColumnsFilterPlugin2'; +import { formatTableSort, TablePlugin } from '@/components/TablePlugin'; +import { tableFixedByPhone } from '@/utils/common'; +import { userListStateProxy } from './components/state'; +import { type IUserEditModalType, UserEditModal } from './components/UserEditModal'; + +const SearchBox = () => { + const snap = useSnapshot(userListStateProxy); + + return ( + <> + + + { + userListStateProxy.page(1); + }} + onEnd={(v) => { + userListStateProxy.params.order_no = v; + }} + /> + + + { + userListStateProxy.page(1); + }} + onEnd={(v) => { + userListStateProxy.params.custom_order_no = v; + }} + /> + + + { + userListStateProxy.page(1); + }} + onEnd={(v) => { + userListStateProxy.params.custom_name = v; + }} + /> + + + + + { + userListStateProxy.page(1); + }} + /> + { + userListStateProxy.reset(); + }} + /> + { + userListStateProxy.showMoreSearch = !userListStateProxy.showMoreSearch; + }} + /> + + + + + + { + userListStateProxy.page(1); + }} + onEnd={(v) => { + userListStateProxy.params.custom_phone = v; + }} + /> + + + + ); +}; + +const Content = () => { + const snap = useSnapshot(userListStateProxy); + const tableColumns: ColumnsTypeUltra2 = [ + { + columnName: '操作', + title: '操作', + fixed: tableFixedByPhone('left'), + width: 52, + render: () => { + return {/* */}; + }, + }, + { columnName: '创建时间', width: 150, title: '创建时间', dataIndex: 'create_date', sorter: true }, + { columnName: '备注', title: '备注', dataIndex: 'comments', width: 180 }, + { columnName: '' }, + ]; + + const UserEditModalRef = useRef(null); + + return ( + <> + + + + + { + userListStateProxy.page(page); + }} + /> + + { + userListStateProxy.params.order = formatTableSort(sort); + userListStateProxy.page(1); + }} + dataSource={snap.ajaxData} + rowKey={'user_id'} + columns={tableColumns} + /> + { + userListStateProxy.page(page, pageSize); + }} + /> + + + + ); +}; + +const UserList = () => ( + + + + +); + +export default UserList; diff --git a/src/router/Link.tsx b/src/router/Link.tsx new file mode 100644 index 0000000..59865c4 --- /dev/null +++ b/src/router/Link.tsx @@ -0,0 +1,8 @@ +import type React from "react"; +import { urlFormat } from "./routerUtils"; + +const Link: React.FC<{ href: string; children: React.ReactNode }> = (props) => { + return {props.children}; +}; + +export default Link; diff --git a/src/router/Outlet.tsx b/src/router/Outlet.tsx new file mode 100644 index 0000000..000a089 --- /dev/null +++ b/src/router/Outlet.tsx @@ -0,0 +1,48 @@ +import { Spin } from 'antd'; +import React, { Suspense, useEffect, useRef, useState } from 'react'; +import { routeMatchingHash } from './routerUtils'; + +const Outlet = () => { + const [key, setKey] = useState(1); + const OutLetRef = useRef(null); + + useEffect(() => { + const handle = async () => { + const res: any = routeMatchingHash(); + if (OutLetRef.current != res?.Component) { + OutLetRef.current = res?.Component; + setKey(Date.now()); + } + }; + handle(); + + window.addEventListener('hashchange', handle); + + return () => { + window.removeEventListener('hashchange', handle); + }; + }, []); + + return ( + + {OutLetRef.current ? ( + OutLetRef.current?.$$typeof == Symbol.for('react.lazy') ? ( + + } + > + + + ) : ( + + ) + ) : null} + + ); +}; + +export default Outlet; diff --git a/src/router/Router.tsx b/src/router/Router.tsx new file mode 100644 index 0000000..a4f3ecc --- /dev/null +++ b/src/router/Router.tsx @@ -0,0 +1,32 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { routerData } from './router-data.js'; +import { routeMatchingHash, routerFormat } from './routerUtils.js'; +import type { IRouteItem } from './types.js'; + +const Router: React.FC<{ routes: IRouteItem[] }> = (props) => { + const { routes } = props; + const [key, setKey] = useState(1); + const LayoutRef = useRef(null); + routerData.routers = routerFormat(routes); + + useEffect(() => { + const handle = () => { + const res: any = routeMatchingHash(); + if (LayoutRef.current != res?.Layout) { + LayoutRef.current = res?.Layout; + setKey(Date.now()); + } + }; + handle(); + + window.addEventListener('hashchange', handle); + + return () => { + window.removeEventListener('hashchange', handle); + }; + }, []); + + return {LayoutRef.current ? : null}; +}; + +export default Router; diff --git a/src/router/router-data.ts b/src/router/router-data.ts new file mode 100644 index 0000000..4ff5e14 --- /dev/null +++ b/src/router/router-data.ts @@ -0,0 +1,9 @@ +interface IRouterData { + mode: 'hash' | 'history'; + routers: Map; +} + +export const routerData: IRouterData = { + mode: 'hash', + routers: new Map(), +}; diff --git a/src/router/routerUtils.ts b/src/router/routerUtils.ts new file mode 100644 index 0000000..f7055e0 --- /dev/null +++ b/src/router/routerUtils.ts @@ -0,0 +1,72 @@ +import { routerData } from './router-data'; + +/** 路由导航 */ +export const navigate = (path: string) => { + window.location.hash = urlFormat(path); +}; + +export const urlFormat = (path: string) => { + const url = `${path || ''}`.trim(); + if (url.startsWith('http') || url.startsWith('blob')) { + return url; + } + return `#${url}`; +}; + +/** 获取查询参数 */ +export const getURLSearchParams = () => { + const { hash } = location; + const params = hash.includes('?') ? hash.slice(hash.indexOf('?')) : ''; + return new URLSearchParams(params); +}; + +export const getHash = () => { + let hash = location.hash; + if (hash.startsWith('#')) { + hash = hash.slice(1); + } + if (hash.includes('?')) { + hash = hash.slice(0, hash.indexOf('?')); + } + hash = hash.endsWith('/') ? hash.slice(0, hash.length - 1) : hash; + return hash; +}; + +export const routerFormat = (routes: any[]) => { + const router = new Map(); + routes.forEach((el) => { + router.set(`${el.path}`, { ...el }); + if (Array.isArray(el.children)) { + el.children.forEach((ell: any, i: number) => { + if (i == 0) { + router.set(`${el.path}`, { ...el, ...ell }); + } + router.set(`${el.path}${ell.path}`, { ...el, ...ell }); + }); + } + }); + return router; +}; + +export const routeMatchingHash = () => { + const hash = getHash(); + let obj: any = {}; + // console.log(hash); + if (routerData.routers.has(hash)) { + obj = routerData.routers.get(hash); + } else { + if (hash) { + if (routerData.routers.has('*')) { + obj = routerData.routers.get('*'); + } + // + // console.warn('请配置 * 错误路由'); + } else { + // 返回第一个路由 + obj = routerData.routers.values().next().value; + } + } + + // document.title = obj.title || ''; + return obj; +}; diff --git a/src/router/types.ts b/src/router/types.ts new file mode 100644 index 0000000..15a3728 --- /dev/null +++ b/src/router/types.ts @@ -0,0 +1,12 @@ +export type IRouteItemChild = { + path: string; + Component: any; + title?: string; +}; + +export type IRouteItem = { + path: string; + Layout: any; + title?: string; + children: IRouteItemChild[]; +}; diff --git a/src/services/UserServices.ts b/src/services/UserServices.ts new file mode 100644 index 0000000..8e5c2dc --- /dev/null +++ b/src/services/UserServices.ts @@ -0,0 +1,3 @@ +export const UserServices = { + list: '/UserServices/list', +} as const; diff --git a/src/store/AuthStore.ts b/src/store/AuthStore.ts new file mode 100644 index 0000000..49d78f1 --- /dev/null +++ b/src/store/AuthStore.ts @@ -0,0 +1,18 @@ +import { create, type StoreApi, type UseBoundStore } from 'zustand'; + +export type IAuth = { [key: string]: boolean }; +export type IAuthByPathname = { [key: string]: string }; + +type IUser = { + auth: IAuth; + authByPathname: IAuthByPathname; + updateAuth: (data: IAuth) => void; + initAuthByPathname: (data: IAuthByPathname) => void; +}; + +export const useAuthStore: UseBoundStore> = create((set) => ({ + auth: {}, + updateAuth: (data) => set(() => ({ auth: data })), + authByPathname: {}, + initAuthByPathname: (data: IAuthByPathname) => set(() => ({ authByPathname: data })), +})); diff --git a/src/store/CompanyStore.ts b/src/store/CompanyStore.ts new file mode 100644 index 0000000..c2e0abb --- /dev/null +++ b/src/store/CompanyStore.ts @@ -0,0 +1,14 @@ +import { create, type StoreApi, type UseBoundStore } from "zustand"; +import type { CompanyInfo } from "./type"; + +type IBear = { + company: CompanyInfo; + updateCompany: (data: CompanyInfo) => void; +}; + +export const useCompanyStore: UseBoundStore> = create( + (set) => ({ + company: {}, + updateCompany: (data) => set(() => ({ company: data })), + }), +); diff --git a/src/store/ConfigStore.ts b/src/store/ConfigStore.ts new file mode 100644 index 0000000..4e8f546 --- /dev/null +++ b/src/store/ConfigStore.ts @@ -0,0 +1,25 @@ +import { create, type StoreApi, type UseBoundStore } from 'zustand'; + +interface IConfigType { + // config_id: number; + config_value: string; + // rel_config_id: number; + // create_date: string; + // config_type: string; + // config_name: string; + // config_desc: string; + // form_type: string; + // setting: string; + // config_state: number; +} + +type IConfig = { + config: { [key: string]: IConfigType }; + updateConfig: (data: { [key: string]: IConfigType }) => void; +}; + +/** 参数化配置store */ +export const useConfigStore: UseBoundStore> = create((set) => ({ + config: {}, + updateConfig: (data) => set(() => ({ config: data })), +})); diff --git a/src/store/RefreshStore.ts b/src/store/RefreshStore.ts new file mode 100644 index 0000000..e5d1df5 --- /dev/null +++ b/src/store/RefreshStore.ts @@ -0,0 +1,11 @@ +import { create, type StoreApi, type UseBoundStore } from 'zustand'; + +type IUser = { + refresh: number; + updateRefresh: (data: number) => void; +}; + +export const useRefreshStore: UseBoundStore> = create((set) => ({ + refresh: 1, + updateRefresh: (data) => set(() => ({ refresh: data })), +})); diff --git a/src/store/UserConfigStore.ts b/src/store/UserConfigStore.ts new file mode 100644 index 0000000..7694ee5 --- /dev/null +++ b/src/store/UserConfigStore.ts @@ -0,0 +1,22 @@ +// import { create, type StoreApi, type UseBoundStore } from 'zustand'; +// import type { UsersConfig } from '@/services/UserServicesAndConfig'; + +// type IUsersConfig = { +// [key in keyof typeof UsersConfig]?: { +// config_id?: number; +// config_name: string; +// config_value: string; +// update_date: string | null; +// }; +// }; + +// type IUser = { +// config: IUsersConfig; +// updateConfig: (data: IUsersConfig) => void; +// }; + +// /** 用户配置store */ +// export const useUsersConfigStore: UseBoundStore> = create((set) => ({ +// config: {}, +// updateConfig: (data) => set(() => ({ config: data })), +// })); diff --git a/src/store/UserStore.ts b/src/store/UserStore.ts new file mode 100644 index 0000000..90cc037 --- /dev/null +++ b/src/store/UserStore.ts @@ -0,0 +1,12 @@ +import { create, type StoreApi, type UseBoundStore } from "zustand"; +import type { UserInfo } from "./type"; + +type IUser = { + user: UserInfo; + updateUser: (data: UserInfo) => void; +}; + +export const useUserStore: UseBoundStore> = create((set) => ({ + user: {}, + updateUser: (data) => set(() => ({ user: data })), +})); diff --git a/src/store/indexDBStore.ts b/src/store/indexDBStore.ts new file mode 100644 index 0000000..71ef91f --- /dev/null +++ b/src/store/indexDBStore.ts @@ -0,0 +1,11 @@ +import { create, type StoreApi, type UseBoundStore } from 'zustand'; + +type IIndexDB = { + db: any; + init: (data: any) => void; +}; + +export const useIndexDBStore: UseBoundStore> = create((set) => ({ + db: {}, + init: (data) => set(() => ({ db: data })), +})); diff --git a/src/store/type.ts b/src/store/type.ts new file mode 100644 index 0000000..d52e02f --- /dev/null +++ b/src/store/type.ts @@ -0,0 +1,39 @@ +export type UserInfo = { + login_name?: string; + logo?: string; + last_login_time?: string; + nick_name?: string; + state?: number; + user_id?: number; + user_sex?: number; + user_phone?: string; + visit_times?: string; + create_date?: string; + logoKey?: number; + wx_head_img?: string; + wx_nick_name?: string; + wx_open_id?: string; + self_account?: number; +}; + +export type CompanyInfo = { + company_address?: string; + company_desc?: string; + company_domain?: string; + company_id?: number; + company_logo?: string; + company_name?: string; + company_state?: string; + create_date?: string; + eff_date?: string; + exp_date?: string; + erp_name?: string; + last_login_name?: string; + user_id?: string; + domain?: string; + desc?: string; + company_list?: any[]; + sale_id?: any; + staff_type?: any; + erp_version?: number; +}; diff --git a/src/utils/EventBus.ts b/src/utils/EventBus.ts new file mode 100644 index 0000000..bc58faf --- /dev/null +++ b/src/utils/EventBus.ts @@ -0,0 +1,150 @@ +import type { ReactNode } from 'react'; + +// class EventBus { +// eventMap: { [key: string]: (() => void)[] } = {}; +// constructor() { +// // 存储事件映射:key=事件名,value=回调函数数组 +// this.eventMap = Object.create(null); +// } + +// /** +// * 订阅事件 +// * @param {string} eventName - 事件名称 +// * @param {Function} callback - 事件触发时的回调函数 +// */ +// on(eventName: string, callback: () => void) { +// if (typeof callback !== 'function') { +// throw new Error('回调函数必须是函数类型'); +// } +// // 若事件不存在,初始化一个空数组 +// if (!this.eventMap[eventName]) { +// this.eventMap[eventName] = []; +// } +// // 将回调加入事件对应的数组(支持多个订阅者) +// this.eventMap[eventName].push(callback); +// } + +// /** +// * 发布事件(触发订阅者回调) +// * @param {string} eventName - 事件名称 +// * @param {...any} args - 传递给回调的参数(支持多个) +// */ +// emit(eventName: string, ...args: any) { +// // 若事件无订阅者,直接返回 +// if (!this.eventMap[eventName]) return; +// // 遍历所有订阅者,执行回调(传递参数) +// // 用slice()创建副本,避免执行回调时修改数组(如取消订阅)导致遍历异常 +// this.eventMap[eventName].slice().forEach((callback: () => void) => { +// callback.apply(this, args); +// }); +// } + +// /** +// * 取消订阅 +// * @param {string} eventName - 事件名称 +// * @param {Function} [callback] - 要取消的回调(不传则取消该事件所有订阅) +// */ +// off(eventName: string, callback: () => void) { +// const callbacks = this.eventMap[eventName]; +// if (!callbacks) return; + +// // 情况1:不传callback,取消该事件所有订阅 +// if (!callback) { +// delete this.eventMap[eventName]; +// return; +// } + +// // 情况2:传callback,只取消指定回调(避免影响其他订阅者) +// const index = callbacks.indexOf(callback); +// if (index !== -1) { +// callbacks.splice(index, 1); +// // 若事件无剩余订阅者,删除该事件(优化内存) +// if (callbacks.length === 0) { +// delete this.eventMap[eventName]; +// } +// } +// } + +// /** +// * 订阅一次事件(触发后自动取消订阅) +// * @param {string} eventName - 事件名称 +// * @param {Function} callback - 回调函数 +// */ +// once(eventName: string, callback: () => void) { +// // 包装回调:执行后自动取消订阅 +// const wrapCallback = (...args: any) => { +// callback.apply(this, args); +// this.off(eventName, wrapCallback); +// }; +// this.on(eventName, wrapCallback); +// } +// } + +/** 单例 */ +// function singleton(className: object) { +// let ins: any = null; +// const proxy = new Proxy(className, { +// construct(target, args) { +// if (!ins) { +// ins = Reflect.construct(target, args); +// } +// return ins; +// }, +// }); +// proxy.prototype.constructor = proxy; +// return proxy; +// } + +// const Bus = singleton(EventBus); + +// export const eventBus = new Bus(); + +type INotificationConfig = { + /** 标题 默认: 系统通知 */ + title?: ReactNode; + /** 内容 */ + description?: ReactNode; + /** 当前通知唯一标志 */ + key?: string; + /** 通知类型, 默认: success */ + type?: 'success' | 'info' | 'error' | 'warning'; + duration?: number; +}; + +const notificationEvent: any = {}; + +/** + * 通知事件 + */ +export const notificationEventBus = { + /** + * 监听打开通知事件 + * @param callback 回调函数 + */ + onOpen(callback: (data: INotificationConfig) => void) { + notificationEvent.notification = callback; + }, + /** + * 发送通知数据 + * @param {INotificationConfig} data INotificationConfig + */ + emit(data: INotificationConfig) { + data.type = data.type || 'success'; + data.title = data.title || '系统通知'; + notificationEvent.notification?.(data); + }, + /** + * 发送关闭通知 + * @param key 通知唯一标志 + */ + emitClose(key: string) { + notificationEvent.notificationClose?.(key); + }, + /** + * 监听关闭通知事件 + * @param callback 回调函数 + */ + onClose(callback: (key: string) => void) { + notificationEvent.notificationClose = callback; + }, +}; diff --git a/src/utils/UniqueKey.ts b/src/utils/UniqueKey.ts new file mode 100644 index 0000000..b881bb8 --- /dev/null +++ b/src/utils/UniqueKey.ts @@ -0,0 +1,4 @@ +let count = 1; +/** 生成唯一key */ +// export const uKey = () => `${Date.now().toString(36)}-${Math.random().toString(36).substring(2)}`; +export const uKey = () => count++; diff --git a/src/utils/common.ts b/src/utils/common.ts new file mode 100644 index 0000000..d6d5238 --- /dev/null +++ b/src/utils/common.ts @@ -0,0 +1,366 @@ +import Big from 'big.js'; +import dayjs from 'dayjs'; +import { DefaultERPName } from '@/configs/config'; + +/** 获取设备屏幕大小 */ +export const getDevice = (): 'phone' | 'tablet' | 'desktop' => { + const width = window.innerWidth; + const isPhone = typeof navigator !== 'undefined' && navigator && navigator.userAgent.match(/phone/gi); + + if (width < 768 || isPhone) { + return 'phone'; + } else if (width < 1280 && width > 768) { + return 'tablet'; + } else { + return 'desktop'; + } +}; + +/** + * 判断字符串包含字符串 + * @param str 待判断的字符串 + * @param search 查询字符串 + * @returns Boolean + */ +export const includesString = (str: string, search: string): boolean => { + return `${str}`.toLocaleUpperCase().includes(search.toLocaleUpperCase()); +}; + +/** 路径前面添加 api */ +export const pathAddApiString = (path: string): string => { + /** 生产环境不加 api */ + // if (import.meta.env.PROD) { + // return path; + // } + if (typeof path == 'string') { + if (`${path}`.startsWith('http') || `${path}`.startsWith('blob:http')) { + return path; + } + if (`${path}`.startsWith('/')) { + return `/api${path}`; + } + return `/api/${path}`; + } + return path; +}; + +/** 只保留数字 */ +export const preserveNumbers = (val: any): string => { + return `${val}`.replace(/[^0-9]/gi, ''); +}; + +/** 判断数据是不是数组类型 */ +export const isArray = (data: any): boolean => { + return data && Array.isArray(data); +}; + +export const toArray = (data: any): any[] => { + return isArray(data) ? data : []; +}; + +/** 判断数据是不是对象类型 */ +export const isObject = (data: any): boolean => { + return data && `${Object.prototype.toString.call(data)}`.includes('Object'); +}; + +export const toObject = (data: any): object => { + return isObject(data) ? data : {}; +}; + +/** object clean */ +export const objectClear = (data: any): any => { + if (isObject(data)) { + for (const key of Object.keys(data)) { + data[key] = undefined; + } + } + return data; +}; + +/** 获取时间毫秒 */ +export const getNowTime = (): number => Date.now(); + +/** 日期格式化 YYYY-MM-DD */ +export const dateFormat = (date: Date): string => { + return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date + .getDate() + .toString() + .padStart(2, '0')}`; +}; + +/** 获取当前日期 格式化 YYYY-MM-DD */ +export const getNowDate = (): string => { + return dateFormat(new Date()); +}; + +/** 判断是json数据 */ +export const isJson = (str: any): boolean => { + if (str && typeof str == 'string') { + try { + const obj = JSON.parse(str); + return obj && typeof obj == 'object'; + } catch (_e) { + // + } + } + return false; +}; + +/** + * 解码json数据 + * @param data 数据 + * @returns array | object | null + */ +export const jsonParse = (data: any): any[] | object | null => { + if (data) { + if (typeof data == 'string') { + try { + const obj = JSON.parse(data); + if (['Array', 'Object'].includes(Object.prototype.toString.call(obj).slice(8, -1))) { + return obj; + } + } catch (_e) { + // + } + } else if (['Array', 'Object'].includes(Object.prototype.toString.call(data).slice(8, -1))) { + return data; + } + } + return null; +}; + +/** 判断是数字 */ +export const isNumber = (value: any): boolean => { + // biome-ignore lint/suspicious/noGlobalIsNan: + // biome-ignore lint/suspicious/noGlobalIsFinite: + return !isNaN(Number.parseFloat(value)) && isFinite(value); +}; + +/** 转成数字 */ +export const toNumber = (str: any): number => { + return isNumber(str) ? Number(str) : 0; +}; + +/** 转成字符串并去除前后空格 */ +export const toStringAndTrim = (value: any, trim = true): string => { + const str = typeof value == 'string' ? value : ''; + return trim ? str.trim() : str; +}; + +/** 替换中文标点 ?【】:() */ +export const chinesePunctuationReplace = (str: string) => { + // ?【】:() + + if (str && typeof str == 'string') { + return str + .replace(/?/g, '?') + .replace(/【/g, '[') + .replace(/】/g, ']') + .replace(/:/g, ':') + .replace(/(/g, '(') + .replace(/)/g, ')') + .replace(/“/g, '"') + .replace(/”/g, '"') + .replace(/‘/g, '"') + .replace(/’/g, '"'); + } + return str; +}; + +/** 打开二维码窗口 */ +export const openPrintWindow = (msg: string, desc?: string) => { + window.open( + `/#/print/qrcode?msg=${encodeURIComponent(msg)}${desc ? `&desc=${encodeURIComponent(desc)}` : ''}`, + '_blank', + 'height=600,width=800', + ); +}; + +export const formatErpConfigList = (data: any[]) => { + const obj: any = {}; + toArray(data).forEach((item) => { + obj[item.config_type_en] = { + config_value: item.config_value, + }; + }); + return obj; +}; + +/** 转换文件大小 */ +export const formatFileSize = (fileSize: any): string => { + const file_size = toNumber(fileSize); + if (file_size == 0) { + return '0B'; + } + const unitArr = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + let index = 0; + index = Math.floor(Math.log(file_size) / Math.log(1024)); + const size = file_size / 1024 ** index; + return size.toFixed(0) + unitArr[index]; +}; + +/** 文本脱敏 */ +export function desensitizedCommon(str: string, begin = 1, end = 1) { + if (!str && begin + end >= str.length) { + return ''; + } + const leftStr = str.substring(0, begin); + const rightStr = str.substring(str.length - end, str.length); + let strCon = ''; + for (let i = 0; i < str.length - end - begin; i++) { + strCon += '*'; + } + return leftStr + strCon + rightStr; +} + +export const isImageFile = (path: string) => { + return /\.(jpe?g|png|gif|svg|webp|bmp)$/i.test(path); +}; + +export const getBoundingClientRect = ( + dom: any, +): { + bottom: number; + height: number; + left: number; + right: number; + top: number; + width: number; +} => { + return ( + dom?.getBoundingClientRect() || { + bottom: 0, + height: 0, + left: 0, + right: 0, + top: 0, + width: 0, + } + ); +}; + +/** + * 商品数量转换成显示单位数量 + * @param goods 商品 + * @param {string} key 商品数量的key + * @returns {Object} {nums: number, unit: string} + */ +export const goodsStockFormatShowUnit = (goods: any, key: string): { nums: any; unit: string } => { + const g: any = toObject(goods); + const unit = g.depot_show_unit_name || ''; + const value = g[key]; + // if (goods.depot_show_unit && goods.base_unit != goods.depot_show_unit) { + // value = new Big(toNumber(value) / toNumber(goods.unit_ratio)).toFixed(3); + // unit = goods.depot_show_unit_name; + // } + + return { + nums: value, + unit, + }; +}; + +type IGoodsRatioFormatNum = { + base_unit: any; + two_unit: any; + ratio: any; + unit: any; + num: any; +}; +/** 商品根据比例换算数量 */ +export const goodsRatioFormatNum = (goods: IGoodsRatioFormatNum): number => { + let num = toNumber(goods.num); + const ratio = toNumber(goods.ratio); + if (goods.base_unit == goods.unit) { + num = Big(num).times(ratio).toNumber(); + } else { + if (ratio != 0) { + num = Big(num).div(ratio).toNumber(); + } else { + console.warn('goodsRatioFormatNum被除数为0'); + } + } + return num; +}; + +/** + * 数组对象排序 数字类型 + * @param data + * @returns data + */ +export const arraySortTypeNumber = (arr: any, order?: string) => { + const data = toArray(arr); + if (order) { + const [key, sort] = order.split(' '); + data.sort((a: any, b: any) => { + if (sort === 'asc') { + return toNumber(a[key]) - toNumber(b[key]); + } else { + return toNumber(b[key]) - toNumber(a[key]); + } + }); + } + return data; +}; + +/** 导出文本 */ +export const exportText = (text: string, filename: string) => { + const blob = new Blob([`${text}`], { type: 'text/plain;charset=utf-8' }); + const objectURL = URL.createObjectURL(blob); + downloadFile(objectURL, filename); + URL.revokeObjectURL(objectURL); +}; + +/** 下载文件 */ +export const downloadFile = (objectURL: string, filename: string) => { + const aTag = document.createElement('a'); + aTag.href = objectURL; + aTag.download = filename; + aTag.click(); +}; + +/** 表格锁列小屏幕不锁列 */ +export const tableFixedByPhone = (fixed: 'left' | 'right') => { + if (window.dfConfig.isPhone) { + return undefined; + } + return fixed; +}; + +/** ajax参数转json字符串 */ +export const paramsToJson = (params: any): object => { + const obj: any = {}; + Object.keys(toObject(params)).forEach((key) => { + if (isArray(params[key]) || isObject(params[key])) { + obj[key] = JSON.stringify(params[key]); + } else { + obj[key] = params[key]; + } + }); + return obj; +}; + +export async function dingRequest(msg: string) { + const loginName = localStorage.getItem('login_name'); + const userPhone = localStorage.getItem('user_phone'); + const userIP = localStorage.getItem('userIP'); + const href = window.location.href; + const userAgent = navigator.userAgent; + + let baseMsg = `${DefaultERPName}发生错误\n报错地址:${href}\n登录用户:${loginName}\n手机号码:${userPhone}\nIP地址:${userIP}\n`; + baseMsg += `浏览器信息:${userAgent}\n触发时间:${dayjs().format('YYYY-MM-DD HH:mm:ss')}`; + + // !开发环境 + if (import.meta.env.DEV || location.hostname == 'free.erp') { + return; + } + // 发送钉钉消息 + return await fetch('https://chenfeng.tech:7779/Weixin-User-ding', { + method: 'POST', + headers: { + 'X-Requested-With': 'XMLHttpRequest', + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', + }, + body: `token=3945cee5317f212d824da3fef16e321bb23ae5c3cf6b5f81d37f90568be89b1e&msg=${baseMsg}\n${msg}`, + }); +} diff --git a/src/utils/commonUtils.ts b/src/utils/commonUtils.ts new file mode 100644 index 0000000..f8428fd --- /dev/null +++ b/src/utils/commonUtils.ts @@ -0,0 +1,184 @@ +import Big from 'big.js'; +import { isArray, isObject, toNumber, toObject, toStringAndTrim } from './common.ts'; + +/** 搜索参数格式化(字符串去除前后空格) */ +export const paramsFormat = ( + params: any, + config?: { + /** 是否将数组转json */ + arrayToJson?: boolean; + /** 是否将对象转json */ + objectToJson?: boolean; + /** 是否将数组和对象转json */ + toJson?: boolean; + }, +): object => { + const obj: any = {}; + Object.keys(toObject(params)).forEach((key) => { + if (typeof params[key] === 'string') { + obj[key] = toStringAndTrim(params[key]); + } else { + if (config?.toJson && (isArray(params[key]) || isObject(params[key]))) { + obj[key] = JSON.stringify(params[key]); + } else { + if (config?.arrayToJson && isArray(params[key])) { + obj[key] = JSON.stringify(params[key]); + } else if (config?.objectToJson && isObject(params[key])) { + obj[key] = JSON.stringify(params[key]); + } else { + obj[key] = params[key]; + } + } + } + }); + return obj; +}; + +/** + * sleep + * @param callback 回调函数 + * @param ms 毫秒, 默认300ms + * @returns + */ +export const sleep = (callback?: () => void, ms = 300): Promise => { + return new Promise((resolve) => { + setTimeout(() => { + callback?.(); + resolve(true); + }, ms); + }); +}; + +/** + * 空数据是返回前一页 + * @param dataLength + * @param curr_page 当前页 + * @param page 分页函数 + */ +export const navigateBackIfEmpty = (dataLength: number, curr_page: number, page: (curr: number) => void) => { + dataLength === 0 && curr_page > 1 && page(curr_page - 1); +}; + +/** 检测支持语言 */ +export const checkSupportLanguage = (lang?: string | null) => (lang && ['zh-cn', 'en'].includes(lang) ? lang : 'zh-cn'); + +// export const notificationFun = (option: { +// title?: React.ReactNode; +// type?: 'info' | 'success' | 'warning' | 'error' | 'normal'; +// content?: React.ReactNode; +// }) => { +// const { title = t('系统提示'), type = 'success' } = option; +// Notification[type]({ +// title: title, +// content: option.content || '', +// }); +// }; + +// export const modalFun = (option: { +// title?: React.ReactNode; +// type?: 'info' | 'success' | 'warning' | 'error' | 'confirm'; +// content?: React.ReactNode; +// onOk: (e?: MouseEvent) => Promise; +// okText?: string; +// okButtonProps?: ButtonProps; +// }) => { +// const { title = t('系统提示'), type = 'confirm', onOk } = option; +// Modal[type]({ +// title: title, +// content: option.content || '', +// okButtonProps: { +// autoFocus: true, +// ...option.okButtonProps, +// }, +// onOk: onOk, +// okText: option.okText, +// }); +// }; + +/** 笛卡尔积 */ +export const cartesianProduct = (...arrays: any[]) => { + let result: any[] = []; + // 处理边界情况:如果没有传入数组,返回空数组 + if (arrays.length === 0) return result; + + // 初始结果为第一个数组的每个元素组成的单元素数组 + result = arrays[0].map((item: any) => [item]); + + // 遍历剩余的数组,与当前结果合并计算笛卡尔积 + for (let i = 1; i < arrays.length; i++) { + const currentArray = arrays[i]; + const temp: any[] = []; + // 对于结果中的每个已有组合 + for (const existingCombination of result) { + // 与当前数组的每个元素组合 + for (const item of currentArray) { + temp.push([...existingCombination, item]); + } + } + + // 更新结果为新的组合 + result = temp; + } + + return result; +}; + +export const blobToFile = (blob: BlobPart[], fileName: string, options?: FilePropertyBag) => { + return new File( + blob, + fileName, + options ?? { + lastModified: Date.now(), + }, + ); +}; + +/** Map数据转对象 */ +export const MapToObject = (data: Map) => Object.fromEntries(data); + +/** Map数据转对象json */ +export const MapToJson = (data: Map) => JSON.stringify(MapToObject(data)); + +/** 深拷贝 Map 对象(支持基本类型和引用类型只支持(一层)) */ +export const deepCopyMap = (data: Map) => { + const newMap = new Map(); + for (const [key, value] of data) { + if (Array.isArray(value)) { + // 处理数组 + newMap.set(key, [...value]); // 数组深拷贝(一层) + } else if (isObject(value)) { + // 处理对象(非 null 且为对象类型) + newMap.set(key, { ...value }); // 对象深拷贝(一层) + } else { + // 基本类型直接赋值 + newMap.set(key, value); + } + } + return newMap; +}; + +/** Set数据转数组 */ +export const SetToArray = (data: Set) => [...data]; + +/** Set数据转数组json */ +export const SetToJson = (data: Set) => JSON.stringify(SetToArray(data)); + +/** 价格保留位数 */ +export const priceRetentionDigits = (value?: number) => { + return toNumber(Big(toNumber(value)).toFixed(window.dfERPConfig.PRICE_HOLD_POINT)); +}; + +/** 价格保留位数字符串类型 */ +export const priceRetentionDigitsString = (value?: number) => { + return Big(toNumber(value)).toFixed(window.dfERPConfig.PRICE_HOLD_POINT); +}; + +/** 数量保留位数 */ +export const numRetentionDigits = (value?: number) => { + return toNumber(Big(toNumber(value)).toFixed(window.dfERPConfig.NUMS_HOLD_POINT)); +}; + +/** 数量保留位数字符串类型 */ +export const numRetentionDigitsString = (value?: number) => { + return Big(toNumber(value)).toFixed(window.dfERPConfig.NUMS_HOLD_POINT); +}; diff --git a/src/utils/copyToClipboard.ts b/src/utils/copyToClipboard.ts new file mode 100644 index 0000000..89d8d53 --- /dev/null +++ b/src/utils/copyToClipboard.ts @@ -0,0 +1,25 @@ +export const copyToClipboard = (textToCopy: string) => { + // navigator clipboard 需要https等安全上下文 + if (navigator.clipboard && window.isSecureContext) { + // navigator clipboard 向剪贴板写文本 + return navigator.clipboard.writeText(textToCopy); + } else { + // 创建text area + const textArea = document.createElement('textarea'); + textArea.value = textToCopy; + // 使text area不在viewport,同时设置不可见 + textArea.style.position = 'absolute'; + textArea.style.opacity = '0'; + textArea.style.left = '-999999px'; + textArea.style.top = '-999999px'; + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + + return new Promise((resolve, reject) => { + // 执行复制命令并移除文本框 + document.execCommand('copy') ? resolve() : reject(); + textArea.remove(); + }); + } +}; diff --git a/src/utils/http.ts b/src/utils/http.ts new file mode 100644 index 0000000..b7f58ee --- /dev/null +++ b/src/utils/http.ts @@ -0,0 +1,69 @@ +import { pathAddApiString } from './common'; +import type { IAjaxDataBase } from './type'; + +// 辅助方法:统一处理响应内容(支持 json/text/blob 等格式) +// async function getResponseContent(response: any) { +// try { +// // 优先尝试解析为 JSON(大部分接口的错误响应格式) +// return await response.json(); +// } catch (_e) { +// try { +// // 若不是 JSON,解析为文本 +// return await response.text(); +// } catch (_e2) { +// // 若为二进制数据(如图片错误),返回类型提示 +// return `[二进制数据,类型: ${response.headers.get('Content-Type')}]`; +// } +// } +// } + +const ajax = async (method: string, url: string, data: any, config?: any) => { + try { + const res: IAjaxDataBase = await fetch(pathAddApiString(url), { + headers: { + 'X-Requested-With': 'XMLHttpRequest', + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', + }, + method: method, + body: data, + credentials: 'include', + mode: 'cors', + ...config, + }).then((res) => res.json()); + + return res; + } catch (error: any) { + const err = { + message: error.message, + status: error.status || '无', // 仅 HTTP 状态错误有 + url: error.url || url, + responseBody: error.responseBody || '无', // 仅 HTTP 状态错误有 + stack: error.stack, // 错误堆栈(定位代码位置) + cause: error.cause, // 网络错误的底层原因(Chrome 96+ 支持) + type: error.name, // 错误类型(如 TypeError/AbortError) + }; + + // console.error('【fetch 详细错误】', error); + // 可根据错误类型做针对性处理 + if (error.name === 'AbortError') { + console.log('请求被手动取消'); + } else if (error.message.includes('CORS')) { + console.log('跨域错误,请检查服务端跨域配置'); + } + + throw err; // 可选:向上层抛出,让调用方处理 + } +}; + +export const post = (url: string, data?: any, config?: any) => { + return ajax('POST', url, data, config); +}; + +export const get = (url: string, data: any, config?: any) => { + return ajax('GET', url, data, config); +}; + +export const request = { + post, + get, +}; diff --git a/src/utils/type.ts b/src/utils/type.ts new file mode 100644 index 0000000..7587377 --- /dev/null +++ b/src/utils/type.ts @@ -0,0 +1,25 @@ +import type { ForwardedRef } from 'react'; + +/** params的基础类型 */ +export type IParamsBase = { + curr_page: number; + page_count: number; + order?: string; +}; + +/** ajax返回的基础类型 */ +export type IAjaxDataBase = { + count: number | undefined; + err_msg?: string; + err_code?: number; + [key: string]: any; +}; + +export type IRef = { + ref: ForwardedRef; +}; + +export type IOption = { + value: string | number; + label: string; +}; diff --git a/src/utils/update.js b/src/utils/update.js new file mode 100644 index 0000000..16bbc7d --- /dev/null +++ b/src/utils/update.js @@ -0,0 +1,51 @@ +import * as fs from "node:fs"; + +// 获取版本号 +const currDate = new Date(); +const year = currDate.getFullYear(); +const month = currDate.getMonth() + 1; +const strMonth = month >= 1 && month <= 9 ? `0${month}` : `${month}`; +const date = currDate.getDate(); +const strDate = date >= 0 && date <= 9 ? `0${date}` : `${date}`; +const hourMinutes = `${currDate.getHours()}${currDate.getMinutes()}`; +const version = `V${year}${strMonth}${strDate}-${hourMinutes}`; +const logDir = process.argv.slice(2)[0] ?? ""; +//span: { xl: 2, xxl: 2 }, +// 读取组件版本 +fs.readFile("package.json", "utf8", (_err, dataStr) => { + const data = JSON.parse(dataStr); + let comStr = "[\n"; + Object.keys(data.dependencies).forEach((item, index) => { + comStr += ` {\n key: ${ + index + 1 + },\n label: '${item}',\n value: '${ + data.dependencies[item] + }',\n },\n`; + }); + comStr += " ]"; + + const cfg = `// 基础配置文件,记录版本号,编译时间等 +export const EnvConfig = { + version: '${version}', + compile: '${currDate.toLocaleString()}', + helpName: '易宝赞系统操作手册', + helpAddr: 'https://docs.qq.com/doc/DQXZTV3lvcXpqdkt3', + docName: '易宝赞系统更新说明 2025-04-27', + docAddr: 'https://docs.qq.com/aio/DS2NCRFFseG9Ma3Ja?p=60db8i0gVHuMAuNx56deBp', + comItems: ${comStr}, +}`; + + fs.writeFile(`./${logDir}/.cfg.ts`, cfg, (err) => { + if (err) console.log("日志写入失败", err); + else console.log("日志写入成功"); + }); + + fs.writeFile( + `./${logDir}/public/ver.txt`, + `${Math.round(Date.now() / 1000).toString(16)}`, + (err) => { + if (err) console.log("版本写入失败", err); + else console.log("版本写入成功"); + } + ); +}); diff --git a/src/utils/useRequest.ts b/src/utils/useRequest.ts new file mode 100644 index 0000000..e4e6c24 --- /dev/null +++ b/src/utils/useRequest.ts @@ -0,0 +1,154 @@ +import { useState } from 'react'; +// import { dingRequest } from '@/services/common'; +import { dingRequest, toNumber } from './common'; +import { notificationEventBus } from './EventBus'; +import { post } from './http'; +import type { IAjaxDataBase } from './type'; + +type IOptions = { + // onSuccess?: (res: IAjaxDataBase, params: any, cancel: boolean) => void; + onSuccess?: (res: IAjaxDataBase, params: any) => void; + /** 错误码为 0 的时候执行 */ + onSuccessCodeZero?: (res: IAjaxDataBase, params: any) => void; + onCatch?: (error: any) => void; + // method?: 'POST' | 'GET'; + hideNotice?: boolean; +}; + +export const useRequest = (url: string, options?: IOptions) => { + // 请求返回的数据 + const [data, setData] = useState(null); + // 请求返回的错误信息 + const [error, setError] = useState(null); + // 请求的loading 状态 + const [loading, setLoading] = useState(false); + const [count, setCount] = useState(0); + + // const cancelRef = useRef(false); + // function cancelRequest() { + // cancelRef.current = true; + // } + + // loader 方法通过 fetch API 发出 http 请求 + const request = async (data?: any, config?: any) => { + // cancelRef.current = false; + setLoading(true); + try { + const res = await post(url, data, config); + + if (res?.err_code != 0) { + if (res.err_code == 110000) { + location.href = '#/login'; + } else { + if (options?.hideNotice != true) { + notificationEventBus.emit({ + title: `${'错误码'}:${res.err_code}`, + description: res.err_msg, + type: 'error', + }); + } + } + } + if (res?.err_code === 0) { + options?.onSuccessCodeZero?.(res, data); + } + options?.onSuccess?.(res, data); + setData(res); + setCount(toNumber(res?.count)); + setLoading(false); + return res; + } catch (error: any) { + setError(error); + setLoading(false); + // const traces = toArray(error?.response?.data?.traces).map((el) => { + // return { + // file: el?.file, + // line: el?.line, + // message: el?.message, + // }; + // }); + console.log(error); + notificationEventBus.emit({ title: `${'服务错误'}`, description: `${error?.message}`, type: 'error' }); + options?.onCatch?.(error); + + // const params = JSON.parse(JSON.stringify(data)); + // console.log(error?.response?.data?.traces); + const msg = `接口地址:${url}\n响应信息:${error?.message}\n错误信息:${JSON.stringify( + error?.response?.data || '', + ).replace(/"/g, '')}\n请求参数:${JSON.stringify(data || '').replace(/"/g, '')}`; + // console.log(msg); + dingRequest(msg || '错误'); + } + }; + // 将 loader, data, error, loading 作为自定义 hook 的返回值 cancelRequest + return { request, data, loading, error, count }; +}; + +type IConfig = { + hideNotice?: boolean; +}; + +/** + * 轻量级请求 + * @param url 请求地址 + * @param data 请求参数 + * @param config 请求配置项 + 提示组件 {} + * @returns Promise + * */ +export const requestLite = async (url: string, data?: any, config?: IConfig) => { + const { hideNotice = false, ...option } = config || {}; + try { + const res = await post(url, data, option); + if (res?.err_code != 0) { + if (res?.err_code == 110000) { + location.href = '#/login'; + } else { + if (!hideNotice) { + notificationEventBus.emit({ title: `${'错误码'}:${res.err_code}`, description: res.err_msg, type: 'error' }); + } + } + } + return res; + } catch (error: any) { + console.log(error); + if (!hideNotice) { + notificationEventBus.emit({ title: `${'服务错误'}`, description: `${error?.message}`, type: 'error' }); + } + const msg = `接口地址:${url}\n响应信息:${error?.message}\n错误信息:${JSON.stringify( + error?.response?.data || '', + ).replace(/"/g, '')}\n请求参数:${JSON.stringify(data || '').replace(/"/g, '')}`; + dingRequest(msg || '错误'); + } +}; + +/** + * 请求获取文件 + * @param url 文件地址 + * @param options 参数 + * @param options.oss 判断是不是 oss文件, 默认 true + * @returns Promise + */ +// export const requestFile = async ( +// url: string, +// options: { oss?: boolean } = { oss: true }, +// ) => { +// const u = url.startsWith("/") ? url.substring(1) : url; +// return new Promise((resolve, reject) => { +// try { +// fetch(options.oss ? `${OSSBaseUrl}${u}` : url, { mode: "cors" }) +// .then((response) => { +// if (!response.ok) { +// throw new Error(`HTTP 错误!状态:${response.status}`); +// } +// return response.blob(); +// }) +// .then((res) => { +// // const fileName = url.split('/').pop() || ''; +// // resolve(blobToFile([res], fileName)); +// resolve(res); +// }); +// } catch (error) { +// reject(error); +// } +// }); +// }; diff --git a/src/utils/useRequest2.ts b/src/utils/useRequest2.ts new file mode 100644 index 0000000..7601dae --- /dev/null +++ b/src/utils/useRequest2.ts @@ -0,0 +1,79 @@ +// import { OSSBaseUrl } from '@/config/config'; +import type { NotificationInstance } from 'antd/es/notification/interface'; +import { dingRequest } from './common'; +import { post } from './http'; + +type IConfig = { + notification?: NotificationInstance; +}; + +/** + * 轻量级请求 + * @param url 请求地址 + * @param data 请求参数 + * @param config 请求配置项 + 提示组件 {} + * @returns Promise + * */ +export const requestLite = async (url: string, data?: any, config?: IConfig) => { + const { notification, ...option } = config || {}; + try { + const res = await post(url, data, option); + if (res?.err_code != 0) { + if (res?.err_code == 110000) { + location.href = '#/login'; + } else { + if (notification) { + notification.error({ + title: `${'错误码'}:${res.err_code}`, + description: res.err_msg, + }); + } + } + } + return res; + } catch (error: any) { + console.log(error); + if (notification) { + notification.error({ + title: `${'服务错误'}`, + description: `${error?.message}, ${error?.response?.data?.message || ''}`, + }); + } + const msg = `接口地址:${url}\n响应信息:${error?.message}\n错误信息:${JSON.stringify( + error?.response?.data || '', + ).replace(/"/g, '')}\n请求参数:${JSON.stringify(data || '').replace(/"/g, '')}`; + dingRequest(msg || '错误'); + } +}; + +/** + * 请求获取文件 + * @param url 文件地址 + * @param options 参数 + * @param options.oss 判断是不是 oss文件, 默认 true + * @returns Promise + */ +// export const requestFile = async ( +// url: string, +// options: { oss?: boolean } = { oss: true }, +// ) => { +// const u = url.startsWith("/") ? url.substring(1) : url; +// return new Promise((resolve, reject) => { +// try { +// fetch(options.oss ? `${OSSBaseUrl}${u}` : url, { mode: "cors" }) +// .then((response) => { +// if (!response.ok) { +// throw new Error(`HTTP 错误!状态:${response.status}`); +// } +// return response.blob(); +// }) +// .then((res) => { +// // const fileName = url.split('/').pop() || ''; +// // resolve(blobToFile([res], fileName)); +// resolve(res); +// }); +// } catch (error) { +// reject(error); +// } +// }); +// }; diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..eeeb99f --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1,23 @@ +/// + +declare interface Window { + dfConfig: { + isPhone: boolean; + /** 表格设置粘性头部 */ + tableStickyOffsetHeader: number; + /** 语言 */ + language: "zh-cn" | "en"; + /** 视口高度 */ + vhUnit: "vh" | "dvh"; + }; + showProcessGraphId?: number; + debug: { + add: (data: { [key: string]: any }) => void; + }; + dfERPConfig: { + /** 价格小数点保留位数 */ + PRICE_HOLD_POINT: number; + /** 数量小数点保留位数 */ + NUMS_HOLD_POINT: number; + }; +} diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..c3e704f --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..8a67f62 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..6b3413b --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,37 @@ +import path from 'node:path'; +import react from '@vitejs/plugin-react-swc'; +// import { visualizer } from 'rollup-plugin-visualizer'; +import { defineConfig } from 'vite'; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + react(), // 打包后生成体积分析报告(dist/stats.html) + // visualizer({ + // // open: true, // 自动打开报告页面 + // filename: 'stats.html', + // }), + ], + base: './', + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, + server: { + port: 4050, + host: '0.0.0.0', + proxy: { + '/api': { + target: 'http://192.168.1.138:93', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, ''), // 不可以省略rewrite + }, + '/static': { + target: 'http://192.168.1.138:83', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, ''), // 不可以省略rewrite + }, + }, + }, +});