From 6a4e99885d5f4e5cd55b14e711a6e46790fd72a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Thu, 17 Jul 2025 21:56:53 +0800 Subject: [PATCH 01/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E6=96=B0=E9=A1=B9=E7=9B=AE=E6=A8=A1=E6=9D=BF=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/.env.development | 4 +++ nkebao/.env.production | 4 +++ nkebao/.eslintrc.js | 21 ++++++++++++ nkebao/.gitignore | 6 ++++ nkebao/README.md | 33 ++++++++++++++++++ nkebao/index.html | 13 +++++++ nkebao/package.json | 37 ++++++++++++++++++++ nkebao/postcss.config.js | 8 +++++ nkebao/src/App.tsx | 23 +++++++++++++ nkebao/src/api/module/auth.ts | 22 ++++++++++++ nkebao/src/api/request.ts | 64 +++++++++++++++++++++++++++++++++++ nkebao/src/main.tsx | 7 ++++ nkebao/src/pages/About.tsx | 7 ++++ nkebao/src/pages/Home.tsx | 7 ++++ nkebao/src/react-app-env.d.ts | 1 + nkebao/src/styles/global.scss | 18 ++++++++++ nkebao/tsconfig.json | 23 +++++++++++++ nkebao/vite.config.ts | 15 ++++++++ nkebao/技术栈.md | 26 ++++++++++++++ 19 files changed, 339 insertions(+) create mode 100644 nkebao/.env.development create mode 100644 nkebao/.env.production create mode 100644 nkebao/.eslintrc.js create mode 100644 nkebao/.gitignore create mode 100644 nkebao/README.md create mode 100644 nkebao/index.html create mode 100644 nkebao/package.json create mode 100644 nkebao/postcss.config.js create mode 100644 nkebao/src/App.tsx create mode 100644 nkebao/src/api/module/auth.ts create mode 100644 nkebao/src/api/request.ts create mode 100644 nkebao/src/main.tsx create mode 100644 nkebao/src/pages/About.tsx create mode 100644 nkebao/src/pages/Home.tsx create mode 100644 nkebao/src/react-app-env.d.ts create mode 100644 nkebao/src/styles/global.scss create mode 100644 nkebao/tsconfig.json create mode 100644 nkebao/vite.config.ts create mode 100644 nkebao/技术栈.md diff --git a/nkebao/.env.development b/nkebao/.env.development new file mode 100644 index 000000000..fe189d220 --- /dev/null +++ b/nkebao/.env.development @@ -0,0 +1,4 @@ +# 基础环境变量示例 +VITE_API_BASE_URL=https://ckbapi.quwanzhi.com +VITE_APP_TITLE=Nkebao Base + diff --git a/nkebao/.env.production b/nkebao/.env.production new file mode 100644 index 000000000..fe189d220 --- /dev/null +++ b/nkebao/.env.production @@ -0,0 +1,4 @@ +# 基础环境变量示例 +VITE_API_BASE_URL=https://ckbapi.quwanzhi.com +VITE_APP_TITLE=Nkebao Base + diff --git a/nkebao/.eslintrc.js b/nkebao/.eslintrc.js new file mode 100644 index 000000000..887b3d7ab --- /dev/null +++ b/nkebao/.eslintrc.js @@ -0,0 +1,21 @@ +module.exports = { + root: true, + env: { + browser: true, + es2021: true, + node: true, + }, + extends: [ + 'react-app', + 'plugin:react/recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended', + ], + parser: '@typescript-eslint/parser', + plugins: ['react', '@typescript-eslint', 'prettier'], + rules: { + 'prettier/prettier': 'warn', + 'react/react-in-jsx-scope': 'off', + '@typescript-eslint/no-unused-vars': 'warn', + }, +}; \ No newline at end of file diff --git a/nkebao/.gitignore b/nkebao/.gitignore new file mode 100644 index 000000000..57747f4f2 --- /dev/null +++ b/nkebao/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +dist/ +build/ +yarn.lock +.env +.DS_Store \ No newline at end of file diff --git a/nkebao/README.md b/nkebao/README.md new file mode 100644 index 000000000..d67e78b27 --- /dev/null +++ b/nkebao/README.md @@ -0,0 +1,33 @@ +# Nkebao Base + +基于 React + TypeScript + axios + sass + React Router v6 + antd + antd-mobile 的基础项目模板。 + +## 启动方式 + +```bash +yarn install +yarn start +``` + +## 技术栈 +- React +- TypeScript +- axios +- sass +- React Router v6 +- antd +- antd-mobile +- postcss-pxtorem (rem 适配) + +## 目录结构 + +``` +src/ + api/ # axios 封装 + pages/ # 页面组件 + styles/ # 全局样式 + App.tsx # 入口组件 + main.tsx # 入口文件 +public/ + index.html # HTML 模板 +``` \ No newline at end of file diff --git a/nkebao/index.html b/nkebao/index.html new file mode 100644 index 000000000..2aaa892ee --- /dev/null +++ b/nkebao/index.html @@ -0,0 +1,13 @@ + + + + + + Nkebao Base + + + +
+ + + \ No newline at end of file diff --git a/nkebao/package.json b/nkebao/package.json new file mode 100644 index 000000000..cfa0f01bb --- /dev/null +++ b/nkebao/package.json @@ -0,0 +1,37 @@ +{ + "name": "nkebao-base", + "version": "0.1.0", + "private": true, + "dependencies": { + "antd": "^5.13.1", + "antd-mobile": "^5.39.1", + "axios": "^1.6.7", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.20.0" + }, + "devDependencies": { + "@types/node": "^24.0.14", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@typescript-eslint/eslint-plugin": "^7.7.0", + "@typescript-eslint/parser": "^7.7.0", + "@vitejs/plugin-react": "^4.6.0", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-react": "^7.34.1", + "postcss": "^8.4.38", + "postcss-pxtorem": "^6.0.0", + "prettier": "^3.2.5", + "sass": "^1.75.0", + "typescript": "^5.4.5", + "vite": "^7.0.5" + }, + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}' --fix" + } +} diff --git a/nkebao/postcss.config.js b/nkebao/postcss.config.js new file mode 100644 index 000000000..34ea9ede4 --- /dev/null +++ b/nkebao/postcss.config.js @@ -0,0 +1,8 @@ +module.exports = { + plugins: { + 'postcss-pxtorem': { + rootValue: 16, + propList: ['*'], + }, + }, +}; \ No newline at end of file diff --git a/nkebao/src/App.tsx b/nkebao/src/App.tsx new file mode 100644 index 000000000..bb423eff6 --- /dev/null +++ b/nkebao/src/App.tsx @@ -0,0 +1,23 @@ +import React from "react"; +import { BrowserRouter, Routes, Route } from "react-router-dom"; +import { Button } from "antd"; +import { Button as MobileButton } from "antd-mobile"; +import Home from "./pages/Home"; +import About from "./pages/About"; + +function App() { + return ( + + + } /> + } /> + +
+ + Antd-Mobile 按钮 +
+
+ ); +} + +export default App; diff --git a/nkebao/src/api/module/auth.ts b/nkebao/src/api/module/auth.ts new file mode 100644 index 000000000..dba5ef64a --- /dev/null +++ b/nkebao/src/api/module/auth.ts @@ -0,0 +1,22 @@ +import { request } from '@/api/request'; + +export const list = (data?: any) => + request('/cw/companyUser/list', data, 'GET'); + +export const joinAudit = (data: any) => + request('/cw/companyUser/joinAudit', data, 'PUT'); + +export const listJoinAudit = (data?: any) => + request('/cw/companyUser/listJoinAudit', data, 'GET'); + +export const CwCompanyUserApplyAdd = (data: any) => + request('/cw/CwCompanyUserApply/add', data, 'PUT'); + +export const CwCompanyUserApplyAdminList = (data?: any) => + request('/cw/CwCompanyUserApply/adminList', data, 'GET'); + +export const CwCompanyUserApplyAudit = (data: any) => + request('/cw/CwCompanyUserApply/audit', data, 'POST'); + +export const CwCompanyUserApplyUserList = (data?: any) => + request('/cw/CwCompanyUserApply/userList', data, 'GET'); \ No newline at end of file diff --git a/nkebao/src/api/request.ts b/nkebao/src/api/request.ts new file mode 100644 index 000000000..aaa2dca13 --- /dev/null +++ b/nkebao/src/api/request.ts @@ -0,0 +1,64 @@ +import axios, { AxiosInstance, AxiosRequestConfig, Method, AxiosResponse } from 'axios'; + +const DEBOUNCE_GAP = 1000; +const debounceMap = new Map(); + +const instance: AxiosInstance = axios.create({ + baseURL: (import.meta as any).env?.VITE_API_BASE_URL || '/api', + timeout: 10000, + headers: { + 'Content-Type': 'application/json', + }, +}); + +instance.interceptors.request.use(config => { + const token = localStorage.getItem('token'); + if (token) { + config.headers = config.headers || {}; + config.headers['Authorization'] = `Bearer ${token}`; + } + return config; +}); + +instance.interceptors.response.use( + (res: AxiosResponse) => { + if (res.data && (res.data.code === 200 || res.data.success)) { + return res.data.data ?? res.data; + } + window?.alert?.(res.data?.msg || '接口错误'); + return Promise.reject(res.data?.msg || '接口错误'); + }, + err => { + window?.alert?.(err.message || '网络异常'); + return Promise.reject(err); + } +); + +export function request( + url: string, + data?: any, + method: Method = 'GET', + config?: AxiosRequestConfig +): Promise { + const key = `${method}_${url}_${JSON.stringify(data)}`; + const now = Date.now(); + const last = debounceMap.get(key) || 0; + if (now - last < DEBOUNCE_GAP) { + return Promise.reject('请求过于频繁,请稍后再试'); + } + debounceMap.set(key, now); + + const axiosConfig: AxiosRequestConfig = { + url, + method, + ...config, + }; + if (method.toUpperCase() === 'GET') { + axiosConfig.params = data; + } else { + axiosConfig.data = data; + } + return instance(axiosConfig); +} + +export default request; \ No newline at end of file diff --git a/nkebao/src/main.tsx b/nkebao/src/main.tsx new file mode 100644 index 000000000..f62c6b7bd --- /dev/null +++ b/nkebao/src/main.tsx @@ -0,0 +1,7 @@ +import React from "react"; +import { createRoot } from "react-dom/client"; +import App from "./App"; +import "./styles/global.scss"; + +const root = createRoot(document.getElementById("root")!); +root.render(); diff --git a/nkebao/src/pages/About.tsx b/nkebao/src/pages/About.tsx new file mode 100644 index 000000000..370fdfe27 --- /dev/null +++ b/nkebao/src/pages/About.tsx @@ -0,0 +1,7 @@ +import React from "react"; + +const About: React.FC = () => { + return

关于 About

; +}; + +export default About; diff --git a/nkebao/src/pages/Home.tsx b/nkebao/src/pages/Home.tsx new file mode 100644 index 000000000..2f0a324ae --- /dev/null +++ b/nkebao/src/pages/Home.tsx @@ -0,0 +1,7 @@ +import React from "react"; + +const Home: React.FC = () => { + return

首页 Home

; +}; + +export default Home; diff --git a/nkebao/src/react-app-env.d.ts b/nkebao/src/react-app-env.d.ts new file mode 100644 index 000000000..751f967ff --- /dev/null +++ b/nkebao/src/react-app-env.d.ts @@ -0,0 +1 @@ +/// \ No newline at end of file diff --git a/nkebao/src/styles/global.scss b/nkebao/src/styles/global.scss new file mode 100644 index 000000000..62f634696 --- /dev/null +++ b/nkebao/src/styles/global.scss @@ -0,0 +1,18 @@ +html, body { + height: 100%; + overflow: hidden; // 禁止 body 滚动和回弹 +} + +#root, .app-content { + height: 100vh; + overflow-y: auto; + -webkit-overflow-scrolling: touch; // iOS 惯性滚动 +} + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + background: #f5f5f5; +} \ No newline at end of file diff --git a/nkebao/tsconfig.json b/nkebao/tsconfig.json new file mode 100644 index 000000000..53184fe34 --- /dev/null +++ b/nkebao/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "baseUrl": "./", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src"] +} \ No newline at end of file diff --git a/nkebao/vite.config.ts b/nkebao/vite.config.ts new file mode 100644 index 000000000..52f1437a9 --- /dev/null +++ b/nkebao/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import path from 'path'; + +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + '@': path.resolve('src'), + }, + }, + server: { + open: true, + }, +}); \ No newline at end of file diff --git a/nkebao/技术栈.md b/nkebao/技术栈.md new file mode 100644 index 000000000..be554562e --- /dev/null +++ b/nkebao/技术栈.md @@ -0,0 +1,26 @@ +## 使用技术栈 +- React 18 +- TypeScript +- Vite(新一代前端构建工具) +- axios +- sass (scss) +- React Router v6 +- antd-mobile +- antd(已设置基础单位为 rem,配合 postcss-pxtorem) +- postcss-pxtorem(px 转 rem,移动端适配) +- ESLint + Prettier(代码规范与自动格式化) +- 路径别名 @ 指向 src 目录 + +## 关于兼容与工程化 +- 自动化脚本(yarn lint、yarn dev 等) +- 移动端 rem 适配(html 根字体 + pxtorem) +- iOS 浏览器滚动回弹兼容问题已通过全局样式处理 +- 支持 VS Code 编辑器自动格式化(推荐配合 ESLint/Prettier 插件) + +## 目录结构简要 +- src/ 业务源码(pages、api、styles、App.tsx、main.tsx 等) +- public/ 静态资源目录 +- index.html 项目入口(根目录) +- vite.config.ts 构建与路径别名配置 +- tsconfig.json TypeScript 配置 +- .eslintrc.js 代码规范配置 \ No newline at end of file From 9d18af22f10deb093b06b279da4d105c48ec1933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Thu, 17 Jul 2025 22:19:37 +0800 Subject: [PATCH 02/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20store=20=E5=92=8C=20router=20=E5=B0=81=E8=A3=85=E5=AE=8C?= =?UTF-8?q?=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/package.json | 3 ++- nkebao/src/App.tsx | 13 ++++------ nkebao/src/api/request.ts | 24 +++++++++++++++---- nkebao/src/router/index.tsx | 34 +++++++++++++++++++++++++++ nkebao/src/router/module/about.tsx | 10 ++++++++ nkebao/src/router/module/home.tsx | 17 ++++++++++++++ nkebao/src/router/permissionRoute.tsx | 28 ++++++++++++++++++++++ nkebao/src/store/index.ts | 2 ++ nkebao/src/store/module/user.ts | 20 ++++++++++++++++ 9 files changed, 137 insertions(+), 14 deletions(-) create mode 100644 nkebao/src/router/index.tsx create mode 100644 nkebao/src/router/module/about.tsx create mode 100644 nkebao/src/router/module/home.tsx create mode 100644 nkebao/src/router/permissionRoute.tsx create mode 100644 nkebao/src/store/index.ts create mode 100644 nkebao/src/store/module/user.ts diff --git a/nkebao/package.json b/nkebao/package.json index cfa0f01bb..f9f4ac6e2 100644 --- a/nkebao/package.json +++ b/nkebao/package.json @@ -8,7 +8,8 @@ "axios": "^1.6.7", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-router-dom": "^6.20.0" + "react-router-dom": "^6.20.0", + "zustand": "^5.0.6" }, "devDependencies": { "@types/node": "^24.0.14", diff --git a/nkebao/src/App.tsx b/nkebao/src/App.tsx index bb423eff6..4437b1404 100644 --- a/nkebao/src/App.tsx +++ b/nkebao/src/App.tsx @@ -1,22 +1,17 @@ import React from "react"; -import { BrowserRouter, Routes, Route } from "react-router-dom"; +import AppRouter from "@/router"; import { Button } from "antd"; import { Button as MobileButton } from "antd-mobile"; -import Home from "./pages/Home"; -import About from "./pages/About"; function App() { return ( - - - } /> - } /> - + <> +
Antd-Mobile 按钮
-
+ ); } diff --git a/nkebao/src/api/request.ts b/nkebao/src/api/request.ts index aaa2dca13..bd7a086a6 100644 --- a/nkebao/src/api/request.ts +++ b/nkebao/src/api/request.ts @@ -1,4 +1,5 @@ import axios, { AxiosInstance, AxiosRequestConfig, Method, AxiosResponse } from 'axios'; +import { Toast } from 'antd-mobile'; const DEBOUNCE_GAP = 1000; const debounceMap = new Map(); @@ -22,14 +23,28 @@ instance.interceptors.request.use(config => { instance.interceptors.response.use( (res: AxiosResponse) => { - if (res.data && (res.data.code === 200 || res.data.success)) { + const { code, success, msg } = res.data || {}; + if (code === 200 || success) { return res.data.data ?? res.data; } - window?.alert?.(res.data?.msg || '接口错误'); - return Promise.reject(res.data?.msg || '接口错误'); + // 业务错误统一提示 + Toast.show({ content: msg || '接口错误', position: 'top' }); + // 分类处理 + if (code === 401) { + // 未登录或登录失效 + // 可跳转登录页或清除 token + localStorage.removeItem('token'); + } else if (code === 403) { + // 无权限 + // 可做特殊处理 + } else if (code === 500) { + // 服务端异常 + } + return Promise.reject(msg || '接口错误'); }, err => { - window?.alert?.(err.message || '网络异常'); + // 网络错误、超时等 + Toast.show({ content: err.message || '网络异常', position: 'top' }); return Promise.reject(err); } ); @@ -44,6 +59,7 @@ export function request( const now = Date.now(); const last = debounceMap.get(key) || 0; if (now - last < DEBOUNCE_GAP) { + Toast.show({ content: '请求过于频繁,请稍后再试', position: 'top' }); return Promise.reject('请求过于频繁,请稍后再试'); } debounceMap.set(key, now); diff --git a/nkebao/src/router/index.tsx b/nkebao/src/router/index.tsx new file mode 100644 index 000000000..0c58db7ea --- /dev/null +++ b/nkebao/src/router/index.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import { BrowserRouter, useRoutes } from "react-router-dom"; +import homeRoutes from "./module/home"; +import aboutRoutes from "./module/about"; +import PermissionRoute from "./permissionRoute"; + +function wrapWithPermission(route) { + if (route.auth) { + return { + ...route, + element: ( + + {route.element} + + ), + }; + } + return route; +} + +const routes = [ + ...homeRoutes.map(wrapWithPermission), + ...aboutRoutes.map(wrapWithPermission), +]; + +const AppRoutes = () => useRoutes(routes); + +const AppRouter: React.FC = () => ( + + + +); + +export default AppRouter; diff --git a/nkebao/src/router/module/about.tsx b/nkebao/src/router/module/about.tsx new file mode 100644 index 000000000..d76d7e08e --- /dev/null +++ b/nkebao/src/router/module/about.tsx @@ -0,0 +1,10 @@ +import About from "@/pages/About"; + +const aboutRoutes = [ + { + path: "/about", + element: , + }, +]; + +export default aboutRoutes; diff --git a/nkebao/src/router/module/home.tsx b/nkebao/src/router/module/home.tsx new file mode 100644 index 000000000..a4c5247a4 --- /dev/null +++ b/nkebao/src/router/module/home.tsx @@ -0,0 +1,17 @@ +import Home from "@/pages/Home"; + +const homeRoutes = [ + { + path: "/", + element: , + auth: false, // 不需要权限 + }, + { + path: "/dashboard", + element: , + auth: true, // 需要登录 + requiredRole: "admin", // 需要 admin 角色 + }, +]; + +export default homeRoutes; diff --git a/nkebao/src/router/permissionRoute.tsx b/nkebao/src/router/permissionRoute.tsx new file mode 100644 index 000000000..66b0b3dcb --- /dev/null +++ b/nkebao/src/router/permissionRoute.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import { Navigate, useLocation } from "react-router-dom"; +import { useUserStore } from "@/store/module/user"; // 假设你用 zustand 管理用户状态 + +interface Props { + children: React.ReactNode; + requiredRole?: string; // 可选:需要的角色 +} + +const PermissionRoute: React.FC = ({ children, requiredRole }) => { + const user = useUserStore((state) => state.user); + const location = useLocation(); + + // 未登录 + if (!user) { + return ; + } + + // 有角色要求但不满足 + if (requiredRole && user.role !== requiredRole) { + return ; + } + + // 通过 + return <>{children}; +}; + +export default PermissionRoute; diff --git a/nkebao/src/store/index.ts b/nkebao/src/store/index.ts new file mode 100644 index 000000000..8edf9c6ee --- /dev/null +++ b/nkebao/src/store/index.ts @@ -0,0 +1,2 @@ +export * from './module/user'; +// 未来可继续合并其他模块 \ No newline at end of file diff --git a/nkebao/src/store/module/user.ts b/nkebao/src/store/module/user.ts new file mode 100644 index 000000000..63202e04d --- /dev/null +++ b/nkebao/src/store/module/user.ts @@ -0,0 +1,20 @@ +// src/store/user.ts +import { create } from 'zustand'; + +export interface User { + name: string; + role: string; + token: string; +} + +interface UserState { + user: User | null; + setUser: (user: User) => void; + clearUser: () => void; +} + +export const useUserStore = create((set) => ({ + user: null, + setUser: (user) => set({ user }), + clearUser: () => set({ user: null }), +})); \ No newline at end of file From f39a33819e1f67e8b3c4b2e36341c30ecf45bbb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Thu, 17 Jul 2025 22:42:24 +0800 Subject: [PATCH 03/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E5=9F=BA=E7=A1=80=E6=A1=86=E6=9E=B6=E5=AE=8C=E6=88=90?= =?UTF-8?q?=EF=BC=8C=E6=B5=8B=E8=AF=95=E6=89=93=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/router/index.tsx | 26 +++++++++++----- nkebao/src/store/createPersistStore.ts | 16 ++++++++++ nkebao/src/store/module/user.ts | 43 ++++++++++++++------------ nkebao/src/vite-env.d.ts | 1 + 4 files changed, 58 insertions(+), 28 deletions(-) create mode 100644 nkebao/src/store/createPersistStore.ts create mode 100644 nkebao/src/vite-env.d.ts diff --git a/nkebao/src/router/index.tsx b/nkebao/src/router/index.tsx index 0c58db7ea..2c7c0311e 100644 --- a/nkebao/src/router/index.tsx +++ b/nkebao/src/router/index.tsx @@ -1,10 +1,23 @@ import React from "react"; -import { BrowserRouter, useRoutes } from "react-router-dom"; -import homeRoutes from "./module/home"; -import aboutRoutes from "./module/about"; +import { BrowserRouter, useRoutes, RouteObject } from "react-router-dom"; import PermissionRoute from "./permissionRoute"; -function wrapWithPermission(route) { +// 动态导入所有 module 下的 ts/tsx 路由模块 +const modules = import.meta.glob("./module/*.{ts,tsx}", { eager: true }); + +// 合并所有模块的默认导出(假设每个模块都是 export default 路由数组) +const allRoutes: (RouteObject & { auth?: boolean; requiredRole?: string })[] = + []; +Object.values(modules).forEach((mod: any) => { + if (Array.isArray(mod.default)) { + allRoutes.push(...mod.default); + } +}); + +// 权限包装 +function wrapWithPermission( + route: RouteObject & { auth?: boolean; requiredRole?: string } +) { if (route.auth) { return { ...route, @@ -18,10 +31,7 @@ function wrapWithPermission(route) { return route; } -const routes = [ - ...homeRoutes.map(wrapWithPermission), - ...aboutRoutes.map(wrapWithPermission), -]; +const routes = allRoutes.map(wrapWithPermission); const AppRoutes = () => useRoutes(routes); diff --git a/nkebao/src/store/createPersistStore.ts b/nkebao/src/store/createPersistStore.ts new file mode 100644 index 000000000..cdfbbb076 --- /dev/null +++ b/nkebao/src/store/createPersistStore.ts @@ -0,0 +1,16 @@ +// src/store/createPersistStore.ts +import { create } from 'zustand'; +import { persist, PersistOptions } from 'zustand/middleware'; + +export function createPersistStore( + createState: (set: any, get: any) => T, + name: string, + partialize?: (state: T) => Partial +) { + return create()( + persist(createState, { + name, + partialize, + } as PersistOptions) + ); +} diff --git a/nkebao/src/store/module/user.ts b/nkebao/src/store/module/user.ts index 63202e04d..876c8a774 100644 --- a/nkebao/src/store/module/user.ts +++ b/nkebao/src/store/module/user.ts @@ -1,20 +1,23 @@ -// src/store/user.ts -import { create } from 'zustand'; - -export interface User { - name: string; - role: string; - token: string; -} - -interface UserState { - user: User | null; - setUser: (user: User) => void; - clearUser: () => void; -} - -export const useUserStore = create((set) => ({ - user: null, - setUser: (user) => set({ user }), - clearUser: () => set({ user: null }), -})); \ No newline at end of file +import { createPersistStore } from '@/store/createPersistStore'; + +export interface User { + name: string; + role: string; + token: string; +} + +interface UserState { + user: User | null; + setUser: (user: User) => void; + clearUser: () => void; +} + +export const useUserStore = createPersistStore( + (set) => ({ + user: null, + setUser: (user) => set({ user }), + clearUser: () => set({ user: null }), + }), + 'user-store', + (state) => ({ user: state.user }) +); \ No newline at end of file diff --git a/nkebao/src/vite-env.d.ts b/nkebao/src/vite-env.d.ts new file mode 100644 index 000000000..7d0ff9efa --- /dev/null +++ b/nkebao/src/vite-env.d.ts @@ -0,0 +1 @@ +/// From f23286682568844cb9e41d25268b36ee203e9952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Thu, 17 Jul 2025 22:46:39 +0800 Subject: [PATCH 04/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E5=9F=BA=E7=A1=80=E6=A0=B7=E5=BC=8F=E6=9E=84=E5=BB=BA?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/README.md | 33 ++++++++---- nkebao/src/styles/global.scss | 98 ++++++++++++++++++++++++++++++++++- 2 files changed, 119 insertions(+), 12 deletions(-) diff --git a/nkebao/README.md b/nkebao/README.md index d67e78b27..88c0217ec 100644 --- a/nkebao/README.md +++ b/nkebao/README.md @@ -1,29 +1,40 @@ # Nkebao Base -基于 React + TypeScript + axios + sass + React Router v6 + antd + antd-mobile 的基础项目模板。 +一个现代化的 React + TypeScript 前端项目模板,适用于中后台管理系统、移动端 H5 等场景。 +集成了 Vite 极速构建、Zustand 轻量状态管理、模块化路由与权限拦截、axios 请求封装、移动端 rem 适配等主流工程能力。 + +--- ## 启动方式 ```bash yarn install -yarn start +yarn dev ``` +--- + ## 技术栈 -- React -- TypeScript -- axios -- sass -- React Router v6 -- antd -- antd-mobile -- postcss-pxtorem (rem 适配) + +- **React 18**:主流前端框架 +- **TypeScript**:类型安全开发 +- **Vite**:极速开发与构建工具 +- **axios**:请求库,统一封装 +- **sass**:CSS 预处理 +- **React Router v6**:路由管理,支持模块化与权限拦截 +- **antd / antd-mobile**:桌面端与移动端 UI 组件库 +- **postcss-pxtorem**:rem 适配,移动端友好 +- **Zustand**:轻量状态管理,支持持久化 +- **ESLint + Prettier**:代码规范与自动格式化 +- **路径别名 @**:简化 import + +--- ## 目录结构 ``` src/ - api/ # axios 封装 + api/ # axios 封装与接口模块 pages/ # 页面组件 styles/ # 全局样式 App.tsx # 入口组件 diff --git a/nkebao/src/styles/global.scss b/nkebao/src/styles/global.scss index 62f634696..314af21a4 100644 --- a/nkebao/src/styles/global.scss +++ b/nkebao/src/styles/global.scss @@ -1,18 +1,114 @@ +/* Reset & 基础样式,兼容移动端和PC端,补充 iOS/安卓容差处理 */ + +/* 1. 通用 Reset */ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, table, caption, +tbody, tfoot, thead, tr, th, td, article, +aside, canvas, details, embed, figure, +figcaption, footer, header, hgroup, menu, +nav, output, ruby, section, summary, time, +mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; + box-sizing: border-box; +} + +/* 2. HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} + +/* 3. 列表、表格、图片等 */ +ol, ul { + list-style: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +img { + max-width: 100%; + display: block; + border: 0; +} + +/* 4. 链接、按钮等 */ +a { + color: inherit; + text-decoration: none; +} +button, input, select, textarea { + font: inherit; + outline: none; + border: none; + background: none; + box-shadow: none; + appearance: none; +} + +/* 5. 滚动条美化(可选) */ +::-webkit-scrollbar { + width: 8px; + background: transparent; +} +::-webkit-scrollbar-thumb { + background: #e0e0e0; + border-radius: 4px; +} + +/* 6. 移动端/PC端兼容基础样式 */ html, body { height: 100%; overflow: hidden; // 禁止 body 滚动和回弹 + -webkit-tap-highlight-color: transparent; // 移动端点击无高亮 + -webkit-text-size-adjust: 100%; // 禁止iOS自动调整字体 + text-size-adjust: 100%; + /* iOS/安卓容差补充 */ + /* 解决 iOS 12+ 及部分安卓机型底部安全区适配 */ + padding-bottom: constant(safe-area-inset-bottom); + padding-bottom: env(safe-area-inset-bottom); + /* 解决 iOS 顶部刘海屏安全区 */ + padding-top: constant(safe-area-inset-top); + padding-top: env(safe-area-inset-top); } #root, .app-content { height: 100vh; overflow-y: auto; -webkit-overflow-scrolling: touch; // iOS 惯性滚动 + /* 安卓部分 WebView 兼容 */ + overscroll-behavior: contain; } body { - margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; background: #f5f5f5; + color: #222; + line-height: 1.5; + /* 安卓部分机型字体抗锯齿 */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* iOS input、textarea 默认圆角和阴影去除 */ +input, textarea { + border-radius: 0; + box-shadow: none; +} + +/* 安卓部分 WebView 点击延迟优化 */ +body, input, textarea, select, button { + touch-action: manipulation; } \ No newline at end of file From e2ad86ca458b092faa520a0aaf89fa90359871d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Thu, 17 Jul 2025 22:48:37 +0800 Subject: [PATCH 05/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E5=AE=9A=E7=A8=BF=E4=BA=86=EF=BC=8C=E5=BC=80=E5=A7=8B?= =?UTF-8?q?=E8=BF=81=E7=A7=BB=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/api/request.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/nkebao/src/api/request.ts b/nkebao/src/api/request.ts index bd7a086a6..ee008705b 100644 --- a/nkebao/src/api/request.ts +++ b/nkebao/src/api/request.ts @@ -1,7 +1,7 @@ import axios, { AxiosInstance, AxiosRequestConfig, Method, AxiosResponse } from 'axios'; import { Toast } from 'antd-mobile'; -const DEBOUNCE_GAP = 1000; +const DEFAULT_DEBOUNCE_GAP = 1000; const debounceMap = new Map(); const instance: AxiosInstance = axios.create({ @@ -32,11 +32,9 @@ instance.interceptors.response.use( // 分类处理 if (code === 401) { // 未登录或登录失效 - // 可跳转登录页或清除 token localStorage.removeItem('token'); } else if (code === 403) { // 无权限 - // 可做特殊处理 } else if (code === 500) { // 服务端异常 } @@ -49,16 +47,25 @@ instance.interceptors.response.use( } ); +/** + * @param url 接口地址 + * @param data 请求参数 + * @param method 请求方法 + * @param config axios 配置 + * @param debounceGap 防抖时间(毫秒),不传则用默认值 + */ export function request( url: string, data?: any, method: Method = 'GET', - config?: AxiosRequestConfig + config?: AxiosRequestConfig, + debounceGap?: number ): Promise { + const gap = typeof debounceGap === 'number' ? debounceGap : DEFAULT_DEBOUNCE_GAP; const key = `${method}_${url}_${JSON.stringify(data)}`; const now = Date.now(); const last = debounceMap.get(key) || 0; - if (now - last < DEBOUNCE_GAP) { + if (gap > 0 && now - last < gap) { Toast.show({ content: '请求过于频繁,请稍后再试', position: 'top' }); return Promise.reject('请求过于频繁,请稍后再试'); } From 23ac626a7ecf4c5ec0f3bbe39fe0d7921f6f678d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Thu, 17 Jul 2025 22:51:33 +0800 Subject: [PATCH 06/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E5=AD=98=E4=BA=86=EF=BC=8C=E5=BC=80=E5=A7=8B=E8=BF=81?= =?UTF-8?q?=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/迁移提示词.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 nkebao/迁移提示词.md diff --git a/nkebao/迁移提示词.md b/nkebao/迁移提示词.md new file mode 100644 index 000000000..f0e587b68 --- /dev/null +++ b/nkebao/迁移提示词.md @@ -0,0 +1,28 @@ +迁移提示词(Checklist) +当你将其他项目迁移到 nkebao 项目结构时,可参考以下提示词进行自适应改造: +1. 目录与文件结构 +按 nkebao 目录规范拆分:api、router/module、store/module、pages、styles 等 +公共样式、reset、rem 适配、兼容性样式统一放到 styles/global.scss +2. 路由迁移 +将原有路由配置拆分为 module 目录下的多个路由模块,导出数组 +路由配置中增加 auth/requiredRole 字段,适配权限拦截 +如有嵌套路由,按 useRoutes 结构组织 +3. 状态管理迁移 +将原有全局状态拆分为多个 zustand store,放到 store/module 下 +如需持久化,使用 createPersistStore 工具 +组件中统一用 useXxxStore 访问和修改状态 +4. 请求管理迁移 +所有接口请求统一用 src/api/request.ts 封装 +需要防抖的接口可自定义 debounceGap 参数 +错误提示统一用 Toast,必要时细分错误类型 +需要 token 的接口无需手动加 header,自动注入 +5. 组件与样式迁移 +组件按业务拆分到 pages/ 下 +样式统一用 scss,变量、reset、兼容性样式集中管理 +移动端适配用 rem,PC 端可用媒体查询补充 +6. 工程化与规范 +路径引用统一用 @/xxx +保持 tsconfig.json、vite.config.ts、.eslintrc.js、postcss.config.js 配置一致 +代码风格、格式化、类型声明与 nkebao 保持一致 +7. 其他 +如有全局 loading、消息、权限等需求,建议用 zustand/store 单独模块管理 \ No newline at end of file From 6f6cbadacd2cd0c6439c987774247c8221fb1c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Thu, 17 Jul 2025 23:19:08 +0800 Subject: [PATCH 07/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20css=20module=E8=BF=81=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/App.tsx | 4 -- nkebao/src/api/module/auth.ts | 22 -------- nkebao/src/com/Layout/Layout.tsx | 20 ++++++++ nkebao/src/com/Layout/layout.module.scss | 10 ++++ nkebao/src/pages/Home.tsx | 7 --- nkebao/src/pages/home/api.ts | 14 +++++ nkebao/src/pages/home/index.scss | 35 +++++++++++++ nkebao/src/pages/home/index.tsx | 65 ++++++++++++++++++++++++ nkebao/src/router/module/about.tsx | 10 ---- nkebao/src/router/module/home.tsx | 8 +-- 10 files changed, 145 insertions(+), 50 deletions(-) delete mode 100644 nkebao/src/api/module/auth.ts create mode 100644 nkebao/src/com/Layout/Layout.tsx create mode 100644 nkebao/src/com/Layout/layout.module.scss delete mode 100644 nkebao/src/pages/Home.tsx create mode 100644 nkebao/src/pages/home/api.ts create mode 100644 nkebao/src/pages/home/index.scss create mode 100644 nkebao/src/pages/home/index.tsx delete mode 100644 nkebao/src/router/module/about.tsx diff --git a/nkebao/src/App.tsx b/nkebao/src/App.tsx index 4437b1404..40792893b 100644 --- a/nkebao/src/App.tsx +++ b/nkebao/src/App.tsx @@ -7,10 +7,6 @@ function App() { return ( <> -
- - Antd-Mobile 按钮 -
); } diff --git a/nkebao/src/api/module/auth.ts b/nkebao/src/api/module/auth.ts deleted file mode 100644 index dba5ef64a..000000000 --- a/nkebao/src/api/module/auth.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { request } from '@/api/request'; - -export const list = (data?: any) => - request('/cw/companyUser/list', data, 'GET'); - -export const joinAudit = (data: any) => - request('/cw/companyUser/joinAudit', data, 'PUT'); - -export const listJoinAudit = (data?: any) => - request('/cw/companyUser/listJoinAudit', data, 'GET'); - -export const CwCompanyUserApplyAdd = (data: any) => - request('/cw/CwCompanyUserApply/add', data, 'PUT'); - -export const CwCompanyUserApplyAdminList = (data?: any) => - request('/cw/CwCompanyUserApply/adminList', data, 'GET'); - -export const CwCompanyUserApplyAudit = (data: any) => - request('/cw/CwCompanyUserApply/audit', data, 'POST'); - -export const CwCompanyUserApplyUserList = (data?: any) => - request('/cw/CwCompanyUserApply/userList', data, 'GET'); \ No newline at end of file diff --git a/nkebao/src/com/Layout/Layout.tsx b/nkebao/src/com/Layout/Layout.tsx new file mode 100644 index 000000000..405808952 --- /dev/null +++ b/nkebao/src/com/Layout/Layout.tsx @@ -0,0 +1,20 @@ +import React from "react"; +import "layout.module.scss"; +interface LayoutProps { + loading?: boolean; + children?: React.ReactNode; + header?: React.ReactNode; + footer?: React.ReactNode; +} + +const Layout: React.FC = ({ children, header, footer }) => { + return ( +
+ {header &&
{header}
} +
{children}
+ {footer &&
{footer}
} +
+ ); +}; + +export default Layout; diff --git a/nkebao/src/com/Layout/layout.module.scss b/nkebao/src/com/Layout/layout.module.scss new file mode 100644 index 000000000..e6e6cb0b4 --- /dev/null +++ b/nkebao/src/com/Layout/layout.module.scss @@ -0,0 +1,10 @@ +.container { + display: flex; + height: 100vh; + flex-direction: column; +} + +.container main { + flex: 1; + overflow: auto; +} \ No newline at end of file diff --git a/nkebao/src/pages/Home.tsx b/nkebao/src/pages/Home.tsx deleted file mode 100644 index 2f0a324ae..000000000 --- a/nkebao/src/pages/Home.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import React from "react"; - -const Home: React.FC = () => { - return

首页 Home

; -}; - -export default Home; diff --git a/nkebao/src/pages/home/api.ts b/nkebao/src/pages/home/api.ts new file mode 100644 index 000000000..535147142 --- /dev/null +++ b/nkebao/src/pages/home/api.ts @@ -0,0 +1,14 @@ +import request from '@/api/request'; + +// 设备统计 +export function getDeviceStats() { + return request('/v1/dashboard/device-stats', {}, 'GET'); +} + +// 微信号统计 +export function getWechatStats() { + return request('/v1/dashboard/wechat-stats', {}, 'GET'); +} + +// 你可以根据需要继续添加其他接口 +// 例如:场景获客统计、今日数据统计等 diff --git a/nkebao/src/pages/home/index.scss b/nkebao/src/pages/home/index.scss new file mode 100644 index 000000000..d8def7fd0 --- /dev/null +++ b/nkebao/src/pages/home/index.scss @@ -0,0 +1,35 @@ +.home-page { + padding: 16px; + background: #f5f5f5; + min-height: 100vh; + .home-title { + font-size: 22px; + font-weight: bold; + margin-bottom: 16px; + color: #1677ff; + text-align: center; + } + .home-cards { + display: flex; + gap: 16px; + flex-direction: column; + .home-card { + .home-card-value { + font-size: 28px; + font-weight: bold; + color: #1677ff; + margin-bottom: 8px; + } + .home-card-desc { + font-size: 14px; + color: #888; + } + } + } + .home-loading { + margin-top: 24px; + text-align: center; + color: #999; + font-size: 16px; + } +} \ No newline at end of file diff --git a/nkebao/src/pages/home/index.tsx b/nkebao/src/pages/home/index.tsx new file mode 100644 index 000000000..e96bd9349 --- /dev/null +++ b/nkebao/src/pages/home/index.tsx @@ -0,0 +1,65 @@ +import React, { useEffect, useState } from "react"; +import { Card, Toast } from "antd-mobile"; +import { getDeviceStats, getWechatStats } from "./api"; +import Layout from "@/com/Layout/Layout"; +import "./index.scss"; + +const Home: React.FC = () => { + const [stats, setStats] = useState({ + totalDevices: 0, + onlineDevices: 0, + totalWechatAccounts: 0, + onlineWechatAccounts: 0, + }); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchStats = async () => { + setLoading(true); + try { + const [device, wechat] = await Promise.all([ + getDeviceStats(), + getWechatStats(), + ]); + setStats({ + totalDevices: device.total || 0, + onlineDevices: device.online || 0, + totalWechatAccounts: wechat.total || 0, + onlineWechatAccounts: wechat.active || 0, + }); + } catch (err: any) { + Toast.show({ + content: err?.message || "数据加载失败", + position: "top", + }); + } finally { + setLoading(false); + } + }; + fetchStats(); + }, []); + + return ( + 首页 Home} + footer={
底部内容
} // 如有底部内容可加 + > +
+ +
{stats.totalDevices}
+
在线设备:{stats.onlineDevices}
+
+ +
{stats.totalWechatAccounts}
+
+ 在线微信号:{stats.onlineWechatAccounts} +
+
+
+ {loading &&
加载中...
} +
+ ); +}; + +export default Home; diff --git a/nkebao/src/router/module/about.tsx b/nkebao/src/router/module/about.tsx deleted file mode 100644 index d76d7e08e..000000000 --- a/nkebao/src/router/module/about.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import About from "@/pages/About"; - -const aboutRoutes = [ - { - path: "/about", - element: , - }, -]; - -export default aboutRoutes; diff --git a/nkebao/src/router/module/home.tsx b/nkebao/src/router/module/home.tsx index a4c5247a4..840b36dc4 100644 --- a/nkebao/src/router/module/home.tsx +++ b/nkebao/src/router/module/home.tsx @@ -1,4 +1,4 @@ -import Home from "@/pages/Home"; +import Home from "@/pages/home/index"; const homeRoutes = [ { @@ -6,12 +6,6 @@ const homeRoutes = [ element: , auth: false, // 不需要权限 }, - { - path: "/dashboard", - element: , - auth: true, // 需要登录 - requiredRole: "admin", // 需要 admin 角色 - }, ]; export default homeRoutes; From d8c59f43c2b2b426da9fcf173298410c692b7f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Fri, 18 Jul 2025 12:03:13 +0800 Subject: [PATCH 08/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E6=90=9E=E7=99=BB=E5=BD=95=E6=8B=A6=E6=88=AA=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/package.json | 2 + nkebao/src/App.tsx | 3 - .../src/{com => components}/Layout/Layout.tsx | 40 ++-- .../Layout/layout.module.scss | 0 nkebao/src/components/LineChart.tsx | 53 +++++ .../src/components/MeauMobile/MeauMoible.tsx | 81 ++++++++ nkebao/src/pages/home/api.ts | 21 +- nkebao/src/pages/home/index.module.scss | 70 +++++++ nkebao/src/pages/home/index.scss | 35 ---- nkebao/src/pages/home/index.tsx | 186 ++++++++++++------ nkebao/src/pages/login/login.module.scss | 0 nkebao/src/pages/login/login.tsx | 74 +++++++ nkebao/src/pages/mine/api.ts | 14 ++ nkebao/src/pages/mine/index.module.scss | 71 +++++++ nkebao/src/pages/mine/index.tsx | 132 +++++++++++++ nkebao/src/pages/scene/api.ts | 14 ++ nkebao/src/pages/scene/index.module.scss | 71 +++++++ nkebao/src/pages/scene/index.tsx | 132 +++++++++++++ nkebao/src/pages/work/api.ts | 14 ++ nkebao/src/pages/work/index.module.scss | 71 +++++++ nkebao/src/pages/work/index.tsx | 132 +++++++++++++ nkebao/src/router/module/home.tsx | 28 +-- package.json | 5 + yarn.lock | 23 +++ 24 files changed, 1136 insertions(+), 136 deletions(-) rename nkebao/src/{com => components}/Layout/Layout.tsx (73%) rename nkebao/src/{com => components}/Layout/layout.module.scss (100%) create mode 100644 nkebao/src/components/LineChart.tsx create mode 100644 nkebao/src/components/MeauMobile/MeauMoible.tsx create mode 100644 nkebao/src/pages/home/index.module.scss delete mode 100644 nkebao/src/pages/home/index.scss create mode 100644 nkebao/src/pages/login/login.module.scss create mode 100644 nkebao/src/pages/login/login.tsx create mode 100644 nkebao/src/pages/mine/api.ts create mode 100644 nkebao/src/pages/mine/index.module.scss create mode 100644 nkebao/src/pages/mine/index.tsx create mode 100644 nkebao/src/pages/scene/api.ts create mode 100644 nkebao/src/pages/scene/index.module.scss create mode 100644 nkebao/src/pages/scene/index.tsx create mode 100644 nkebao/src/pages/work/api.ts create mode 100644 nkebao/src/pages/work/index.module.scss create mode 100644 nkebao/src/pages/work/index.tsx create mode 100644 package.json create mode 100644 yarn.lock diff --git a/nkebao/package.json b/nkebao/package.json index f9f4ac6e2..8d148544e 100644 --- a/nkebao/package.json +++ b/nkebao/package.json @@ -6,6 +6,8 @@ "antd": "^5.13.1", "antd-mobile": "^5.39.1", "axios": "^1.6.7", + "echarts": "^5.6.0", + "echarts-for-react": "^3.0.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.20.0", diff --git a/nkebao/src/App.tsx b/nkebao/src/App.tsx index 40792893b..9138ddb54 100644 --- a/nkebao/src/App.tsx +++ b/nkebao/src/App.tsx @@ -1,8 +1,5 @@ import React from "react"; import AppRouter from "@/router"; -import { Button } from "antd"; -import { Button as MobileButton } from "antd-mobile"; - function App() { return ( <> diff --git a/nkebao/src/com/Layout/Layout.tsx b/nkebao/src/components/Layout/Layout.tsx similarity index 73% rename from nkebao/src/com/Layout/Layout.tsx rename to nkebao/src/components/Layout/Layout.tsx index 405808952..2cef351d8 100644 --- a/nkebao/src/com/Layout/Layout.tsx +++ b/nkebao/src/components/Layout/Layout.tsx @@ -1,20 +1,20 @@ -import React from "react"; -import "layout.module.scss"; -interface LayoutProps { - loading?: boolean; - children?: React.ReactNode; - header?: React.ReactNode; - footer?: React.ReactNode; -} - -const Layout: React.FC = ({ children, header, footer }) => { - return ( -
- {header &&
{header}
} -
{children}
- {footer &&
{footer}
} -
- ); -}; - -export default Layout; +import React from "react"; +import styles from "./layout.module.scss"; +interface LayoutProps { + loading?: boolean; + children?: React.ReactNode; + header?: React.ReactNode; + footer?: React.ReactNode; +} + +const Layout: React.FC = ({ children, header, footer }) => { + return ( +
+ {header &&
{header}
} +
{children}
+ {footer &&
{footer}
} +
+ ); +}; + +export default Layout; diff --git a/nkebao/src/com/Layout/layout.module.scss b/nkebao/src/components/Layout/layout.module.scss similarity index 100% rename from nkebao/src/com/Layout/layout.module.scss rename to nkebao/src/components/Layout/layout.module.scss diff --git a/nkebao/src/components/LineChart.tsx b/nkebao/src/components/LineChart.tsx new file mode 100644 index 000000000..7bd9db0df --- /dev/null +++ b/nkebao/src/components/LineChart.tsx @@ -0,0 +1,53 @@ +import React from "react"; +import ReactECharts from "echarts-for-react"; + +interface LineChartProps { + title?: string; + xData: string[]; + yData: number[]; + height?: number | string; +} + +const LineChart: React.FC = ({ + title = "", + xData, + yData, + height = 200, +}) => { + const option = { + title: { + text: title, + left: "center", + textStyle: { fontSize: 16 }, + }, + tooltip: { trigger: "axis" }, + xAxis: { + type: "category", + data: xData, + boundaryGap: false, + }, + yAxis: { + type: "value", + boundaryGap: ["10%", "10%"], // 上下留白 + min: (value: any) => value.min - 10, // 下方多留一点空间 + max: (value: any) => value.max + 10, // 上方多留一点空间 + minInterval: 1, + axisLabel: { margin: 12 }, + }, + series: [ + { + data: yData, + type: "line", + smooth: true, + symbol: "circle", + lineStyle: { color: "#1677ff" }, + itemStyle: { color: "#1677ff" }, + }, + ], + grid: { left: 40, right: 24, top: 40, bottom: 32 }, + }; + + return ; +}; + +export default LineChart; diff --git a/nkebao/src/components/MeauMobile/MeauMoible.tsx b/nkebao/src/components/MeauMobile/MeauMoible.tsx new file mode 100644 index 000000000..4fe432f81 --- /dev/null +++ b/nkebao/src/components/MeauMobile/MeauMoible.tsx @@ -0,0 +1,81 @@ +import React, { useState, useEffect } from "react"; +import { TabBar } from "antd-mobile"; +import { + AppOutline, + ShopbagOutline, + PieOutline, + UserOutline, +} from "antd-mobile-icons"; +import { useLocation, useNavigate } from "react-router-dom"; + +const tabs = [ + { + key: "home", + title: "首页", + icon: , + path: "/", + }, + { + key: "scene", + title: "场景获客", + icon: , + path: "/scene", + }, + { + key: "work", + title: "工作台", + icon: , + path: "/work", + }, + { + key: "mine", + title: "我的", + icon: , + path: "/mine", + }, +]; + +// 需要展示菜单的路由白名单(可根据实际业务调整) +const menuPaths = ["/", "/scene", "/work", "/mine"]; + +const MeauMobile: React.FC = () => { + const location = useLocation(); + const navigate = useNavigate(); + const [activeKey, setActiveKey] = useState("home"); + + // 根据当前路由自动设置 activeKey,支持嵌套路由 + useEffect(() => { + const found = tabs.find((tab) => + tab.path === "/" + ? location.pathname === "/" + : location.pathname.startsWith(tab.path) + ); + if (found) setActiveKey(found.key); + }, [location.pathname]); + + // 判断当前路由是否需要展示菜单 + const showMenu = menuPaths.some((path) => + path === "/" + ? location.pathname === "/" + : location.pathname.startsWith(path) + ); + if (!showMenu) return null; + + return ( + { + setActiveKey(key); + const tab = tabs.find((t) => t.key === key); + if (tab && tab.path) navigate(tab.path); + }} + > + {tabs.map((item) => ( + + ))} + + ); +}; + +export default MeauMobile; diff --git a/nkebao/src/pages/home/api.ts b/nkebao/src/pages/home/api.ts index 535147142..25016d2d3 100644 --- a/nkebao/src/pages/home/api.ts +++ b/nkebao/src/pages/home/api.ts @@ -10,5 +10,22 @@ export function getWechatStats() { return request('/v1/dashboard/wechat-stats', {}, 'GET'); } -// 你可以根据需要继续添加其他接口 -// 例如:场景获客统计、今日数据统计等 +// 今日数据统计 +export function getTodayStats() { + return request('/v1/dashboard/today-stats', {}, 'GET'); +} + +// 首页仪表盘总览 +export function getDashboard() { + return request('/v1/dashboard', {}, 'GET'); +} + +// 获客场景统计 +export function getPlanStats(params:any) { + return request('/v1/dashboard/plan-stats', params, 'GET'); +} + +// 近七天统计 +export function getSevenDayStats() { + return request('/v1/dashboard/sevenDay-stats', {}, 'GET'); +} diff --git a/nkebao/src/pages/home/index.module.scss b/nkebao/src/pages/home/index.module.scss new file mode 100644 index 000000000..e2c392aa5 --- /dev/null +++ b/nkebao/src/pages/home/index.module.scss @@ -0,0 +1,70 @@ +.home-page { + padding: 12px; + background: #f5f5f5; +} + +.home-cards { + display: flex; + gap: 12px; + margin-bottom: 12px; + > :global(.adm-card) { + flex: 1; + } +} + +.home-section { + margin-bottom: 12px; + background: #fff; + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0,0,0,0.03); +} + +.home-section-title { + font-size: 16px; + font-weight: 600; + margin-bottom: 15px; +} + +.home-scene-stats { + display: flex; + justify-content: space-between; + margin: 0 8px 8px 8px; +} +.home-scene-item { + flex: 1; + text-align: center; +} +.home-scene-icon { + margin: 0 auto 4px auto; +} +.home-scene-value { + font-size: 18px; + font-weight: bold; + color: #1677ff; +} +.home-scene-label { + font-size: 12px; + color: #888; +} + +.home-today-item { + text-align: center; + padding: 8px 0; + background: #f7f8fa; + border-radius: 8px; +} +.home-today-value { + font-size: 18px; + font-weight: bold; + color: #1677ff; +} +.home-today-label { + font-size: 12px; + color: #888; +} + +.home-chart { + margin-top: 8px; + width: 100%; + min-height: 100px; +} \ No newline at end of file diff --git a/nkebao/src/pages/home/index.scss b/nkebao/src/pages/home/index.scss deleted file mode 100644 index d8def7fd0..000000000 --- a/nkebao/src/pages/home/index.scss +++ /dev/null @@ -1,35 +0,0 @@ -.home-page { - padding: 16px; - background: #f5f5f5; - min-height: 100vh; - .home-title { - font-size: 22px; - font-weight: bold; - margin-bottom: 16px; - color: #1677ff; - text-align: center; - } - .home-cards { - display: flex; - gap: 16px; - flex-direction: column; - .home-card { - .home-card-value { - font-size: 28px; - font-weight: bold; - color: #1677ff; - margin-bottom: 8px; - } - .home-card-desc { - font-size: 14px; - color: #888; - } - } - } - .home-loading { - margin-top: 24px; - text-align: center; - color: #999; - font-size: 16px; - } -} \ No newline at end of file diff --git a/nkebao/src/pages/home/index.tsx b/nkebao/src/pages/home/index.tsx index e96bd9349..f63752b62 100644 --- a/nkebao/src/pages/home/index.tsx +++ b/nkebao/src/pages/home/index.tsx @@ -1,65 +1,121 @@ -import React, { useEffect, useState } from "react"; -import { Card, Toast } from "antd-mobile"; -import { getDeviceStats, getWechatStats } from "./api"; -import Layout from "@/com/Layout/Layout"; -import "./index.scss"; - -const Home: React.FC = () => { - const [stats, setStats] = useState({ - totalDevices: 0, - onlineDevices: 0, - totalWechatAccounts: 0, - onlineWechatAccounts: 0, - }); - const [loading, setLoading] = useState(true); - - useEffect(() => { - const fetchStats = async () => { - setLoading(true); - try { - const [device, wechat] = await Promise.all([ - getDeviceStats(), - getWechatStats(), - ]); - setStats({ - totalDevices: device.total || 0, - onlineDevices: device.online || 0, - totalWechatAccounts: wechat.total || 0, - onlineWechatAccounts: wechat.active || 0, - }); - } catch (err: any) { - Toast.show({ - content: err?.message || "数据加载失败", - position: "top", - }); - } finally { - setLoading(false); - } - }; - fetchStats(); - }, []); - - return ( - 首页 Home} - footer={
底部内容
} // 如有底部内容可加 - > -
- -
{stats.totalDevices}
-
在线设备:{stats.onlineDevices}
-
- -
{stats.totalWechatAccounts}
-
- 在线微信号:{stats.onlineWechatAccounts} -
-
-
- {loading &&
加载中...
} -
- ); -}; - -export default Home; +import React, { useEffect, useState } from "react"; +import { Card, NavBar, TabBar, Grid } from "antd-mobile"; +import { + AppOutline, + UserOutline, + PieOutline, // 替换 BarChartOutline + ShopbagOutline, +} from "antd-mobile-icons"; +import MeauMobile from "@/components/MeauMobile/MeauMoible"; +import Layout from "@/components/Layout/Layout"; +import style from "./index.module.scss"; +import LineChart from "@/components/LineChart"; +import { getPlanStats } from "./api"; + +const sceneStats = [ + { + label: "公众号获客", + value: 234, + icon: , + }, + { + label: "海报获客", + value: 167, + icon: , + }, + { + label: "抖音获客", + value: 156, + icon: , + }, + { + label: "小红书获客", + value: 89, + icon: , + }, +]; + +const todayStats = [ + { label: "朋友圈同步", value: 12 }, + { label: "群发任务", value: 8 }, + { label: "获客转化", value: "85%" }, + { label: "系统活跃度", value: "98%" }, +]; + +const Home: React.FC = () => { + useEffect(() => { + getPlanStats({ num: 4 }).then((res: any) => { + console.log(res); + }); + }, []); + + return ( + + 存客宝 + + } + footer={} + > +
+ {/* 统计卡片 */} +
+ +
设备数量
+
0
+
+ +
微信号数量
+
0
+
+ +
在线微信号
+
0
+
+
+ + {/* 场景获客统计 */} + +
场景获客统计
+
+ {sceneStats.map((item) => ( +
+
{item.icon}
+
{item.value}
+
{item.label}
+
+ ))} +
+
+ + {/* 今日数据 */} + +
今日数据
+ + {todayStats.map((item) => ( + +
+
{item.value}
+
{item.label}
+
+
+ ))} +
+
+ + {/* 每日获客趋势(静态图表占位) */} + +
每日获客趋势
+
+ +
+
+
+
+ ); +}; +export default Home; diff --git a/nkebao/src/pages/login/login.module.scss b/nkebao/src/pages/login/login.module.scss new file mode 100644 index 000000000..e69de29bb diff --git a/nkebao/src/pages/login/login.tsx b/nkebao/src/pages/login/login.tsx new file mode 100644 index 000000000..a9a930682 --- /dev/null +++ b/nkebao/src/pages/login/login.tsx @@ -0,0 +1,74 @@ +import React, { useState } from "react"; +import { Form, Input, Button, Toast } from "antd-mobile"; +import { EyeInvisibleOutline, EyeOutline } from "antd-mobile-icons"; +import request from "@/api/request"; +import style from "./login.module.scss"; + +const Login: React.FC = () => { + const [loading, setLoading] = useState(false); + const [visible, setVisible] = useState(false); + + const onFinish = async (values: any) => { + setLoading(true); + try { + // 假设接口为 /api/login,实际请根据后端接口调整 + const res = await request("/api/login", values, "POST"); + // 登录成功后保存 token + localStorage.setItem("token", res.token); + Toast.show({ content: "登录成功", position: "top" }); + // 跳转首页或其他页面 + window.location.href = "/"; + } catch (err: any) { + Toast.show({ content: err?.message || "登录失败", position: "top" }); + } finally { + setLoading(false); + } + }; + + return ( +
+
账号登录
+
+ 登录 + + } + > + + + + + setVisible((v) => !v)}> + {visible ? : } +
+ } + /> + + + + ); +}; + +export default Login; diff --git a/nkebao/src/pages/mine/api.ts b/nkebao/src/pages/mine/api.ts new file mode 100644 index 000000000..535147142 --- /dev/null +++ b/nkebao/src/pages/mine/api.ts @@ -0,0 +1,14 @@ +import request from '@/api/request'; + +// 设备统计 +export function getDeviceStats() { + return request('/v1/dashboard/device-stats', {}, 'GET'); +} + +// 微信号统计 +export function getWechatStats() { + return request('/v1/dashboard/wechat-stats', {}, 'GET'); +} + +// 你可以根据需要继续添加其他接口 +// 例如:场景获客统计、今日数据统计等 diff --git a/nkebao/src/pages/mine/index.module.scss b/nkebao/src/pages/mine/index.module.scss new file mode 100644 index 000000000..53304f695 --- /dev/null +++ b/nkebao/src/pages/mine/index.module.scss @@ -0,0 +1,71 @@ +.home-page { + padding: 12px; + background: #f5f5f5; +} + +.home-cards { + display: flex; + gap: 12px; + margin-bottom: 12px; + > :global(.adm-card) { + flex: 1; + } +} + +.home-section { + margin-bottom: 12px; + background: #fff; + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0,0,0,0.03); +} + +.home-section-title { + font-size: 16px; + font-weight: 600; + margin-bottom: 15px; + padding-left: 16px; +} + +.home-scene-stats { + display: flex; + justify-content: space-between; + margin: 0 8px 8px 8px; +} +.home-scene-item { + flex: 1; + text-align: center; +} +.home-scene-icon { + margin: 0 auto 4px auto; +} +.home-scene-value { + font-size: 18px; + font-weight: bold; + color: #1677ff; +} +.home-scene-label { + font-size: 12px; + color: #888; +} + +.home-today-item { + text-align: center; + padding: 8px 0; + background: #f7f8fa; + border-radius: 8px; +} +.home-today-value { + font-size: 18px; + font-weight: bold; + color: #1677ff; +} +.home-today-label { + font-size: 12px; + color: #888; +} + +.home-chart { + margin-top: 8px; + width: 100%; + min-height: 100px; +} \ No newline at end of file diff --git a/nkebao/src/pages/mine/index.tsx b/nkebao/src/pages/mine/index.tsx new file mode 100644 index 000000000..dc673c00b --- /dev/null +++ b/nkebao/src/pages/mine/index.tsx @@ -0,0 +1,132 @@ +import React, { useState } from "react"; +import { Card, NavBar, TabBar, Grid } from "antd-mobile"; +import { + AppOutline, + UserOutline, + PieOutline, // 替换 BarChartOutline + ShopbagOutline, + BellOutline, +} from "antd-mobile-icons"; +import MeauMobile from "@/components/MeauMobile/MeauMoible"; +import Layout from "@/components/Layout/Layout"; +import style from "./index.module.scss"; +const sceneStats = [ + { + label: "公众号获客", + value: 234, + icon: , + }, + { + label: "海报获客", + value: 167, + icon: , + }, + { + label: "抖音获客", + value: 156, + icon: , + }, + { + label: "小红书获客", + value: 89, + icon: , + }, +]; + +const todayStats = [ + { label: "朋友圈同步", value: 12 }, + { label: "群发任务", value: 8 }, + { label: "获客转化", value: "85%" }, + { label: "系统活跃度", value: "98%" }, +]; + +const Home: React.FC = () => { + return ( + + 存客宝 + + } + footer={} + > +
+ {/* 统计卡片 */} +
+ +
设备数量
+
0
+
+ +
微信号数量
+
0
+
+ +
在线微信号
+
0
+
+
+ + {/* 场景获客统计 */} + +
场景获客统计
+
+ {sceneStats.map((item) => ( +
+
{item.icon}
+
{item.value}
+
{item.label}
+
+ ))} +
+
+ + {/* 今日数据 */} + +
今日数据
+ + {todayStats.map((item) => ( + +
+
{item.value}
+
{item.label}
+
+
+ ))} +
+
+ + {/* 每日获客趋势(静态图表占位) */} + +
每日获客趋势
+
+ + + {/* x轴文字 */} + {["周一", "周二", "周三", "周四", "周五", "周六", "周日"].map( + (d, i) => ( + + {d} + + ) + )} + +
+
+
+
+ ); +}; + +export default Home; diff --git a/nkebao/src/pages/scene/api.ts b/nkebao/src/pages/scene/api.ts new file mode 100644 index 000000000..535147142 --- /dev/null +++ b/nkebao/src/pages/scene/api.ts @@ -0,0 +1,14 @@ +import request from '@/api/request'; + +// 设备统计 +export function getDeviceStats() { + return request('/v1/dashboard/device-stats', {}, 'GET'); +} + +// 微信号统计 +export function getWechatStats() { + return request('/v1/dashboard/wechat-stats', {}, 'GET'); +} + +// 你可以根据需要继续添加其他接口 +// 例如:场景获客统计、今日数据统计等 diff --git a/nkebao/src/pages/scene/index.module.scss b/nkebao/src/pages/scene/index.module.scss new file mode 100644 index 000000000..53304f695 --- /dev/null +++ b/nkebao/src/pages/scene/index.module.scss @@ -0,0 +1,71 @@ +.home-page { + padding: 12px; + background: #f5f5f5; +} + +.home-cards { + display: flex; + gap: 12px; + margin-bottom: 12px; + > :global(.adm-card) { + flex: 1; + } +} + +.home-section { + margin-bottom: 12px; + background: #fff; + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0,0,0,0.03); +} + +.home-section-title { + font-size: 16px; + font-weight: 600; + margin-bottom: 15px; + padding-left: 16px; +} + +.home-scene-stats { + display: flex; + justify-content: space-between; + margin: 0 8px 8px 8px; +} +.home-scene-item { + flex: 1; + text-align: center; +} +.home-scene-icon { + margin: 0 auto 4px auto; +} +.home-scene-value { + font-size: 18px; + font-weight: bold; + color: #1677ff; +} +.home-scene-label { + font-size: 12px; + color: #888; +} + +.home-today-item { + text-align: center; + padding: 8px 0; + background: #f7f8fa; + border-radius: 8px; +} +.home-today-value { + font-size: 18px; + font-weight: bold; + color: #1677ff; +} +.home-today-label { + font-size: 12px; + color: #888; +} + +.home-chart { + margin-top: 8px; + width: 100%; + min-height: 100px; +} \ No newline at end of file diff --git a/nkebao/src/pages/scene/index.tsx b/nkebao/src/pages/scene/index.tsx new file mode 100644 index 000000000..dc673c00b --- /dev/null +++ b/nkebao/src/pages/scene/index.tsx @@ -0,0 +1,132 @@ +import React, { useState } from "react"; +import { Card, NavBar, TabBar, Grid } from "antd-mobile"; +import { + AppOutline, + UserOutline, + PieOutline, // 替换 BarChartOutline + ShopbagOutline, + BellOutline, +} from "antd-mobile-icons"; +import MeauMobile from "@/components/MeauMobile/MeauMoible"; +import Layout from "@/components/Layout/Layout"; +import style from "./index.module.scss"; +const sceneStats = [ + { + label: "公众号获客", + value: 234, + icon: , + }, + { + label: "海报获客", + value: 167, + icon: , + }, + { + label: "抖音获客", + value: 156, + icon: , + }, + { + label: "小红书获客", + value: 89, + icon: , + }, +]; + +const todayStats = [ + { label: "朋友圈同步", value: 12 }, + { label: "群发任务", value: 8 }, + { label: "获客转化", value: "85%" }, + { label: "系统活跃度", value: "98%" }, +]; + +const Home: React.FC = () => { + return ( + + 存客宝 + + } + footer={} + > +
+ {/* 统计卡片 */} +
+ +
设备数量
+
0
+
+ +
微信号数量
+
0
+
+ +
在线微信号
+
0
+
+
+ + {/* 场景获客统计 */} + +
场景获客统计
+
+ {sceneStats.map((item) => ( +
+
{item.icon}
+
{item.value}
+
{item.label}
+
+ ))} +
+
+ + {/* 今日数据 */} + +
今日数据
+ + {todayStats.map((item) => ( + +
+
{item.value}
+
{item.label}
+
+
+ ))} +
+
+ + {/* 每日获客趋势(静态图表占位) */} + +
每日获客趋势
+
+ + + {/* x轴文字 */} + {["周一", "周二", "周三", "周四", "周五", "周六", "周日"].map( + (d, i) => ( + + {d} + + ) + )} + +
+
+
+
+ ); +}; + +export default Home; diff --git a/nkebao/src/pages/work/api.ts b/nkebao/src/pages/work/api.ts new file mode 100644 index 000000000..535147142 --- /dev/null +++ b/nkebao/src/pages/work/api.ts @@ -0,0 +1,14 @@ +import request from '@/api/request'; + +// 设备统计 +export function getDeviceStats() { + return request('/v1/dashboard/device-stats', {}, 'GET'); +} + +// 微信号统计 +export function getWechatStats() { + return request('/v1/dashboard/wechat-stats', {}, 'GET'); +} + +// 你可以根据需要继续添加其他接口 +// 例如:场景获客统计、今日数据统计等 diff --git a/nkebao/src/pages/work/index.module.scss b/nkebao/src/pages/work/index.module.scss new file mode 100644 index 000000000..53304f695 --- /dev/null +++ b/nkebao/src/pages/work/index.module.scss @@ -0,0 +1,71 @@ +.home-page { + padding: 12px; + background: #f5f5f5; +} + +.home-cards { + display: flex; + gap: 12px; + margin-bottom: 12px; + > :global(.adm-card) { + flex: 1; + } +} + +.home-section { + margin-bottom: 12px; + background: #fff; + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0,0,0,0.03); +} + +.home-section-title { + font-size: 16px; + font-weight: 600; + margin-bottom: 15px; + padding-left: 16px; +} + +.home-scene-stats { + display: flex; + justify-content: space-between; + margin: 0 8px 8px 8px; +} +.home-scene-item { + flex: 1; + text-align: center; +} +.home-scene-icon { + margin: 0 auto 4px auto; +} +.home-scene-value { + font-size: 18px; + font-weight: bold; + color: #1677ff; +} +.home-scene-label { + font-size: 12px; + color: #888; +} + +.home-today-item { + text-align: center; + padding: 8px 0; + background: #f7f8fa; + border-radius: 8px; +} +.home-today-value { + font-size: 18px; + font-weight: bold; + color: #1677ff; +} +.home-today-label { + font-size: 12px; + color: #888; +} + +.home-chart { + margin-top: 8px; + width: 100%; + min-height: 100px; +} \ No newline at end of file diff --git a/nkebao/src/pages/work/index.tsx b/nkebao/src/pages/work/index.tsx new file mode 100644 index 000000000..dc673c00b --- /dev/null +++ b/nkebao/src/pages/work/index.tsx @@ -0,0 +1,132 @@ +import React, { useState } from "react"; +import { Card, NavBar, TabBar, Grid } from "antd-mobile"; +import { + AppOutline, + UserOutline, + PieOutline, // 替换 BarChartOutline + ShopbagOutline, + BellOutline, +} from "antd-mobile-icons"; +import MeauMobile from "@/components/MeauMobile/MeauMoible"; +import Layout from "@/components/Layout/Layout"; +import style from "./index.module.scss"; +const sceneStats = [ + { + label: "公众号获客", + value: 234, + icon: , + }, + { + label: "海报获客", + value: 167, + icon: , + }, + { + label: "抖音获客", + value: 156, + icon: , + }, + { + label: "小红书获客", + value: 89, + icon: , + }, +]; + +const todayStats = [ + { label: "朋友圈同步", value: 12 }, + { label: "群发任务", value: 8 }, + { label: "获客转化", value: "85%" }, + { label: "系统活跃度", value: "98%" }, +]; + +const Home: React.FC = () => { + return ( + + 存客宝 + + } + footer={} + > +
+ {/* 统计卡片 */} +
+ +
设备数量
+
0
+
+ +
微信号数量
+
0
+
+ +
在线微信号
+
0
+
+
+ + {/* 场景获客统计 */} + +
场景获客统计
+
+ {sceneStats.map((item) => ( +
+
{item.icon}
+
{item.value}
+
{item.label}
+
+ ))} +
+
+ + {/* 今日数据 */} + +
今日数据
+ + {todayStats.map((item) => ( + +
+
{item.value}
+
{item.label}
+
+
+ ))} +
+
+ + {/* 每日获客趋势(静态图表占位) */} + +
每日获客趋势
+
+ + + {/* x轴文字 */} + {["周一", "周二", "周三", "周四", "周五", "周六", "周日"].map( + (d, i) => ( + + {d} + + ) + )} + +
+
+
+
+ ); +}; + +export default Home; diff --git a/nkebao/src/router/module/home.tsx b/nkebao/src/router/module/home.tsx index 840b36dc4..17a08aa0d 100644 --- a/nkebao/src/router/module/home.tsx +++ b/nkebao/src/router/module/home.tsx @@ -1,11 +1,17 @@ -import Home from "@/pages/home/index"; - -const homeRoutes = [ - { - path: "/", - element: , - auth: false, // 不需要权限 - }, -]; - -export default homeRoutes; +import Home from "@/pages/home/index"; +import Login from "@/pages/login/login"; + +const homeRoutes = [ + { + path: "/", + element: , + auth: false, // 不需要权限 + }, + { + path: "/login", + element: , + auth: false, // 不需要权限 + }, +]; + +export default homeRoutes; diff --git a/package.json b/package.json new file mode 100644 index 000000000..f73eb4d67 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "echarts": "^5.6.0" + } +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 000000000..aab17ca26 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,23 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +echarts@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/echarts/-/echarts-5.6.0.tgz#2377874dca9fb50f104051c3553544752da3c9d6" + integrity sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA== + dependencies: + tslib "2.3.0" + zrender "5.6.1" + +tslib@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" + integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== + +zrender@5.6.1: + version "5.6.1" + resolved "https://registry.yarnpkg.com/zrender/-/zrender-5.6.1.tgz#e08d57ecf4acac708c4fcb7481eb201df7f10a6b" + integrity sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag== + dependencies: + tslib "2.3.0" From c611cb9e0c52e81b3e3fab72ab43ff5f5a1ae952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Fri, 18 Jul 2025 14:37:27 +0800 Subject: [PATCH 09/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E6=8E=A7=E5=88=B6=E5=8F=B0=E5=91=8A=E8=AD=A6=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/router/index.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nkebao/src/router/index.tsx b/nkebao/src/router/index.tsx index 2c7c0311e..87779623b 100644 --- a/nkebao/src/router/index.tsx +++ b/nkebao/src/router/index.tsx @@ -36,7 +36,12 @@ const routes = allRoutes.map(wrapWithPermission); const AppRoutes = () => useRoutes(routes); const AppRouter: React.FC = () => ( - + ); From a336d1173b1640aa21cc5ade976f515a2e66d45c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Fri, 18 Jul 2025 15:56:12 +0800 Subject: [PATCH 10/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E7=99=BB=E5=BD=95=E9=A1=B5=E9=9D=A2=E6=96=B0=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/api/auth.ts | 54 +++ nkebao/src/api/request.ts | 23 +- nkebao/src/pages/login/api.ts | 53 +++ nkebao/src/pages/login/login.module.scss | 462 +++++++++++++++++++++++ nkebao/src/pages/login/login.tsx | 396 ++++++++++++++++--- nkebao/src/router/module/home.tsx | 17 - nkebao/src/router/module/index.tsx | 35 ++ nkebao/src/router/permissionRoute.tsx | 44 ++- nkebao/src/store/module/user.ts | 37 +- 9 files changed, 1018 insertions(+), 103 deletions(-) create mode 100644 nkebao/src/api/auth.ts create mode 100644 nkebao/src/pages/login/api.ts delete mode 100644 nkebao/src/router/module/home.tsx create mode 100644 nkebao/src/router/module/index.tsx diff --git a/nkebao/src/api/auth.ts b/nkebao/src/api/auth.ts new file mode 100644 index 000000000..36b1a82ec --- /dev/null +++ b/nkebao/src/api/auth.ts @@ -0,0 +1,54 @@ +import request from './request'; + +export interface LoginParams { + phone: string; + password?: string; + verificationCode?: string; +} + +export interface LoginResponse { + code: number; + msg: string; + data: { + token: string; + token_expired: string; + member: { + id: string; + name: string; + phone: string; + s2_accountId: string; + avatar?: string; + email?: string; + }; + }; +} + +export interface SendCodeResponse { + code: number; + msg: string; +} + +// 密码登录 +export function loginWithPassword(phone: string, password: string) { + return request('/v1/auth/login', { phone, password }, 'POST'); +} + +// 验证码登录 +export function loginWithCode(phone: string, verificationCode: string) { + return request('/v1/auth/login-code', { phone, verificationCode }, 'POST'); +} + +// 发送验证码 +export function sendVerificationCode(phone: string) { + return request('/v1/auth/send-code', { phone }, 'POST'); +} + +// 退出登录 +export function logout() { + return request('/v1/auth/logout', {}, 'POST'); +} + +// 获取用户信息 +export function getUserInfo() { + return request('/v1/auth/user-info', {}, 'GET'); +} \ No newline at end of file diff --git a/nkebao/src/api/request.ts b/nkebao/src/api/request.ts index ee008705b..2c5372bf9 100644 --- a/nkebao/src/api/request.ts +++ b/nkebao/src/api/request.ts @@ -27,33 +27,24 @@ instance.interceptors.response.use( if (code === 200 || success) { return res.data.data ?? res.data; } - // 业务错误统一提示 Toast.show({ content: msg || '接口错误', position: 'top' }); - // 分类处理 if (code === 401) { - // 未登录或登录失效 localStorage.removeItem('token'); - } else if (code === 403) { - // 无权限 - } else if (code === 500) { - // 服务端异常 + const currentPath = window.location.pathname + window.location.search; + if (currentPath === '/login') { + window.location.href = '/login'; + } else { + window.location.href = `/login?redirect=${encodeURIComponent(currentPath)}`; + } } return Promise.reject(msg || '接口错误'); }, err => { - // 网络错误、超时等 Toast.show({ content: err.message || '网络异常', position: 'top' }); return Promise.reject(err); } ); -/** - * @param url 接口地址 - * @param data 请求参数 - * @param method 请求方法 - * @param config axios 配置 - * @param debounceGap 防抖时间(毫秒),不传则用默认值 - */ export function request( url: string, data?: any, @@ -84,4 +75,4 @@ export function request( return instance(axiosConfig); } -export default request; \ No newline at end of file +export default request; diff --git a/nkebao/src/pages/login/api.ts b/nkebao/src/pages/login/api.ts new file mode 100644 index 000000000..1aba513ee --- /dev/null +++ b/nkebao/src/pages/login/api.ts @@ -0,0 +1,53 @@ +import request from '@/api/request'; +export interface LoginParams { + phone: string; + password?: string; + verificationCode?: string; +} + +export interface LoginResponse { + code: number; + msg: string; + data: { + token: string; + token_expired: string; + member: { + id: string; + name: string; + phone: string; + s2_accountId: string; + avatar?: string; + email?: string; + }; + }; +} + +export interface SendCodeResponse { + code: number; + msg: string; +} + +// 密码登录 +export function loginWithPassword(params:any) { + return request('/v1/auth/login', params, 'POST'); +} + +// 验证码登录 +export function loginWithCode(params:any) { + return request('/v1/auth/login-code', params, 'POST'); +} + +// 发送验证码 +export function sendVerificationCode(params:any) { + return request('/v1/auth/send-code',params, 'POST'); +} + +// 退出登录 +export function logout() { + return request('/v1/auth/logout', {}, 'POST'); +} + +// 获取用户信息 +export function getUserInfo() { + return request('/v1/auth/user-info', {}, 'GET'); +} \ No newline at end of file diff --git a/nkebao/src/pages/login/login.module.scss b/nkebao/src/pages/login/login.module.scss index e69de29bb..0f9408ea0 100644 --- a/nkebao/src/pages/login/login.module.scss +++ b/nkebao/src/pages/login/login.module.scss @@ -0,0 +1,462 @@ +.login-page { + min-height: 100vh; + background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + position: relative; + overflow: hidden; +} + +// 背景装饰 +.bg-decoration { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + pointer-events: none; + z-index: 0; +} + +.bg-circle { + position: absolute; + border-radius: 50%; + background: rgba(255, 255, 255, 0.1); + animation: float 6s ease-in-out infinite; + + &:nth-child(1) { + width: 200px; + height: 200px; + top: -100px; + right: -100px; + animation-delay: 0s; + } + + &:nth-child(2) { + width: 150px; + height: 150px; + bottom: -75px; + left: -75px; + animation-delay: 2s; + } + + &:nth-child(3) { + width: 100px; + height: 100px; + top: 50%; + right: 10%; + animation-delay: 4s; + } +} + +@keyframes float { + 0%, 100% { + transform: translateY(0px) rotate(0deg); + } + 50% { + transform: translateY(-20px) rotate(180deg); + } +} + +.login-container { + width: 100%; + max-width: 420px; + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(20px); + border-radius: 24px; + padding: 32px 24px; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); + position: relative; + z-index: 1; + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.login-header { + text-align: center; + margin-bottom: 24px; +} + +.logo-section { + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + margin-bottom: 16px; +} + +.logo-icon { + width: 40px; + height: 40px; + background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 20px; + box-shadow: 0 6px 12px rgba(24, 144, 255, 0.3); +} + +.app-name { + font-size: 24px; + font-weight: 800; + background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + margin: 0; +} + +.welcome-text { + font-size: 18px; + font-weight: 600; + color: #333; + margin: 0 0 4px 0; +} + +.subtitle { + font-size: 13px; + color: #666; + margin: 0; +} + +.form-container { + margin-bottom: 20px; +} + +// 标签页样式 +.tab-container { + display: flex; + background: #f8f9fa; + border-radius: 10px; + padding: 3px; + margin-bottom: 24px; + position: relative; +} + +.tab-item { + flex: 1; + text-align: center; + padding: 10px 12px; + font-size: 13px; + font-weight: 500; + color: #666; + cursor: pointer; + border-radius: 7px; + transition: all 0.3s ease; + position: relative; + z-index: 2; + + &.active { + color: #1890ff; + font-weight: 600; + } +} + +.tab-indicator { + position: absolute; + top: 3px; + left: 3px; + width: calc(50% - 3px); + height: calc(100% - 6px); + background: white; + border-radius: 7px; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); + transition: transform 0.3s ease; + z-index: 1; + + &.slide { + transform: translateX(100%); + } +} + +// 表单样式 +.login-form { + :global(.adm-form) { + --adm-font-size-main: 14px; + } +} + +.input-group { + margin-bottom: 18px; +} + +.input-label { + display: block; + font-size: 13px; + font-weight: 600; + color: #333; + margin-bottom: 6px; +} + +.input-wrapper { + position: relative; + display: flex; + align-items: center; + background: #f8f9fa; + border: 2px solid transparent; + border-radius: 10px; + transition: all 0.3s ease; + + &:focus-within { + border-color: #1890ff; + background: white; + box-shadow: 0 0 0 3px rgba(24, 144, 255, 0.1); + } +} + +.input-prefix { + padding: 0 12px; + color: #666; + font-size: 13px; + font-weight: 500; + border-right: 1px solid #e5e5e5; +} + +.phone-input, +.password-input, +.code-input { + flex: 1; + border: none !important; + background: transparent !important; + padding: 12px 14px !important; + font-size: 15px !important; + color: #333 !important; + + &::placeholder { + color: #999; + } + + &:focus { + box-shadow: none !important; + } +} + +.eye-icon { + padding: 0 12px; + color: #666; + cursor: pointer; + transition: color 0.3s ease; + + &:hover { + color: #1890ff; + } +} + +.send-code-btn { + padding: 6px 12px; + margin-right: 6px; + background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); + color: white; + border: none; + border-radius: 6px; + font-size: 11px; + font-weight: 500; + cursor: pointer; + transition: all 0.3s ease; + + &:hover:not(.disabled) { + transform: translateY(-1px); + box-shadow: 0 3px 8px rgba(24, 144, 255, 0.3); + } + + &.disabled { + background: #e5e5e5; + color: #999; + cursor: not-allowed; + transform: none; + box-shadow: none; + } +} + +.agreement-section { + margin-bottom: 24px; + padding: 12px; + background: #f8f9fa; + border-radius: 10px; + border: 1px solid #e5e5e5; +} + +.agreement-checkbox { + display: flex; + align-items: center; + gap: 8px; + font-size: 12px; + color: #666; + line-height: 1.4; + white-space: nowrap; + + :global(.adm-checkbox) { + margin-top: 0; + flex-shrink: 0; + } +} + +.agreement-text { + flex: 1; + display: flex; + align-items: center; + flex-wrap: nowrap; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.agreement-link { + color: #1890ff; + cursor: pointer; + text-decoration: none; + white-space: nowrap; + + &:hover { + text-decoration: underline; + } +} + +.login-btn { + height: 46px; + font-size: 15px; + font-weight: 600; + border-radius: 10px; + background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); + border: none; + box-shadow: 0 6px 12px rgba(24, 144, 255, 0.3); + transition: all 0.3s ease; + + &:hover:not(:disabled) { + transform: translateY(-1px); + box-shadow: 0 8px 16px rgba(24, 144, 255, 0.4); + } + + &:disabled { + background: #e5e5e5; + color: #999; + transform: none; + box-shadow: none; + } +} + +.divider { + position: relative; + text-align: center; + margin: 24px 0; + + &::before { + content: ''; + position: absolute; + top: 50%; + left: 0; + right: 0; + height: 1px; + background: #e5e5e5; + } + + span { + background: rgba(255, 255, 255, 0.95); + padding: 0 12px; + color: #999; + font-size: 11px; + font-weight: 500; + } +} + +.third-party-login { + display: flex; + justify-content: center; + gap: 20px; +} + +.third-party-item { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + cursor: pointer; + padding: 12px; + border-radius: 10px; + transition: all 0.3s ease; + + &:hover { + background: #f8f9fa; + transform: translateY(-1px); + } + + span { + font-size: 11px; + color: #666; + font-weight: 500; + } +} + +.wechat-icon, +.apple-icon { + width: 36px; + height: 36px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 18px; + transition: all 0.3s ease; +} + +.wechat-icon { + background: #07c160; + box-shadow: 0 3px 8px rgba(7, 193, 96, 0.3); + + &:hover { + box-shadow: 0 4px 12px rgba(7, 193, 96, 0.4); + } + + svg { + width: 20px; + height: 20px; + } +} + +.apple-icon { + background: #000; + box-shadow: 0 3px 8px rgba(0, 0, 0, 0.3); + + &:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); + } + + svg { + width: 20px; + height: 20px; + } +} + +// 响应式设计 +@media (max-width: 480px) { + .login-container { + padding: 24px 20px; + margin: 0 12px; + } + + .app-name { + font-size: 22px; + } + + .welcome-text { + font-size: 16px; + } + + .third-party-login { + gap: 16px; + } + + .third-party-item { + padding: 10px; + } + + .wechat-icon, + .apple-icon { + width: 32px; + height: 32px; + } +} diff --git a/nkebao/src/pages/login/login.tsx b/nkebao/src/pages/login/login.tsx index a9a930682..52720f7cf 100644 --- a/nkebao/src/pages/login/login.tsx +++ b/nkebao/src/pages/login/login.tsx @@ -1,72 +1,360 @@ -import React, { useState } from "react"; -import { Form, Input, Button, Toast } from "antd-mobile"; -import { EyeInvisibleOutline, EyeOutline } from "antd-mobile-icons"; -import request from "@/api/request"; +import React, { useState, useEffect } from "react"; +import { useNavigate, useSearchParams } from "react-router-dom"; +import { Form, Input, Button, Toast, Tabs, Checkbox } from "antd-mobile"; +import { + EyeInvisibleOutline, + EyeOutline, + UserOutline, +} from "antd-mobile-icons"; +import { useUserStore } from "@/store/module/user"; +import { loginWithPassword, loginWithCode, sendVerificationCode } from "./api"; import style from "./login.module.scss"; const Login: React.FC = () => { + const [form] = Form.useForm(); + const [activeTab, setActiveTab] = useState("password"); const [loading, setLoading] = useState(false); - const [visible, setVisible] = useState(false); + const [countdown, setCountdown] = useState(0); + const [showPassword, setShowPassword] = useState(false); + const [agreeToTerms, setAgreeToTerms] = useState(false); - const onFinish = async (values: any) => { - setLoading(true); + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + const { login } = useUserStore(); + + // 倒计时效果 + useEffect(() => { + if (countdown > 0) { + const timer = setTimeout(() => setCountdown(countdown - 1), 1000); + return () => clearTimeout(timer); + } + }, [countdown]); + + // 检查URL是否为登录页面 + const isLoginPage = (url: string) => { try { - // 假设接口为 /api/login,实际请根据后端接口调整 - const res = await request("/api/login", values, "POST"); - // 登录成功后保存 token - localStorage.setItem("token", res.token); - Toast.show({ content: "登录成功", position: "top" }); - // 跳转首页或其他页面 - window.location.href = "/"; - } catch (err: any) { - Toast.show({ content: err?.message || "登录失败", position: "top" }); + const urlObj = new URL(url, window.location.origin); + return urlObj.pathname === "/login" || urlObj.pathname.endsWith("/login"); + } catch { + return false; + } + }; + + // 手机号格式验证 + const validatePhone = (phone: string) => { + const phoneRegex = /^1[3-9]\d{9}$/; + return phoneRegex.test(phone); + }; + + // 发送验证码 + const handleSendVerificationCode = async () => { + const phone = form.getFieldValue("phone"); + + if (!phone) { + Toast.show({ content: "请输入手机号", position: "top" }); + return; + } + + if (!validatePhone(phone)) { + Toast.show({ content: "请输入正确的11位手机号", position: "top" }); + return; + } + + try { + setLoading(true); + const response = await sendVerificationCode(phone); + + if (response.code === 200) { + Toast.show({ content: "验证码已发送", position: "top" }); + setCountdown(60); + } else { + Toast.show({ content: response.msg || "发送失败", position: "top" }); + } + } catch (error) { + Toast.show({ content: "发送失败,请稍后重试", position: "top" }); } finally { setLoading(false); } }; + // 表单验证 + const validateForm = async () => { + try { + const values = await form.validateFields(); + + if (!agreeToTerms) { + Toast.show({ content: "请同意用户协议和隐私政策", position: "top" }); + return false; + } + + if (!validatePhone(values.phone)) { + Toast.show({ content: "请输入正确的11位手机号", position: "top" }); + return false; + } + + if (activeTab === "password" && !values.password) { + Toast.show({ content: "请输入密码", position: "top" }); + return false; + } + + if (activeTab === "verification" && !values.verificationCode) { + Toast.show({ content: "请输入验证码", position: "top" }); + return false; + } + + return values; + } catch (error) { + return false; + } + }; + + // 登录处理 + const handleLogin = async () => { + const values = await validateForm(); + if (!values) return; + + setLoading(true); + try { + let response; + + if (activeTab === "password") { + response = await loginWithPassword(values.phone, values.password); + } else { + response = await loginWithCode(values.phone, values.verificationCode); + } + + if (response.code === 200 && response.data) { + // 保存登录信息到localStorage + localStorage.setItem("token", response.data.token); + localStorage.setItem("token_expired", response.data.token_expired); + localStorage.setItem("s2_accountId", response.data.member.s2_accountId); + localStorage.setItem("userInfo", JSON.stringify(response.data.member)); + + // 更新状态管理 + login(response.data.token, response.data.member); + + Toast.show({ content: "登录成功", position: "top" }); + + // 跳转到首页或重定向URL + const returnUrl = searchParams.get("returnUrl"); + if (returnUrl) { + const decodedUrl = decodeURIComponent(returnUrl); + if (isLoginPage(decodedUrl)) { + navigate("/"); + } else { + window.location.href = decodedUrl; + } + } else { + navigate("/"); + } + } else { + Toast.show({ content: response.msg || "登录失败", position: "top" }); + } + } catch (error: any) { + Toast.show({ + content: error?.message || "登录失败,请稍后重试", + position: "top", + }); + } finally { + setLoading(false); + } + }; + + // 第三方登录处理 + const handleWechatLogin = () => { + Toast.show({ content: "微信登录功能开发中", position: "top" }); + }; + + const handleAppleLogin = () => { + Toast.show({ content: "Apple登录功能开发中", position: "top" }); + }; + return (
-
账号登录
-
+
+
+
+
+ +
+ {/* Logo和标题区域 */} +
+
+
+ +
+

存客宝

+
+

欢迎回来

+

登录您的账户继续使用

+
+ + {/* 登录表单 */} +
+ {/* 标签页切换 */} +
+
setActiveTab("password")} + > + 密码登录 +
+
setActiveTab("verification")} + > + 验证码登录 +
+
+
+ + - 登录 - - } - > - - - - - setVisible((v) => !v)}> - {visible ? : } + {/* 手机号输入 */} +
+ +
+ +86 +
- } - /> - - +
+ + {/* 密码输入 */} + {activeTab === "password" && ( +
+ +
+ +
setShowPassword(!showPassword)} + > + {showPassword ? : } +
+
+
+ )} + + {/* 验证码输入 */} + {activeTab === "verification" && ( +
+ +
+ + +
+
+ )} + + {/* 用户协议 */} +
+ + + 我已阅读并同意 + + 《存客宝用户协议》 + + 和 + 《隐私政策》 + + +
+ + {/* 登录按钮 */} + + + + {/* 分割线 */} +
+ 其他登录方式 +
+ + {/* 第三方登录 */} +
+
+
+ + + + +
+ 微信 +
+
+
+ + + +
+ Apple +
+
+
+
); }; diff --git a/nkebao/src/router/module/home.tsx b/nkebao/src/router/module/home.tsx deleted file mode 100644 index 17a08aa0d..000000000 --- a/nkebao/src/router/module/home.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import Home from "@/pages/home/index"; -import Login from "@/pages/login/login"; - -const homeRoutes = [ - { - path: "/", - element: , - auth: false, // 不需要权限 - }, - { - path: "/login", - element: , - auth: false, // 不需要权限 - }, -]; - -export default homeRoutes; diff --git a/nkebao/src/router/module/index.tsx b/nkebao/src/router/module/index.tsx new file mode 100644 index 000000000..f92633a82 --- /dev/null +++ b/nkebao/src/router/module/index.tsx @@ -0,0 +1,35 @@ +import Home from "@/pages/home/index"; +import Login from "@/pages/login/login"; +import Scene from "@/pages/scene/index"; +import Work from "@/pages/work/index"; +import Mine from "@/pages/mine/index"; + +const routes = [ + { + path: "/", + element: , + auth: true, // 需要登录 + }, + { + path: "/login", + element: , + auth: false, // 不需要权限 + }, + { + path: "/scene", + element: , + auth: true, + }, + { + path: "/work", + element: , + auth: true, + }, + { + path: "/mine", + element: , + auth: true, + }, +]; + +export default routes; diff --git a/nkebao/src/router/permissionRoute.tsx b/nkebao/src/router/permissionRoute.tsx index 66b0b3dcb..95d8b89d4 100644 --- a/nkebao/src/router/permissionRoute.tsx +++ b/nkebao/src/router/permissionRoute.tsx @@ -1,27 +1,45 @@ -import React from "react"; -import { Navigate, useLocation } from "react-router-dom"; -import { useUserStore } from "@/store/module/user"; // 假设你用 zustand 管理用户状态 +import React, { useEffect } from "react"; +import { useNavigate, useLocation } from "react-router-dom"; +import { useUserStore } from "@/store/module/user"; -interface Props { +interface PermissionRouteProps { children: React.ReactNode; - requiredRole?: string; // 可选:需要的角色 + requiredRole?: string; } -const PermissionRoute: React.FC = ({ children, requiredRole }) => { - const user = useUserStore((state) => state.user); +const PermissionRoute: React.FC = ({ + children, + requiredRole, +}) => { + const { user, isLoggedIn } = useUserStore(); + const navigate = useNavigate(); const location = useLocation(); - // 未登录 - if (!user) { - return ; + useEffect(() => { + // 检查是否已登录 + if (!isLoggedIn || !user) { + const currentPath = location.pathname + location.search; + navigate(`/login?returnUrl=${encodeURIComponent(currentPath)}`); + return; + } + + // 检查角色权限(如果需要) + if (requiredRole && user.role !== requiredRole) { + navigate("/"); + return; + } + }, [isLoggedIn, user, requiredRole, navigate, location]); + + // 如果未登录,不渲染子组件 + if (!isLoggedIn || !user) { + return null; } - // 有角色要求但不满足 + // 如果角色不匹配,不渲染子组件 if (requiredRole && user.role !== requiredRole) { - return ; + return null; } - // 通过 return <>{children}; }; diff --git a/nkebao/src/store/module/user.ts b/nkebao/src/store/module/user.ts index 876c8a774..1df118944 100644 --- a/nkebao/src/store/module/user.ts +++ b/nkebao/src/store/module/user.ts @@ -1,23 +1,54 @@ import { createPersistStore } from '@/store/createPersistStore'; export interface User { + id?: string; name: string; + phone: string; role: string; token: string; + token_expired?: string; + s2_accountId?: string; + avatar?: string; + email?: string; } interface UserState { user: User | null; + isLoggedIn: boolean; setUser: (user: User) => void; clearUser: () => void; + login: (token: string, userInfo: any) => void; + logout: () => void; } export const useUserStore = createPersistStore( (set) => ({ user: null, - setUser: (user) => set({ user }), - clearUser: () => set({ user: null }), + isLoggedIn: false, + setUser: (user) => set({ user, isLoggedIn: true }), + clearUser: () => set({ user: null, isLoggedIn: false }), + login: (token, userInfo) => { + const user: User = { + id: userInfo.id, + name: userInfo.name || userInfo.nickname || userInfo.username, + phone: userInfo.phone, + role: userInfo.role || 'user', + token, + token_expired: userInfo.token_expired, + s2_accountId: userInfo.s2_accountId, + avatar: userInfo.avatar, + email: userInfo.email, + }; + set({ user, isLoggedIn: true }); + }, + logout: () => { + localStorage.removeItem('token'); + localStorage.removeItem('token_expired'); + localStorage.removeItem('s2_accountId'); + localStorage.removeItem('userInfo'); + set({ user: null, isLoggedIn: false }); + }, }), 'user-store', - (state) => ({ user: state.user }) + (state) => ({ user: state.user, isLoggedIn: state.isLoggedIn }) ); \ No newline at end of file From 0e6bc6d7835ee9307f444e5ab6c192b1b38f1432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Fri, 18 Jul 2025 16:21:15 +0800 Subject: [PATCH 11/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E6=9B=BF=E6=8D=A2=E5=BE=AE=E4=BF=A1=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/pages/login/login.module.scss | 47 +- nkebao/src/pages/login/login.tsx | 724 +++++++++++------------ nkebao/src/styles/global.scss | 11 + 3 files changed, 391 insertions(+), 391 deletions(-) diff --git a/nkebao/src/pages/login/login.module.scss b/nkebao/src/pages/login/login.module.scss index 0f9408ea0..455278839 100644 --- a/nkebao/src/pages/login/login.module.scss +++ b/nkebao/src/pages/login/login.module.scss @@ -1,6 +1,6 @@ .login-page { min-height: 100vh; - background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); + background: var(--primary-gradient); display: flex; align-items: center; justify-content: center; @@ -63,7 +63,7 @@ .login-container { width: 100%; max-width: 420px; - background: rgba(255, 255, 255, 0.95); + background: #ffffff; backdrop-filter: blur(20px); border-radius: 24px; padding: 32px 24px; @@ -89,20 +89,20 @@ .logo-icon { width: 40px; height: 40px; - background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); + background: var(--primary-gradient); border-radius: 10px; display: flex; align-items: center; justify-content: center; color: white; font-size: 20px; - box-shadow: 0 6px 12px rgba(24, 144, 255, 0.3); + box-shadow: 0 6px 12px var(--primary-shadow); } .app-name { font-size: 24px; font-weight: 800; - background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); + background: var(--primary-gradient); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; @@ -150,26 +150,15 @@ z-index: 2; &.active { - color: #1890ff; + color: var(--primary-color); font-weight: 600; + background: white; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); } } .tab-indicator { - position: absolute; - top: 3px; - left: 3px; - width: calc(50% - 3px); - height: calc(100% - 6px); - background: white; - border-radius: 7px; - box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); - transition: transform 0.3s ease; - z-index: 1; - - &.slide { - transform: translateX(100%); - } + display: none; // 隐藏分割线指示器 } // 表单样式 @@ -201,9 +190,9 @@ transition: all 0.3s ease; &:focus-within { - border-color: #1890ff; + border-color: var(--primary-color); background: white; - box-shadow: 0 0 0 3px rgba(24, 144, 255, 0.1); + box-shadow: 0 0 0 3px var(--primary-shadow-light); } } @@ -241,14 +230,14 @@ transition: color 0.3s ease; &:hover { - color: #1890ff; + color: var(--primary-color); } } .send-code-btn { padding: 6px 12px; margin-right: 6px; - background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); + background: var(--primary-gradient); color: white; border: none; border-radius: 6px; @@ -259,7 +248,7 @@ &:hover:not(.disabled) { transform: translateY(-1px); - box-shadow: 0 3px 8px rgba(24, 144, 255, 0.3); + box-shadow: 0 3px 8px var(--primary-shadow); } &.disabled { @@ -305,7 +294,7 @@ } .agreement-link { - color: #1890ff; + color: var(--primary-color); cursor: pointer; text-decoration: none; white-space: nowrap; @@ -320,14 +309,14 @@ font-size: 15px; font-weight: 600; border-radius: 10px; - background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); + background: var(--primary-gradient); border: none; - box-shadow: 0 6px 12px rgba(24, 144, 255, 0.3); + box-shadow: 0 6px 12px var(--primary-shadow); transition: all 0.3s ease; &:hover:not(:disabled) { transform: translateY(-1px); - box-shadow: 0 8px 16px rgba(24, 144, 255, 0.4); + box-shadow: 0 8px 16px var(--primary-shadow-dark); } &:disabled { diff --git a/nkebao/src/pages/login/login.tsx b/nkebao/src/pages/login/login.tsx index 52720f7cf..25beeeade 100644 --- a/nkebao/src/pages/login/login.tsx +++ b/nkebao/src/pages/login/login.tsx @@ -1,362 +1,362 @@ -import React, { useState, useEffect } from "react"; -import { useNavigate, useSearchParams } from "react-router-dom"; -import { Form, Input, Button, Toast, Tabs, Checkbox } from "antd-mobile"; -import { - EyeInvisibleOutline, - EyeOutline, - UserOutline, -} from "antd-mobile-icons"; -import { useUserStore } from "@/store/module/user"; -import { loginWithPassword, loginWithCode, sendVerificationCode } from "./api"; -import style from "./login.module.scss"; - -const Login: React.FC = () => { - const [form] = Form.useForm(); - const [activeTab, setActiveTab] = useState("password"); - const [loading, setLoading] = useState(false); - const [countdown, setCountdown] = useState(0); - const [showPassword, setShowPassword] = useState(false); - const [agreeToTerms, setAgreeToTerms] = useState(false); - - const navigate = useNavigate(); - const [searchParams] = useSearchParams(); - const { login } = useUserStore(); - - // 倒计时效果 - useEffect(() => { - if (countdown > 0) { - const timer = setTimeout(() => setCountdown(countdown - 1), 1000); - return () => clearTimeout(timer); - } - }, [countdown]); - - // 检查URL是否为登录页面 - const isLoginPage = (url: string) => { - try { - const urlObj = new URL(url, window.location.origin); - return urlObj.pathname === "/login" || urlObj.pathname.endsWith("/login"); - } catch { - return false; - } - }; - - // 手机号格式验证 - const validatePhone = (phone: string) => { - const phoneRegex = /^1[3-9]\d{9}$/; - return phoneRegex.test(phone); - }; - - // 发送验证码 - const handleSendVerificationCode = async () => { - const phone = form.getFieldValue("phone"); - - if (!phone) { - Toast.show({ content: "请输入手机号", position: "top" }); - return; - } - - if (!validatePhone(phone)) { - Toast.show({ content: "请输入正确的11位手机号", position: "top" }); - return; - } - - try { - setLoading(true); - const response = await sendVerificationCode(phone); - - if (response.code === 200) { - Toast.show({ content: "验证码已发送", position: "top" }); - setCountdown(60); - } else { - Toast.show({ content: response.msg || "发送失败", position: "top" }); - } - } catch (error) { - Toast.show({ content: "发送失败,请稍后重试", position: "top" }); - } finally { - setLoading(false); - } - }; - - // 表单验证 - const validateForm = async () => { - try { - const values = await form.validateFields(); - - if (!agreeToTerms) { - Toast.show({ content: "请同意用户协议和隐私政策", position: "top" }); - return false; - } - - if (!validatePhone(values.phone)) { - Toast.show({ content: "请输入正确的11位手机号", position: "top" }); - return false; - } - - if (activeTab === "password" && !values.password) { - Toast.show({ content: "请输入密码", position: "top" }); - return false; - } - - if (activeTab === "verification" && !values.verificationCode) { - Toast.show({ content: "请输入验证码", position: "top" }); - return false; - } - - return values; - } catch (error) { - return false; - } - }; - - // 登录处理 - const handleLogin = async () => { - const values = await validateForm(); - if (!values) return; - - setLoading(true); - try { - let response; - - if (activeTab === "password") { - response = await loginWithPassword(values.phone, values.password); - } else { - response = await loginWithCode(values.phone, values.verificationCode); - } - - if (response.code === 200 && response.data) { - // 保存登录信息到localStorage - localStorage.setItem("token", response.data.token); - localStorage.setItem("token_expired", response.data.token_expired); - localStorage.setItem("s2_accountId", response.data.member.s2_accountId); - localStorage.setItem("userInfo", JSON.stringify(response.data.member)); - - // 更新状态管理 - login(response.data.token, response.data.member); - - Toast.show({ content: "登录成功", position: "top" }); - - // 跳转到首页或重定向URL - const returnUrl = searchParams.get("returnUrl"); - if (returnUrl) { - const decodedUrl = decodeURIComponent(returnUrl); - if (isLoginPage(decodedUrl)) { - navigate("/"); - } else { - window.location.href = decodedUrl; - } - } else { - navigate("/"); - } - } else { - Toast.show({ content: response.msg || "登录失败", position: "top" }); - } - } catch (error: any) { - Toast.show({ - content: error?.message || "登录失败,请稍后重试", - position: "top", - }); - } finally { - setLoading(false); - } - }; - - // 第三方登录处理 - const handleWechatLogin = () => { - Toast.show({ content: "微信登录功能开发中", position: "top" }); - }; - - const handleAppleLogin = () => { - Toast.show({ content: "Apple登录功能开发中", position: "top" }); - }; - - return ( -
- {/* 背景装饰 */} -
-
-
-
-
- -
- {/* Logo和标题区域 */} -
-
-
- -
-

存客宝

-
-

欢迎回来

-

登录您的账户继续使用

-
- - {/* 登录表单 */} -
- {/* 标签页切换 */} -
-
setActiveTab("password")} - > - 密码登录 -
-
setActiveTab("verification")} - > - 验证码登录 -
-
-
- -
- {/* 手机号输入 */} -
- -
- +86 - -
-
- - {/* 密码输入 */} - {activeTab === "password" && ( -
- -
- -
setShowPassword(!showPassword)} - > - {showPassword ? : } -
-
-
- )} - - {/* 验证码输入 */} - {activeTab === "verification" && ( -
- -
- - -
-
- )} - - {/* 用户协议 */} -
- - - 我已阅读并同意 - - 《存客宝用户协议》 - - 和 - 《隐私政策》 - - -
- - {/* 登录按钮 */} - -
- - {/* 分割线 */} -
- 其他登录方式 -
- - {/* 第三方登录 */} -
-
-
- - - - -
- 微信 -
-
-
- - - -
- Apple -
-
-
-
-
- ); -}; - -export default Login; +import React, { useState, useEffect } from "react"; +import { useNavigate, useSearchParams } from "react-router-dom"; +import { Form, Input, Button, Toast, Tabs, Checkbox } from "antd-mobile"; +import { + EyeInvisibleOutline, + EyeOutline, + UserOutline, +} from "antd-mobile-icons"; +import { useUserStore } from "@/store/module/user"; +import { loginWithPassword, loginWithCode, sendVerificationCode } from "./api"; +import style from "./login.module.scss"; + +const Login: React.FC = () => { + const [form] = Form.useForm(); + const [activeTab, setActiveTab] = useState("password"); + const [loading, setLoading] = useState(false); + const [countdown, setCountdown] = useState(0); + const [showPassword, setShowPassword] = useState(false); + const [agreeToTerms, setAgreeToTerms] = useState(false); + + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + const { login } = useUserStore(); + + // 倒计时效果 + useEffect(() => { + if (countdown > 0) { + const timer = setTimeout(() => setCountdown(countdown - 1), 1000); + return () => clearTimeout(timer); + } + }, [countdown]); + + // 检查URL是否为登录页面 + const isLoginPage = (url: string) => { + try { + const urlObj = new URL(url, window.location.origin); + return urlObj.pathname === "/login" || urlObj.pathname.endsWith("/login"); + } catch { + return false; + } + }; + + // 手机号格式验证 + const validatePhone = (phone: string) => { + const phoneRegex = /^1[3-9]\d{9}$/; + return phoneRegex.test(phone); + }; + + // 发送验证码 + const handleSendVerificationCode = async () => { + const phone = form.getFieldValue("phone"); + + if (!phone) { + Toast.show({ content: "请输入手机号", position: "top" }); + return; + } + + if (!validatePhone(phone)) { + Toast.show({ content: "请输入正确的11位手机号", position: "top" }); + return; + } + + try { + setLoading(true); + const response = await sendVerificationCode(phone); + + if (response.code === 200) { + Toast.show({ content: "验证码已发送", position: "top" }); + setCountdown(60); + } else { + Toast.show({ content: response.msg || "发送失败", position: "top" }); + } + } catch (error) { + Toast.show({ content: "发送失败,请稍后重试", position: "top" }); + } finally { + setLoading(false); + } + }; + + // 表单验证 + const validateForm = async () => { + try { + const values = await form.validateFields(); + + if (!agreeToTerms) { + Toast.show({ content: "请同意用户协议和隐私政策", position: "top" }); + return false; + } + + if (!validatePhone(values.phone)) { + Toast.show({ content: "请输入正确的11位手机号", position: "top" }); + return false; + } + + if (activeTab === "password" && !values.password) { + Toast.show({ content: "请输入密码", position: "top" }); + return false; + } + + if (activeTab === "verification" && !values.verificationCode) { + Toast.show({ content: "请输入验证码", position: "top" }); + return false; + } + + return values; + } catch (error) { + return false; + } + }; + + // 登录处理 + const handleLogin = async () => { + const values = await validateForm(); + if (!values) return; + + setLoading(true); + try { + let response; + + if (activeTab === "password") { + response = await loginWithPassword(values.phone, values.password); + } else { + response = await loginWithCode(values.phone, values.verificationCode); + } + + if (response.code === 200 && response.data) { + // 保存登录信息到localStorage + localStorage.setItem("token", response.data.token); + localStorage.setItem("token_expired", response.data.token_expired); + localStorage.setItem("s2_accountId", response.data.member.s2_accountId); + localStorage.setItem("userInfo", JSON.stringify(response.data.member)); + + // 更新状态管理 + login(response.data.token, response.data.member); + + Toast.show({ content: "登录成功", position: "top" }); + + // 跳转到首页或重定向URL + const returnUrl = searchParams.get("returnUrl"); + if (returnUrl) { + const decodedUrl = decodeURIComponent(returnUrl); + if (isLoginPage(decodedUrl)) { + navigate("/"); + } else { + window.location.href = decodedUrl; + } + } else { + navigate("/"); + } + } else { + Toast.show({ content: response.msg || "登录失败", position: "top" }); + } + } catch (error: any) { + Toast.show({ + content: error?.message || "登录失败,请稍后重试", + position: "top", + }); + } finally { + setLoading(false); + } + }; + + // 第三方登录处理 + const handleWechatLogin = () => { + Toast.show({ content: "微信登录功能开发中", position: "top" }); + }; + + const handleAppleLogin = () => { + Toast.show({ content: "Apple登录功能开发中", position: "top" }); + }; + + return ( +
+ {/* 背景装饰 */} +
+
+
+
+
+ +
+ {/* Logo和标题区域 */} +
+
+
+ +
+

存客宝

+
+

欢迎回来

+

登录您的账户继续使用

+
+ + {/* 登录表单 */} +
+ {/* 标签页切换 */} +
+
setActiveTab("password")} + > + 密码登录 +
+
setActiveTab("verification")} + > + 验证码登录 +
+
+
+ +
+ {/* 手机号输入 */} +
+ +
+ +86 + +
+
+ + {/* 密码输入 */} + {activeTab === "password" && ( +
+ +
+ +
setShowPassword(!showPassword)} + > + {showPassword ? : } +
+
+
+ )} + + {/* 验证码输入 */} + {activeTab === "verification" && ( +
+ +
+ + +
+
+ )} + + {/* 用户协议 */} +
+ + + 我已阅读并同意 + + 《存客宝用户协议》 + + 和 + 《隐私政策》 + + +
+ + {/* 登录按钮 */} + +
+ + {/* 分割线 */} +
+ 其他登录方式 +
+ + {/* 第三方登录 */} +
+
+
+ + + + +
+ 微信 +
+
+
+ + + +
+ Apple +
+
+
+
+
+ ); +}; + +export default Login; diff --git a/nkebao/src/styles/global.scss b/nkebao/src/styles/global.scss index 314af21a4..079b5d0ce 100644 --- a/nkebao/src/styles/global.scss +++ b/nkebao/src/styles/global.scss @@ -1,5 +1,16 @@ /* Reset & 基础样式,兼容移动端和PC端,补充 iOS/安卓容差处理 */ +/* 主题色变量定义 */ +:root { + --primary-color: #188eee; + --primary-color-light: #40a9ff; + --primary-color-dark: #096dd9; + --primary-gradient: linear-gradient(135deg, #188eee 0%, #096dd9 100%); + --primary-shadow: rgba(24, 142, 238, 0.3); + --primary-shadow-light: rgba(24, 142, 238, 0.1); + --primary-shadow-dark: rgba(24, 142, 238, 0.4); +} + /* 1. 通用 Reset */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, From aff4700a10c452beeae61218d9efc21ddc9cfe50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Fri, 18 Jul 2025 16:30:37 +0800 Subject: [PATCH 12/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E5=AD=98=E4=BA=86=E5=BE=AE=E4=BF=A1=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/pages/login/login.module.scss | 15 +++-- nkebao/src/pages/login/login.tsx | 81 ++++++++++-------------- 2 files changed, 43 insertions(+), 53 deletions(-) diff --git a/nkebao/src/pages/login/login.module.scss b/nkebao/src/pages/login/login.module.scss index 455278839..5c2c55d2d 100644 --- a/nkebao/src/pages/login/login.module.scss +++ b/nkebao/src/pages/login/login.module.scss @@ -262,7 +262,7 @@ .agreement-section { margin-bottom: 24px; - padding: 12px; + padding: 10px; background: #f8f9fa; border-radius: 10px; border: 1px solid #e5e5e5; @@ -271,15 +271,16 @@ .agreement-checkbox { display: flex; align-items: center; - gap: 8px; - font-size: 12px; + gap: 6px; + font-size: 11px; color: #666; - line-height: 1.4; + line-height: 1.3; white-space: nowrap; :global(.adm-checkbox) { margin-top: 0; flex-shrink: 0; + transform: scale(0.8); } } @@ -289,8 +290,9 @@ align-items: center; flex-wrap: nowrap; white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + overflow: visible; + text-overflow: clip; + font-size: 11px; } .agreement-link { @@ -298,6 +300,7 @@ cursor: pointer; text-decoration: none; white-space: nowrap; + font-size: 11px; &:hover { text-decoration: underline; diff --git a/nkebao/src/pages/login/login.tsx b/nkebao/src/pages/login/login.tsx index 25beeeade..570bf016e 100644 --- a/nkebao/src/pages/login/login.tsx +++ b/nkebao/src/pages/login/login.tsx @@ -7,7 +7,11 @@ import { UserOutline, } from "antd-mobile-icons"; import { useUserStore } from "@/store/module/user"; -import { loginWithPassword, loginWithCode, sendVerificationCode } from "./api"; +import { + loginWithPassword, + loginWithCode, + sendVerificationCode, +} from "@/api/auth"; import style from "./login.module.scss"; const Login: React.FC = () => { @@ -77,41 +81,12 @@ const Login: React.FC = () => { } }; - // 表单验证 - const validateForm = async () => { - try { - const values = await form.validateFields(); - - if (!agreeToTerms) { - Toast.show({ content: "请同意用户协议和隐私政策", position: "top" }); - return false; - } - - if (!validatePhone(values.phone)) { - Toast.show({ content: "请输入正确的11位手机号", position: "top" }); - return false; - } - - if (activeTab === "password" && !values.password) { - Toast.show({ content: "请输入密码", position: "top" }); - return false; - } - - if (activeTab === "verification" && !values.verificationCode) { - Toast.show({ content: "请输入验证码", position: "top" }); - return false; - } - - return values; - } catch (error) { - return false; - } - }; - // 登录处理 - const handleLogin = async () => { - const values = await validateForm(); - if (!values) return; + const handleLogin = async (values: any) => { + if (!agreeToTerms) { + Toast.show({ content: "请同意用户协议和隐私政策", position: "top" }); + return; + } setLoading(true); try { @@ -225,26 +200,36 @@ const Login: React.FC = () => { onFinish={handleLogin} > {/* 手机号输入 */} -
- +
+86
-
+ {/* 密码输入 */} {activeTab === "password" && ( -
- +
{ {showPassword ? : }
- + )} {/* 验证码输入 */} {activeTab === "verification" && ( -
- +
{ {countdown > 0 ? `${countdown}s` : "获取验证码"}
-
+ )} {/* 用户协议 */} From 20a254f957080224e58ce1ad27b9063c85358c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Fri, 18 Jul 2025 16:32:25 +0800 Subject: [PATCH 13/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20api=E9=97=AE=E9=A2=98=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/api/auth.ts | 54 -------------------------------- nkebao/src/pages/login/login.tsx | 6 +--- 2 files changed, 1 insertion(+), 59 deletions(-) delete mode 100644 nkebao/src/api/auth.ts diff --git a/nkebao/src/api/auth.ts b/nkebao/src/api/auth.ts deleted file mode 100644 index 36b1a82ec..000000000 --- a/nkebao/src/api/auth.ts +++ /dev/null @@ -1,54 +0,0 @@ -import request from './request'; - -export interface LoginParams { - phone: string; - password?: string; - verificationCode?: string; -} - -export interface LoginResponse { - code: number; - msg: string; - data: { - token: string; - token_expired: string; - member: { - id: string; - name: string; - phone: string; - s2_accountId: string; - avatar?: string; - email?: string; - }; - }; -} - -export interface SendCodeResponse { - code: number; - msg: string; -} - -// 密码登录 -export function loginWithPassword(phone: string, password: string) { - return request('/v1/auth/login', { phone, password }, 'POST'); -} - -// 验证码登录 -export function loginWithCode(phone: string, verificationCode: string) { - return request('/v1/auth/login-code', { phone, verificationCode }, 'POST'); -} - -// 发送验证码 -export function sendVerificationCode(phone: string) { - return request('/v1/auth/send-code', { phone }, 'POST'); -} - -// 退出登录 -export function logout() { - return request('/v1/auth/logout', {}, 'POST'); -} - -// 获取用户信息 -export function getUserInfo() { - return request('/v1/auth/user-info', {}, 'GET'); -} \ No newline at end of file diff --git a/nkebao/src/pages/login/login.tsx b/nkebao/src/pages/login/login.tsx index 570bf016e..f38d7dc7e 100644 --- a/nkebao/src/pages/login/login.tsx +++ b/nkebao/src/pages/login/login.tsx @@ -7,11 +7,7 @@ import { UserOutline, } from "antd-mobile-icons"; import { useUserStore } from "@/store/module/user"; -import { - loginWithPassword, - loginWithCode, - sendVerificationCode, -} from "@/api/auth"; +import { loginWithPassword, loginWithCode, sendVerificationCode } from "./api"; import style from "./login.module.scss"; const Login: React.FC = () => { From 4ce3036c426c588d2b89fe9142895ec4b7c51e21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Fri, 18 Jul 2025 17:23:25 +0800 Subject: [PATCH 14/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E7=99=BB=E5=BD=95=E9=A1=B5=E9=9D=A2=E7=AE=97=E6=98=AF?= =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/pages/login/api.ts | 2 +- nkebao/src/pages/login/login.module.scss | 23 +----- nkebao/src/pages/login/login.tsx | 99 ++++++++++-------------- nkebao/src/store/module/user.ts | 64 +++++++++------ 4 files changed, 90 insertions(+), 98 deletions(-) diff --git a/nkebao/src/pages/login/api.ts b/nkebao/src/pages/login/api.ts index 1aba513ee..191b14f7e 100644 --- a/nkebao/src/pages/login/api.ts +++ b/nkebao/src/pages/login/api.ts @@ -39,7 +39,7 @@ export function loginWithCode(params:any) { // 发送验证码 export function sendVerificationCode(params:any) { - return request('/v1/auth/send-code',params, 'POST'); + return request('/v1/auth/code',params, 'POST'); } // 退出登录 diff --git a/nkebao/src/pages/login/login.module.scss b/nkebao/src/pages/login/login.module.scss index 5c2c55d2d..a13baa521 100644 --- a/nkebao/src/pages/login/login.module.scss +++ b/nkebao/src/pages/login/login.module.scss @@ -4,7 +4,7 @@ display: flex; align-items: center; justify-content: center; - padding: 20px; + padding: 15px; position: relative; overflow: hidden; } @@ -66,7 +66,7 @@ background: #ffffff; backdrop-filter: blur(20px); border-radius: 24px; - padding: 32px 24px; + padding: 24px 20px; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); position: relative; z-index: 1; @@ -109,13 +109,6 @@ margin: 0; } -.welcome-text { - font-size: 18px; - font-weight: 600; - color: #333; - margin: 0 0 4px 0; -} - .subtitle { font-size: 13px; color: #666; @@ -262,17 +255,13 @@ .agreement-section { margin-bottom: 24px; - padding: 10px; - background: #f8f9fa; - border-radius: 10px; - border: 1px solid #e5e5e5; } .agreement-checkbox { display: flex; align-items: center; gap: 6px; - font-size: 11px; + font-size: 12px; color: #666; line-height: 1.3; white-space: nowrap; @@ -292,7 +281,7 @@ white-space: nowrap; overflow: visible; text-overflow: clip; - font-size: 11px; + font-size: 13px; } .agreement-link { @@ -434,10 +423,6 @@ font-size: 22px; } - .welcome-text { - font-size: 16px; - } - .third-party-login { gap: 16px; } diff --git a/nkebao/src/pages/login/login.tsx b/nkebao/src/pages/login/login.tsx index f38d7dc7e..f53ccc6d0 100644 --- a/nkebao/src/pages/login/login.tsx +++ b/nkebao/src/pages/login/login.tsx @@ -12,7 +12,7 @@ import style from "./login.module.scss"; const Login: React.FC = () => { const [form] = Form.useForm(); - const [activeTab, setActiveTab] = useState("password"); + const [activeTab, setActiveTab] = useState(1); // 1: 密码登录, 2: 验证码登录 const [loading, setLoading] = useState(false); const [countdown, setCountdown] = useState(0); const [showPassword, setShowPassword] = useState(false); @@ -40,38 +40,33 @@ const Login: React.FC = () => { } }; - // 手机号格式验证 - const validatePhone = (phone: string) => { - const phoneRegex = /^1[3-9]\d{9}$/; - return phoneRegex.test(phone); - }; - // 发送验证码 const handleSendVerificationCode = async () => { - const phone = form.getFieldValue("phone"); + const account = form.getFieldValue("account"); - if (!phone) { + if (!account) { Toast.show({ content: "请输入手机号", position: "top" }); return; } - if (!validatePhone(phone)) { + // 手机号格式验证 + const phoneRegex = /^1[3-9]\d{9}$/; + if (!phoneRegex.test(account)) { Toast.show({ content: "请输入正确的11位手机号", position: "top" }); return; } try { setLoading(true); - const response = await sendVerificationCode(phone); + await sendVerificationCode({ + mobile: account, + type: "login", + }); - if (response.code === 200) { - Toast.show({ content: "验证码已发送", position: "top" }); - setCountdown(60); - } else { - Toast.show({ content: response.msg || "发送失败", position: "top" }); - } + Toast.show({ content: "验证码已发送", position: "top" }); + setCountdown(60); } catch (error) { - Toast.show({ content: "发送失败,请稍后重试", position: "top" }); + // 错误已在request中处理,这里不需要额外处理 } finally { setLoading(false); } @@ -86,46 +81,39 @@ const Login: React.FC = () => { setLoading(true); try { + // 添加typeId参数 + const loginParams = { + ...values, + typeId: activeTab as number, + }; + let response; - - if (activeTab === "password") { - response = await loginWithPassword(values.phone, values.password); + if (activeTab === 1) { + response = await loginWithPassword(loginParams); } else { - response = await loginWithCode(values.phone, values.verificationCode); + response = await loginWithCode(loginParams); } + console.log(response, "response"); - if (response.code === 200 && response.data) { - // 保存登录信息到localStorage - localStorage.setItem("token", response.data.token); - localStorage.setItem("token_expired", response.data.token_expired); - localStorage.setItem("s2_accountId", response.data.member.s2_accountId); - localStorage.setItem("userInfo", JSON.stringify(response.data.member)); + // 更新状态管理(token会自动存储到localStorage,用户信息存储在状态管理中) + login(response.token, response.member); - // 更新状态管理 - login(response.data.token, response.data.member); + Toast.show({ content: "登录成功", position: "top" }); - Toast.show({ content: "登录成功", position: "top" }); - - // 跳转到首页或重定向URL - const returnUrl = searchParams.get("returnUrl"); - if (returnUrl) { - const decodedUrl = decodeURIComponent(returnUrl); - if (isLoginPage(decodedUrl)) { - navigate("/"); - } else { - window.location.href = decodedUrl; - } - } else { + // 跳转到首页或重定向URL + const returnUrl = searchParams.get("returnUrl"); + if (returnUrl) { + const decodedUrl = decodeURIComponent(returnUrl); + if (isLoginPage(decodedUrl)) { navigate("/"); + } else { + window.location.href = decodedUrl; } } else { - Toast.show({ content: response.msg || "登录失败", position: "top" }); + navigate("/"); } } catch (error: any) { - Toast.show({ - content: error?.message || "登录失败,请稍后重试", - position: "top", - }); + // 错误已在request中处理,这里不需要额外处理 } finally { setLoading(false); } @@ -158,7 +146,6 @@ const Login: React.FC = () => {

存客宝

-

欢迎回来

登录您的账户继续使用

@@ -168,23 +155,23 @@ const Login: React.FC = () => {
setActiveTab("password")} + onClick={() => setActiveTab(1)} > 密码登录
setActiveTab("verification")} + onClick={() => setActiveTab(2)} > 验证码登录
@@ -197,7 +184,7 @@ const Login: React.FC = () => { > {/* 手机号输入 */} { {/* 密码输入 */} - {activeTab === "password" && ( + {activeTab === 1 && ( { )} {/* 验证码输入 */} - {activeTab === "verification" && ( + {activeTab === 2 && ( void; + setToken: (token: string) => void; clearUser: () => void; - login: (token: string, userInfo: any) => void; + login: (token: string, userInfo: User) => void; logout: () => void; } export const useUserStore = createPersistStore( (set) => ({ user: null, + token: null, isLoggedIn: false, setUser: (user) => set({ user, isLoggedIn: true }), - clearUser: () => set({ user: null, isLoggedIn: false }), + setToken: (token) => set({ token }), + clearUser: () => set({ user: null, token: null, isLoggedIn: false }), login: (token, userInfo) => { + // 只将token存储到localStorage + localStorage.setItem('token', token); + + // 用户信息存储在状态管理中 const user: User = { id: userInfo.id, - name: userInfo.name || userInfo.nickname || userInfo.username, + account: userInfo.account, + username: userInfo.username, phone: userInfo.phone, - role: userInfo.role || 'user', - token, - token_expired: userInfo.token_expired, - s2_accountId: userInfo.s2_accountId, avatar: userInfo.avatar, - email: userInfo.email, + isAdmin: userInfo.isAdmin, + companyId: userInfo.companyId, + typeId: userInfo.typeId, + status: userInfo.status, + s2_accountId: userInfo.s2_accountId, + createTime: userInfo.createTime, + updateTime: userInfo.updateTime, + lastLoginIp: userInfo.lastLoginIp, + lastLoginTime: userInfo.lastLoginTime, }; - set({ user, isLoggedIn: true }); + set({ user, token, isLoggedIn: true }); }, logout: () => { + // 清除localStorage中的token localStorage.removeItem('token'); - localStorage.removeItem('token_expired'); - localStorage.removeItem('s2_accountId'); - localStorage.removeItem('userInfo'); - set({ user: null, isLoggedIn: false }); + set({ user: null, token: null, isLoggedIn: false }); }, }), 'user-store', - (state) => ({ user: state.user, isLoggedIn: state.isLoggedIn }) + (state) => ({ + user: state.user, + token: state.token, + isLoggedIn: state.isLoggedIn + }) ); \ No newline at end of file From c9893b337102e3f1606178a6eb70dd40e9ab304f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Fri, 18 Jul 2025 21:36:49 +0800 Subject: [PATCH 15/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E8=BF=99=E4=B8=AA=E6=A0=B7=E5=BC=8F=E5=BE=88=E6=BC=82?= =?UTF-8?q?=E4=BA=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/pages/home/index.module.scss | 304 +++++++++++++++++++----- nkebao/src/pages/home/index.tsx | 170 +++++++++---- 2 files changed, 365 insertions(+), 109 deletions(-) diff --git a/nkebao/src/pages/home/index.module.scss b/nkebao/src/pages/home/index.module.scss index e2c392aa5..1a7b7b1b6 100644 --- a/nkebao/src/pages/home/index.module.scss +++ b/nkebao/src/pages/home/index.module.scss @@ -1,70 +1,260 @@ .home-page { padding: 12px; - background: #f5f5f5; + background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); + min-height: 100vh; } -.home-cards { +// 导航栏样式 +.nav-title { display: flex; - gap: 12px; - margin-bottom: 12px; - > :global(.adm-card) { - flex: 1; + align-items: center; + justify-content: center; +} + +.nav-text { + color: var(--primary-color); + font-weight: 700; + font-size: 18px; + text-shadow: 0 2px 4px rgba(24, 142, 238, 0.2); +} + +// 统计卡片网格 +.stats-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px; + margin-bottom: 16px; +} + +.stat-card { + background: white; + border-radius: 12px; + padding: 12px 8px; + display: flex; + align-items: center; + gap: 8px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + transition: all 0.3s ease; + + &:hover { + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); } } -.home-section { - margin-bottom: 12px; - background: #fff; - border-radius: 12px; - box-shadow: 0 2px 8px rgba(0,0,0,0.03); -} - -.home-section-title { - font-size: 16px; - font-weight: 600; - margin-bottom: 15px; -} - -.home-scene-stats { - display: flex; - justify-content: space-between; - margin: 0 8px 8px 8px; -} -.home-scene-item { - flex: 1; - text-align: center; -} -.home-scene-icon { - margin: 0 auto 4px auto; -} -.home-scene-value { - font-size: 18px; - font-weight: bold; - color: #1677ff; -} -.home-scene-label { - font-size: 12px; - color: #888; -} - -.home-today-item { - text-align: center; - padding: 8px 0; - background: #f7f8fa; +.stat-icon { + width: 32px; + height: 32px; border-radius: 8px; -} -.home-today-value { - font-size: 18px; - font-weight: bold; - color: #1677ff; -} -.home-today-label { - font-size: 12px; - color: #888; + background: rgba(24, 142, 238, 0.1); + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; } -.home-chart { - margin-top: 8px; +.stat-content { + flex: 1; + min-width: 0; +} + +.stat-value { + font-size: 18px; + font-weight: 700; + color: var(--primary-color); + line-height: 1.2; +} + +.stat-label { + font-size: 11px; + color: #666; + margin-top: 2px; + line-height: 1.2; +} + +// 通用区域样式 +.section { + background: white; + border-radius: 12px; + padding: 16px; + margin-bottom: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + transition: all 0.3s ease; + + &:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + } +} + +.section-header { + margin-bottom: 12px; +} + +.section-title { + font-size: 14px; + font-weight: 600; + color: #333; + position: relative; + padding-left: 8px; + + &::before { + content: ''; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + width: 3px; + height: 14px; + background: var(--primary-gradient); + border-radius: 2px; + } +} + +// 场景统计网格 +.scene-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 8px; +} + +.scene-item { + text-align: center; + padding: 8px 4px; +} + +.scene-icon { + width: 36px; + height: 36px; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 6px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); +} + +.scene-value { + font-size: 16px; + font-weight: 700; + color: #333; + margin-bottom: 2px; + line-height: 1.2; +} + +.scene-label { + font-size: 10px; + color: #666; + line-height: 1.2; +} + +// 今日数据网格 +.today-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 8px; +} + +.today-item { + text-align: center; + padding: 8px 4px; + background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%); + border-radius: 8px; + transition: all 0.3s ease; + + &:hover { + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + } +} + +.today-icon { + margin-bottom: 4px; + opacity: 0.8; +} + +.today-value { + font-size: 14px; + font-weight: 700; + margin-bottom: 2px; + line-height: 1.2; +} + +.today-label { + font-size: 10px; + color: #666; + line-height: 1.2; +} + +// 图表容器 +.chart-container { width: 100%; - min-height: 100px; + min-height: 160px; + border-radius: 8px; + background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%); + padding: 12px; +} + +// 响应式设计 +@media (max-width: 375px) { + .home-page { + padding: 8px; + } + + .stats-grid { + gap: 6px; + margin-bottom: 12px; + } + + .stat-card { + padding: 10px 6px; + } + + .stat-icon { + width: 28px; + height: 28px; + } + + .stat-value { + font-size: 16px; + } + + .stat-label { + font-size: 10px; + } + + .section { + padding: 12px; + margin-bottom: 8px; + } + + .scene-grid, + .today-grid { + gap: 6px; + } + + .scene-icon { + width: 32px; + height: 32px; + } + + .scene-value { + font-size: 14px; + } + + .scene-label { + font-size: 9px; + } + + .today-value { + font-size: 12px; + } + + .today-label { + font-size: 9px; + } + + .chart-container { + min-height: 140px; + padding: 8px; + } } \ No newline at end of file diff --git a/nkebao/src/pages/home/index.tsx b/nkebao/src/pages/home/index.tsx index f63752b62..b088548a4 100644 --- a/nkebao/src/pages/home/index.tsx +++ b/nkebao/src/pages/home/index.tsx @@ -3,8 +3,12 @@ import { Card, NavBar, TabBar, Grid } from "antd-mobile"; import { AppOutline, UserOutline, - PieOutline, // 替换 BarChartOutline + PieOutline, ShopbagOutline, + FileOutline, + StarOutline, + StopOutline, + TeamOutline, } from "antd-mobile-icons"; import MeauMobile from "@/components/MeauMobile/MeauMoible"; import Layout from "@/components/Layout/Layout"; @@ -14,32 +18,56 @@ import { getPlanStats } from "./api"; const sceneStats = [ { - label: "公众号获客", + label: "公众号", value: 234, - icon: , + icon: , + gradient: "linear-gradient(135deg, #4caf50 0%, #45a049 100%)", }, { - label: "海报获客", + label: "海报", value: 167, - icon: , + icon: , + gradient: "linear-gradient(135deg, #ff9800 0%, #f57c00 100%)", }, { - label: "抖音获客", + label: "抖音", value: 156, - icon: , + icon: , + gradient: "linear-gradient(135deg, #2196f3 0%, #1976d2 100%)", }, { - label: "小红书获客", + label: "小红书", value: 89, - icon: , + icon: , + gradient: "linear-gradient(135deg, #e91e63 0%, #c2185b 100%)", }, ]; const todayStats = [ - { label: "朋友圈同步", value: 12 }, - { label: "群发任务", value: 8 }, - { label: "获客转化", value: "85%" }, - { label: "系统活跃度", value: "98%" }, + { + label: "朋友圈", + value: 12, + icon: , + color: "#ff6b35", + }, + { + label: "群发", + value: 8, + icon: , + color: "#ffd700", + }, + { + label: "转化率", + value: "85%", + icon: , + color: "#4caf50", + }, + { + label: "活跃度", + value: "98%", + icon: , + color: "#2196f3", + }, ]; const Home: React.FC = () => { @@ -52,68 +80,106 @@ const Home: React.FC = () => { return ( - 存客宝 + +
+ 存客宝 +
} footer={} >
- {/* 统计卡片 */} -
- -
设备数量
-
0
-
- -
微信号数量
-
0
-
- -
在线微信号
-
0
-
+ {/* 顶部统计卡片 */} +
+
+
+ +
+
+
0
+
设备数量
+
+
+
+
+ +
+
+
0
+
微信号
+
+
+
+
+ +
+
+
0
+
在线
+
+
{/* 场景获客统计 */} - -
场景获客统计
-
+
+
+ 获客统计 +
+
{sceneStats.map((item) => ( -
-
{item.icon}
-
{item.value}
-
{item.label}
+
+
+ {item.icon} +
+
{item.value}
+
{item.label}
))}
- +
{/* 今日数据 */} - -
今日数据
- +
+
+ 今日数据 +
+
{todayStats.map((item) => ( - -
-
{item.value}
-
{item.label}
+
+
{item.icon}
+
+ {item.value}
- +
{item.label}
+
))} - - +
+
- {/* 每日获客趋势(静态图表占位) */} - -
每日获客趋势
-
+ {/* 趋势图表 */} +
+
+ 获客趋势 +
+
- +
); From 56a2f41aef471faa325b48ad35402b3862fcc9a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Fri, 18 Jul 2025 21:56:03 +0800 Subject: [PATCH 16/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E6=96=B0=E7=89=88=E6=9C=AC=E6=A0=B7=E5=BC=8F=E5=AE=8C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/workspace/moments-sync/Detail.tsx | 135 ++++++++++++------ Cunkebao/src/types/moments-sync.ts | 18 +-- nkebao/src/pages/home/index.module.scss | 8 ++ nkebao/src/pages/home/index.tsx | 48 ++----- 4 files changed, 117 insertions(+), 92 deletions(-) diff --git a/Cunkebao/src/pages/workspace/moments-sync/Detail.tsx b/Cunkebao/src/pages/workspace/moments-sync/Detail.tsx index 283f96bef..8500a5858 100644 --- a/Cunkebao/src/pages/workspace/moments-sync/Detail.tsx +++ b/Cunkebao/src/pages/workspace/moments-sync/Detail.tsx @@ -1,19 +1,26 @@ -import React, { useState, useEffect, useCallback } from 'react'; -import { useParams, useNavigate } from 'react-router-dom'; -import { Button } from '@/components/ui/button'; -import { Card } from '@/components/ui/card'; -import { Badge } from '@/components/ui/badge'; -import { Switch } from '@/components/ui/switch'; -import { useToast } from '@/components/ui/toast'; -import { - fetchMomentsSyncTaskDetail, - toggleMomentsSyncTask, - syncMoments -} from '@/api/momentsSync'; -import { MomentsSyncTask } from '@/types/moments-sync'; -import { ChevronLeft, Edit2, RefreshCw, Clock, Database, Smartphone } from 'lucide-react'; -import Layout from '@/components/Layout'; -import BottomNav from '@/components/BottomNav'; +import React, { useState, useEffect, useCallback } from "react"; +import { useParams, useNavigate } from "react-router-dom"; +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Switch } from "@/components/ui/switch"; +import { useToast } from "@/components/ui/toast"; +import { + fetchMomentsSyncTaskDetail, + toggleMomentsSyncTask, + syncMoments, +} from "@/api/momentsSync"; +import { MomentsSyncTask } from "@/types/moments-sync"; +import { + ChevronLeft, + Edit2, + RefreshCw, + Clock, + Database, + Smartphone, +} from "lucide-react"; +import Layout from "@/components/Layout"; +import BottomNav from "@/components/BottomNav"; export default function MomentsSyncDetail() { const { id } = useParams(); @@ -31,7 +38,7 @@ export default function MomentsSyncDetail() { setTask(taskData); } } catch (error) { - toast({ title: '获取任务详情失败', variant: 'destructive' }); + toast({ title: "获取任务详情失败", variant: "destructive" }); } finally { setLoading(false); } @@ -49,9 +56,9 @@ export default function MomentsSyncDetail() { const newStatus = task.status === 1 ? 2 : 1; await toggleMomentsSyncTask(id, newStatus.toString()); setTask({ ...task, status: newStatus }); - toast({ title: newStatus === 1 ? '任务已开启' : '任务已暂停' }); + toast({ title: newStatus === 1 ? "任务已开启" : "任务已暂停" }); } catch (error) { - toast({ title: '操作失败', variant: 'destructive' }); + toast({ title: "操作失败", variant: "destructive" }); } }; @@ -59,10 +66,10 @@ export default function MomentsSyncDetail() { if (!id) return; try { await syncMoments(id); - toast({ title: '同步任务已启动' }); + toast({ title: "同步任务已启动" }); fetchTaskDetail(); // 刷新任务详情 } catch (error) { - toast({ title: '同步失败', variant: 'destructive' }); + toast({ title: "同步失败", variant: "destructive" }); } }; @@ -91,7 +98,10 @@ export default function MomentsSyncDetail() {

任务不存在

-
@@ -106,7 +116,11 @@ export default function MomentsSyncDetail() {
-

朋友圈同步任务详情

@@ -116,10 +130,10 @@ export default function MomentsSyncDetail() { 编辑 - + */}
@@ -137,7 +151,10 @@ export default function MomentsSyncDetail() { {task.status === 1 ? "进行中" : "已暂停"}
- +
@@ -154,7 +171,9 @@ export default function MomentsSyncDetail() {
内容库 - {task.contentLib || '未设置'} + + {task.contentLib || "未设置"} +
已同步 @@ -180,11 +199,15 @@ export default function MomentsSyncDetail() {
上次同步 - {task.lastSyncTime || '暂无'} + + {task.lastSyncTime || "暂无"} +
更新时间 - {task.updateTime || '暂无'} + + {task.updateTime || "暂无"} +
@@ -203,35 +226,51 @@ export default function MomentsSyncDetail() { 同步间隔 {task.syncInterval} 秒
-
- 每日最大 - {task.maxSyncPerDay || 100} 条 -
+
+ 每日最大 + + {task.maxSyncPerDay || 100} 条 + +
时间范围 - {task.timeRange.start} - {task.timeRange.end} + + {task.timeRange?.start && task.timeRange?.end + ? `${task.timeRange.start} - ${task.timeRange.end}` + : "未设置"} +
同步模式 - {task.syncMode === 'auto' ? '自动' : '手动'} + + {task.syncMode === "auto" ? "自动" : "手动"} +
-
- 今日同步 - {task.todaySyncCount || 0} 条 -
-
- 总同步数 - {task.totalSyncCount || task.syncCount || 0} 条 -
+
+ 今日同步 + + {task.todaySyncCount || 0} 条 + +
+
+ 总同步数 + + {task.totalSyncCount || task.syncCount || 0} 条 + +
目标标签 - {task.targetTags.length} 个 + + {task.targetTags?.length || 0} 个 +
内容类型 - {task.contentTypes.join(', ')} + + {task.contentTypes?.join(", ") || "未设置"} +
@@ -242,11 +281,13 @@ export default function MomentsSyncDetail() {

最近同步记录

暂无同步记录

-

任务开始执行后将显示同步记录

+

+ 任务开始执行后将显示同步记录 +

); -} \ No newline at end of file +} diff --git a/Cunkebao/src/types/moments-sync.ts b/Cunkebao/src/types/moments-sync.ts index 4b66f7d7f..ebd1d7c5a 100644 --- a/Cunkebao/src/types/moments-sync.ts +++ b/Cunkebao/src/types/moments-sync.ts @@ -26,17 +26,17 @@ export interface MomentsSyncTask { creatorName: string; syncInterval: number; maxSyncPerDay: number; - timeRange: { start: string; end: string }; - contentTypes: ContentType[]; - targetTags: string[]; + timeRange?: { start: string; end: string }; + contentTypes?: ContentType[]; + targetTags?: string[]; syncMode: SyncMode; - filterKeywords: string[]; + filterKeywords?: string[]; contentLib?: string; - devices: string[]; - friends: string[]; - todaySyncCount: number; - totalSyncCount: number; - updateTime: string; + devices?: string[]; + friends?: string[]; + todaySyncCount?: number; + totalSyncCount?: number; + updateTime?: string; config?: MomentsSyncConfig; } diff --git a/nkebao/src/pages/home/index.module.scss b/nkebao/src/pages/home/index.module.scss index 1a7b7b1b6..0d92815b1 100644 --- a/nkebao/src/pages/home/index.module.scss +++ b/nkebao/src/pages/home/index.module.scss @@ -131,6 +131,14 @@ justify-content: center; margin: 0 auto 6px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + overflow: hidden; +} + +.scene-image { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 8px; } .scene-value { diff --git a/nkebao/src/pages/home/index.tsx b/nkebao/src/pages/home/index.tsx index b088548a4..ce27e61fb 100644 --- a/nkebao/src/pages/home/index.tsx +++ b/nkebao/src/pages/home/index.tsx @@ -16,33 +16,6 @@ import style from "./index.module.scss"; import LineChart from "@/components/LineChart"; import { getPlanStats } from "./api"; -const sceneStats = [ - { - label: "公众号", - value: 234, - icon: , - gradient: "linear-gradient(135deg, #4caf50 0%, #45a049 100%)", - }, - { - label: "海报", - value: 167, - icon: , - gradient: "linear-gradient(135deg, #ff9800 0%, #f57c00 100%)", - }, - { - label: "抖音", - value: 156, - icon: , - gradient: "linear-gradient(135deg, #2196f3 0%, #1976d2 100%)", - }, - { - label: "小红书", - value: 89, - icon: , - gradient: "linear-gradient(135deg, #e91e63 0%, #c2185b 100%)", - }, -]; - const todayStats = [ { label: "朋友圈", @@ -71,9 +44,11 @@ const todayStats = [ ]; const Home: React.FC = () => { + const [sceneStats, setSceneStats] = useState([]); useEffect(() => { getPlanStats({ num: 4 }).then((res: any) => { - console.log(res); + // console.log(res); + setSceneStats(res); }); }, []); @@ -133,15 +108,16 @@ const Home: React.FC = () => {
{sceneStats.map((item) => ( -
-
- {item.icon} +
+
+ {item.name}
-
{item.value}
-
{item.label}
+
{item.allNum}
+
{item.name}
))}
From 77148dff3a947ce441ba4f377b6f1ab144c9b2fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Fri, 18 Jul 2025 22:16:04 +0800 Subject: [PATCH 17/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E6=97=A7=E9=A1=B9=E7=9B=AE=E6=8A=A2=E6=95=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/workspace/moments-sync/Detail.tsx | 11 +- .../src/pages/workspace/moments-sync/new.tsx | 200 +++++++++++------- 2 files changed, 130 insertions(+), 81 deletions(-) diff --git a/Cunkebao/src/pages/workspace/moments-sync/Detail.tsx b/Cunkebao/src/pages/workspace/moments-sync/Detail.tsx index 8500a5858..f4f6e1cc6 100644 --- a/Cunkebao/src/pages/workspace/moments-sync/Detail.tsx +++ b/Cunkebao/src/pages/workspace/moments-sync/Detail.tsx @@ -123,7 +123,7 @@ export default function MomentsSyncDetail() { > -

朋友圈同步任务详情

+

任务详情

} - footer={} >
@@ -172,12 +171,16 @@ export default function MomentsSyncDetail() {
内容库 - {task.contentLib || "未设置"} + {task.config?.contentLibraries + .map((item: any) => item.name) + .join(",")}
已同步 - {task.syncCount} 条 + + {task.config?.syncCount} 条 +
创建人 diff --git a/Cunkebao/src/pages/workspace/moments-sync/new.tsx b/Cunkebao/src/pages/workspace/moments-sync/new.tsx index b575393fc..188d321c4 100644 --- a/Cunkebao/src/pages/workspace/moments-sync/new.tsx +++ b/Cunkebao/src/pages/workspace/moments-sync/new.tsx @@ -1,14 +1,18 @@ -import React, { useState, useEffect, useCallback } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; -import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { Switch } from '@/components/ui/switch'; -import { useToast } from '@/components/ui/toast'; -import { createMomentsSyncTask, updateMomentsSyncTask, fetchMomentsSyncTaskDetail } from '@/api/momentsSync'; -import { ChevronLeft } from 'lucide-react'; -import { DeviceSelectionDialog } from '@/components/DeviceSelectionDialog'; -import { ContentLibrarySelectionDialog } from '@/components/ContentLibrarySelectionDialog'; -import { ContentType } from '@/types/moments-sync'; +import React, { useState, useEffect, useCallback } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Switch } from "@/components/ui/switch"; +import { useToast } from "@/components/ui/toast"; +import { + createMomentsSyncTask, + updateMomentsSyncTask, + fetchMomentsSyncTaskDetail, +} from "@/api/momentsSync"; +import { ChevronLeft } from "lucide-react"; +import { DeviceSelectionDialog } from "@/components/DeviceSelectionDialog"; +import { ContentLibrarySelectionDialog } from "@/components/ContentLibrarySelectionDialog"; +import { ContentType } from "@/types/moments-sync"; // 步骤指示器组件 interface StepIndicatorProps { @@ -25,36 +29,41 @@ function StepIndicator({ currentStep }: StepIndicatorProps) { return (
{/* 连线 - 背景线 */} -
- - {/* 连线 - 已完成线 */} -
- + + {/* 连线 - 已完成线 */} +
+ {/* 步骤圆圈 */} {steps.map((step, index) => (
-
{index + 1}
-
+
{step.title}
@@ -70,18 +79,19 @@ export default function NewMomentsSyncTask() { const [currentStep, setCurrentStep] = useState(1); const [loading, setLoading] = useState(false); const [deviceDialogOpen, setDeviceDialogOpen] = useState(false); - const [contentLibraryDialogOpen, setContentLibraryDialogOpen] = useState(false); + const [contentLibraryDialogOpen, setContentLibraryDialogOpen] = + useState(false); const [formData, setFormData] = useState({ - taskName: '', - startTime: '06:00', - endTime: '23:59', + taskName: "", + startTime: "06:00", + endTime: "23:59", syncCount: 5, interval: 30, // 同步间隔,单位:分钟 - accountType: 'business' as 'business' | 'personal', + accountType: "business" as "business" | "personal", enabled: true, selectedDevices: [] as string[], selectedLibraries: [] as string[], - contentTypes: ['text', 'image', 'video'] as ContentType[], + contentTypes: ["text", "image", "video"] as ContentType[], targetTags: [] as string[], filterKeywords: [] as string[], }); @@ -109,21 +119,21 @@ export default function NewMomentsSyncTask() { if (taskData) { setFormData({ taskName: taskData.name, - startTime: taskData.timeRange?.start || '06:00', - endTime: taskData.timeRange?.end || '23:59', - syncCount: taskData.maxSyncPerDay || 5, + startTime: taskData.timeRange?.start || "06:00", + endTime: taskData.timeRange?.end || "23:59", + syncCount: taskData?.config?.syncCount || 0, interval: taskData.syncInterval || 30, - accountType: taskData.syncMode === 'auto' ? 'business' : 'personal', + accountType: taskData.syncMode === "auto" ? "business" : "personal", enabled: taskData.status === 1, selectedDevices: taskData.devices || [], selectedLibraries: taskData.contentLib ? [taskData.contentLib] : [], - contentTypes: taskData.contentTypes || ['text', 'image', 'video'], + contentTypes: taskData.contentTypes || ["text", "image", "video"], targetTags: taskData.targetTags || [], filterKeywords: taskData.filterKeywords || [], }); } } catch (error) { - toast({ title: '获取任务详情失败', variant: 'destructive' }); + toast({ title: "获取任务详情失败", variant: "destructive" }); } finally { setLoading(false); } @@ -137,15 +147,15 @@ export default function NewMomentsSyncTask() { const handleComplete = async () => { if (!formData.taskName.trim()) { - toast({ title: '请输入任务名称', variant: 'destructive' }); + toast({ title: "请输入任务名称", variant: "destructive" }); return; } if (formData.selectedDevices.length === 0) { - toast({ title: '请选择设备', variant: 'destructive' }); + toast({ title: "请选择设备", variant: "destructive" }); return; } if (formData.selectedLibraries.length === 0) { - toast({ title: '请选择内容库', variant: 'destructive' }); + toast({ title: "请选择内容库", variant: "destructive" }); return; } @@ -157,10 +167,10 @@ export default function NewMomentsSyncTask() { contentLibraries: formData.selectedLibraries, syncInterval: formData.interval, syncCount: formData.syncCount, - syncType: formData.accountType === 'business' ? 1 : 2, + syncType: formData.accountType === "business" ? 1 : 2, startTime: formData.startTime, endTime: formData.endTime, - accountType: formData.accountType === 'business' ? 1 : 2, + accountType: formData.accountType === "business" ? 1 : 2, contentTypes: formData.contentTypes, targetTags: formData.targetTags, filterKeywords: formData.filterKeywords, @@ -171,15 +181,18 @@ export default function NewMomentsSyncTask() { id, ...taskData, }); - toast({ title: '更新成功' }); + toast({ title: "更新成功" }); navigate(`/workspace/moments-sync/${id}`); } else { await createMomentsSyncTask(taskData); - toast({ title: '创建成功' }); - navigate('/workspace/moments-sync'); + toast({ title: "创建成功" }); + navigate("/workspace/moments-sync"); } } catch (error) { - toast({ title: isEditMode ? '更新失败' : '创建失败', variant: 'destructive' }); + toast({ + title: isEditMode ? "更新失败" : "创建失败", + variant: "destructive", + }); } finally { setLoading(false); } @@ -202,8 +215,12 @@ export default function NewMomentsSyncTask() {
每日同步数量
- 分钟
-
设置每次发朋友圈的时间间隔
+
+ 设置每次发朋友圈的时间间隔 +
@@ -251,7 +278,9 @@ export default function NewMomentsSyncTask() { handleUpdateFormData({ startTime: e.target.value })} + onChange={(e) => + handleUpdateFormData({ startTime: e.target.value }) + } className="h-12 rounded-lg border-gray-200 text-base" />
@@ -260,7 +289,9 @@ export default function NewMomentsSyncTask() { handleUpdateFormData({ endTime: e.target.value })} + onChange={(e) => + handleUpdateFormData({ endTime: e.target.value }) + } className="h-12 rounded-lg border-gray-200 text-base" />
@@ -297,7 +328,9 @@ export default function NewMomentsSyncTask() { 是否启用 handleUpdateFormData({ enabled: checked })} + onCheckedChange={(checked) => + handleUpdateFormData({ enabled: checked }) + } className="data-[state=checked]:bg-blue-600 h-7 w-12" />
@@ -316,7 +349,12 @@ export default function NewMomentsSyncTask() {
-

新建朋友圈同步

@@ -327,7 +365,7 @@ export default function NewMomentsSyncTask() { {currentStep === 1 && renderBasicSettings()} - + {currentStep === 2 && (
setDeviceDialogOpen(true)} readOnly /> - + {formData.selectedDevices.length > 0 && (
已选设备:{formData.selectedDevices.length} 个
)} - +
-
- +
)} - + {currentStep === 3 && (
setContentLibraryDialogOpen(true)} readOnly /> - + {formData.selectedLibraries.length > 0 && (
已选内容库:{formData.selectedLibraries.length} 个
)} - +
-
- +
); -} \ No newline at end of file +} From 0532c3c1614cd63357590c70082bf1ca49442a77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Fri, 18 Jul 2025 22:41:41 +0800 Subject: [PATCH 18/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E9=A6=96=E9=A1=B5=E6=9E=84=E5=BB=BA=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/pages/home/index.module.scss | 1 - nkebao/src/pages/home/index.tsx | 103 ++++++++++++++---------- 2 files changed, 60 insertions(+), 44 deletions(-) diff --git a/nkebao/src/pages/home/index.module.scss b/nkebao/src/pages/home/index.module.scss index 0d92815b1..9aa1cc1a9 100644 --- a/nkebao/src/pages/home/index.module.scss +++ b/nkebao/src/pages/home/index.module.scss @@ -1,7 +1,6 @@ .home-page { padding: 12px; background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); - min-height: 100vh; } // 导航栏样式 diff --git a/nkebao/src/pages/home/index.tsx b/nkebao/src/pages/home/index.tsx index ce27e61fb..fd95fce7c 100644 --- a/nkebao/src/pages/home/index.tsx +++ b/nkebao/src/pages/home/index.tsx @@ -1,61 +1,75 @@ import React, { useEffect, useState } from "react"; -import { Card, NavBar, TabBar, Grid } from "antd-mobile"; +import { NavBar } from "antd-mobile"; import { AppOutline, UserOutline, - PieOutline, - ShopbagOutline, - FileOutline, - StarOutline, - StopOutline, - TeamOutline, + LoopOutline, + TravelOutline, + ClockCircleOutline, } from "antd-mobile-icons"; import MeauMobile from "@/components/MeauMobile/MeauMoible"; import Layout from "@/components/Layout/Layout"; import style from "./index.module.scss"; import LineChart from "@/components/LineChart"; -import { getPlanStats } from "./api"; - -const todayStats = [ - { - label: "朋友圈", - value: 12, - icon: , - color: "#ff6b35", - }, - { - label: "群发", - value: 8, - icon: , - color: "#ffd700", - }, - { - label: "转化率", - value: "85%", - icon: , - color: "#4caf50", - }, - { - label: "活跃度", - value: "98%", - icon: , - color: "#2196f3", - }, -]; +import { + getPlanStats, + getSevenDayStats, + getTodayStats, + getDashboard, +} from "./api"; const Home: React.FC = () => { const [sceneStats, setSceneStats] = useState([]); + const [todayStats, setTodayStats] = useState([]); + const [dashboard, setDashboard] = useState({}); + const [sevenDayStats, setSevenDayStats] = useState({}); useEffect(() => { + getDashboard().then((res: any) => { + setDashboard(res); + }); getPlanStats({ num: 4 }).then((res: any) => { - // console.log(res); setSceneStats(res); }); + getSevenDayStats().then((res: any) => { + setSevenDayStats(res); + }); + getTodayStats().then((res: any) => { + const todayStatsData = [ + { + label: "朋友圈", + value: res.momentsNum, + icon: , + color: "#ff6b35", + }, + { + label: "群发", + value: res.groupPushNum, + icon: , + color: "#ffd700", + }, + { + label: "转化率", + value: res.passRate, + icon: , + color: "#4caf50", + }, + { + label: "活跃度", + value: res.sysActive, + icon: ( + + ), + color: "#2196f3", + }, + ]; + setTodayStats(todayStatsData); + }); }, []); return ( +
存客宝
@@ -73,8 +87,9 @@ const Home: React.FC = () => { />
-
0
设备数量
+ +
{dashboard.deviceNum}
@@ -84,19 +99,21 @@ const Home: React.FC = () => { />
-
0
微信号
+
{dashboard.wechatNum}
-
-
0
在线
+
+ {dashboard.aliveWechatNum} +
@@ -151,8 +168,8 @@ const Home: React.FC = () => {
From bf89197517f396efee8a24af88ee7c55209495c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Fri, 18 Jul 2025 22:46:06 +0800 Subject: [PATCH 19/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E8=B7=AF=E7=94=B1=E5=A4=84=E7=90=86=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workspace/moments-sync/MomentsSync.tsx | 279 +++++++++++++----- .../pages/workspace/moments-sync/index.tsx | 103 ++++--- 2 files changed, 278 insertions(+), 104 deletions(-) diff --git a/Cunkebao/src/pages/workspace/moments-sync/MomentsSync.tsx b/Cunkebao/src/pages/workspace/moments-sync/MomentsSync.tsx index aa7ed1554..e68e64345 100644 --- a/Cunkebao/src/pages/workspace/moments-sync/MomentsSync.tsx +++ b/Cunkebao/src/pages/workspace/moments-sync/MomentsSync.tsx @@ -1,5 +1,5 @@ -import React, { useState, useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; +import React, { useState, useEffect } from "react"; +import { useNavigate } from "react-router-dom"; import { Plus, Search, @@ -12,21 +12,21 @@ import { Copy, ChevronLeft, Share2, -} from 'lucide-react'; -import { Card } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { Badge } from '@/components/ui/badge'; -import { Switch } from '@/components/ui/switch'; -import { useToast } from '@/components/ui/toast'; -import '@/components/Layout.css'; -import { - fetchMomentsSyncTasks, - deleteMomentsSyncTask, - toggleMomentsSyncTask, +} from "lucide-react"; +import { Card } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Badge } from "@/components/ui/badge"; +import { Switch } from "@/components/ui/switch"; +import { useToast } from "@/components/ui/toast"; +import "@/components/Layout.css"; +import { + fetchMomentsSyncTasks, + deleteMomentsSyncTask, + toggleMomentsSyncTask, copyMomentsSyncTask, - MomentsSyncTask -} from '@/api/momentsSync'; + MomentsSyncTask, +} from "@/api/momentsSync"; type CardMenuProps = { onView: () => void; @@ -51,7 +51,16 @@ function CardMenu({ onView, onEdit, onCopy, onDelete }: CardMenuProps) { return (
- {open && ( @@ -69,17 +78,106 @@ function CardMenu({ onView, onEdit, onCopy, onDelete }: CardMenuProps) { padding: 4, }} > -
{ onView(); setOpen(false); }} style={{ padding: 8, cursor: "pointer", display: "flex", alignItems: "center", borderRadius: 6, fontSize: 14, gap: 6, transition: "background .2s" }} onMouseOver={e => (e.currentTarget as HTMLDivElement).style.background="#f5f5f5"} onMouseOut={e => (e.currentTarget as HTMLDivElement).style.background=""}> - 查看 +
{ + onView(); + setOpen(false); + }} + style={{ + padding: 8, + cursor: "pointer", + display: "flex", + alignItems: "center", + borderRadius: 6, + fontSize: 14, + gap: 6, + transition: "background .2s", + }} + onMouseOver={(e) => + ((e.currentTarget as HTMLDivElement).style.background = "#f5f5f5") + } + onMouseOut={(e) => + ((e.currentTarget as HTMLDivElement).style.background = "") + } + > + + 查看
-
{ onEdit(); setOpen(false); }} style={{ padding: 8, cursor: "pointer", display: "flex", alignItems: "center", borderRadius: 6, fontSize: 14, gap: 6, transition: "background .2s" }} onMouseOver={e => (e.currentTarget as HTMLDivElement).style.background="#f5f5f5"} onMouseOut={e => (e.currentTarget as HTMLDivElement).style.background=""}> - 编辑 +
{ + onEdit(); + setOpen(false); + }} + style={{ + padding: 8, + cursor: "pointer", + display: "flex", + alignItems: "center", + borderRadius: 6, + fontSize: 14, + gap: 6, + transition: "background .2s", + }} + onMouseOver={(e) => + ((e.currentTarget as HTMLDivElement).style.background = "#f5f5f5") + } + onMouseOut={(e) => + ((e.currentTarget as HTMLDivElement).style.background = "") + } + > + + 编辑
-
{ onCopy(); setOpen(false); }} style={{ padding: 8, cursor: "pointer", display: "flex", alignItems: "center", borderRadius: 6, fontSize: 14, gap: 6, transition: "background .2s" }} onMouseOver={e => (e.currentTarget as HTMLDivElement).style.background="#f5f5f5"} onMouseOut={e => (e.currentTarget as HTMLDivElement).style.background=""}> - 复制 +
{ + onCopy(); + setOpen(false); + }} + style={{ + padding: 8, + cursor: "pointer", + display: "flex", + alignItems: "center", + borderRadius: 6, + fontSize: 14, + gap: 6, + transition: "background .2s", + }} + onMouseOver={(e) => + ((e.currentTarget as HTMLDivElement).style.background = "#f5f5f5") + } + onMouseOut={(e) => + ((e.currentTarget as HTMLDivElement).style.background = "") + } + > + + 复制
-
{ onDelete(); setOpen(false); }} style={{ padding: 8, cursor: "pointer", display: "flex", alignItems: "center", borderRadius: 6, fontSize: 14, gap: 6, color: "#e53e3e", transition: "background .2s" }} onMouseOver={e => (e.currentTarget as HTMLDivElement).style.background="#f5f5f5"} onMouseOut={e => (e.currentTarget as HTMLDivElement).style.background=""}> - 删除 +
{ + onDelete(); + setOpen(false); + }} + style={{ + padding: 8, + cursor: "pointer", + display: "flex", + alignItems: "center", + borderRadius: 6, + fontSize: 14, + gap: 6, + color: "#e53e3e", + transition: "background .2s", + }} + onMouseOver={(e) => + ((e.currentTarget as HTMLDivElement).style.background = "#f5f5f5") + } + onMouseOut={(e) => + ((e.currentTarget as HTMLDivElement).style.background = "") + } + > + + 删除
)} @@ -90,7 +188,7 @@ function CardMenu({ onView, onEdit, onCopy, onDelete }: CardMenuProps) { export default function MomentsSync() { const navigate = useNavigate(); const { toast } = useToast(); - const [searchTerm, setSearchTerm] = useState(''); + const [searchTerm, setSearchTerm] = useState(""); const [loading, setLoading] = useState(false); const [tasks, setTasks] = useState([]); @@ -100,24 +198,24 @@ export default function MomentsSync() { try { const list = await fetchMomentsSyncTasks(); // 确保数据字段与界面一致 - const mappedTasks = list.map(task => ({ + const mappedTasks = list.map((task) => ({ ...task, // 确保字段名称和格式与界面一致 status: task.status || 2, // 默认为关闭状态 deviceCount: task.deviceCount || 0, - targetGroup: task.targetGroup || '默认人群', + targetGroup: task.targetGroup || "默认人群", syncCount: task.todaySyncCount || task.syncCount || 0, - creatorName: task.creatorName || '未知', - lastSyncTime: task.lastSyncTime || '暂无', - createTime: task.createTime || '未知', + creatorName: task.creatorName || "未知", + lastSyncTime: task.lastSyncTime || "暂无", + createTime: task.createTime || "未知", syncInterval: task.syncInterval || 30, maxSyncPerDay: task.maxSyncPerDay || 100, - timeRange: task.timeRange || { start: '08:00', end: '22:00' }, - contentTypes: task.contentTypes || ['text', 'image', 'video'], + timeRange: task.timeRange || { start: "08:00", end: "22:00" }, + contentTypes: task.contentTypes || ["text", "image", "video"], targetTags: task.targetTags || [], - syncMode: task.syncMode || 'auto', + syncMode: task.syncMode || "auto", filterKeywords: task.filterKeywords || [], - contentLib: task.config?.contentLibraryNames?.join(',') || '默认内容库' + contentLib: task.config?.contentLibraryNames?.join(",") || "默认内容库", })); setTasks(mappedTasks); } catch (error) { @@ -137,17 +235,25 @@ export default function MomentsSync() { if (!taskToDelete) return; if (!window.confirm(`确定要删除"${taskToDelete.name}"吗?`)) return; - + try { const response = await deleteMomentsSyncTask(id); if (response.code === 200) { toast({ title: "删除成功" }); fetchTasks(); } else { - toast({ title: "删除失败", description: response.msg || "请稍后重试", variant: "destructive" }); + toast({ + title: "删除失败", + description: response.msg || "请稍后重试", + variant: "destructive", + }); } } catch (error) { - toast({ title: "删除失败", description: "请稍后重试", variant: "destructive" }); + toast({ + title: "删除失败", + description: "请稍后重试", + variant: "destructive", + }); } }; @@ -166,18 +272,26 @@ export default function MomentsSync() { toast({ title: "复制成功" }); fetchTasks(); } else { - toast({ title: "复制失败", description: response.msg || "请稍后重试", variant: "destructive" }); + toast({ + title: "复制失败", + description: response.msg || "请稍后重试", + variant: "destructive", + }); } } catch (error) { - toast({ title: "复制失败", description: "请稍后重试", variant: "destructive" }); + toast({ + title: "复制失败", + description: "请稍后重试", + variant: "destructive", + }); } }; const toggleTaskStatus = async (id: string, status: number) => { // 先更新本地状态 const newStatus = (status === 1 ? 2 : 1) as 1 | 2; - setTasks(prevTasks => - prevTasks.map(task => + setTasks((prevTasks) => + prevTasks.map((task) => task.id === id ? { ...task, status: newStatus } : task ) ); @@ -189,40 +303,48 @@ export default function MomentsSync() { // 成功时不刷新列表,保持本地状态 } else { // 请求失败,回退本地状态 - setTasks(prevTasks => - prevTasks.map(task => + setTasks((prevTasks) => + prevTasks.map((task) => task.id === id ? { ...task, status: status as 1 | 2 } : task ) ); - toast({ title: "操作失败", description: response.msg || "请稍后重试", variant: "destructive" }); + toast({ + title: "操作失败", + description: response.msg || "请稍后重试", + variant: "destructive", + }); } } catch (error) { // 请求异常,回退本地状态 - setTasks(prevTasks => - prevTasks.map(task => + setTasks((prevTasks) => + prevTasks.map((task) => task.id === id ? { ...task, status: status as 1 | 2 } : task ) ); - toast({ title: "操作失败", description: "请稍后重试", variant: "destructive" }); + toast({ + title: "操作失败", + description: "请稍后重试", + variant: "destructive", + }); } }; const handleCreateNew = () => { - navigate('/workspace/moments-sync/new'); + navigate("/workspace/moments-sync/new"); }; const filteredTasks = tasks.filter((task) => - task.name.toLowerCase().includes(searchTerm.toLowerCase()), + task.name.toLowerCase().includes(searchTerm.toLowerCase()) ); const getStatusText = (status: number) => { switch (status) { case 1: - return '进行中'; + return "进行中"; case 2: - return '已暂停'; + return "已暂停"; default: - return '未知'; + return "未知"; } }; @@ -231,13 +353,18 @@ export default function MomentsSync() {
-

朋友圈同步

@@ -246,14 +373,19 @@ export default function MomentsSync() {
- setSearchTerm(e.target.value)} />
-
@@ -94,26 +104,45 @@ export default function MomentsSyncPage() { ) : filteredTasks.length === 0 ? ( 暂无任务 ) : ( - filteredTasks.map(task => ( - + filteredTasks.map((task) => ( +
{task.name} - - {task.status === 1 ? '进行中' : '已暂停'} + + {task.status === 1 ? "进行中" : "已暂停"}
-
上次同步:{task.lastSyncTime || '无'}
-
已同步:{task.syncCount || 0} 条
+
+ 上次同步:{task.lastSyncTime || "无"} +
+
+ 已同步:{task.syncCount || 0} 条 +
- -
@@ -123,4 +152,4 @@ export default function MomentsSyncPage() {
); -} \ No newline at end of file +} From b2bfb2338a86eec085ece8057080333993bbd568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Fri, 18 Jul 2025 22:46:25 +0800 Subject: [PATCH 20/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cunkebao/src/pages/workspace/moments-sync/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/Cunkebao/src/pages/workspace/moments-sync/index.tsx b/Cunkebao/src/pages/workspace/moments-sync/index.tsx index 6c71067f5..5b6049a52 100644 --- a/Cunkebao/src/pages/workspace/moments-sync/index.tsx +++ b/Cunkebao/src/pages/workspace/moments-sync/index.tsx @@ -5,7 +5,6 @@ import PageHeader from "@/components/PageHeader"; import { Card } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; -import { Progress } from "@/components/ui/progress"; import { Badge } from "@/components/ui/badge"; import { RefreshCw, Search, RefreshCw as SyncIcon, Eye } from "lucide-react"; import { From 9630389e2214c232ef17824b62a31e16693c393a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Sat, 19 Jul 2025 09:27:49 +0800 Subject: [PATCH 21/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E9=A6=96=E9=A1=B5=E6=90=9E=E5=AE=9A=EF=BC=8C=E5=BC=80?= =?UTF-8?q?=E5=A7=8B=E5=9C=BA=E6=99=AF=E8=8E=B7=E5=AE=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/src/components/Layout/layout.module.scss | 1 + nkebao/src/pages/home/index.module.scss | 2 +- nkebao/src/pages/home/index.tsx | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/nkebao/src/components/Layout/layout.module.scss b/nkebao/src/components/Layout/layout.module.scss index e6e6cb0b4..796b26365 100644 --- a/nkebao/src/components/Layout/layout.module.scss +++ b/nkebao/src/components/Layout/layout.module.scss @@ -2,6 +2,7 @@ display: flex; height: 100vh; flex-direction: column; + background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); } .container main { diff --git a/nkebao/src/pages/home/index.module.scss b/nkebao/src/pages/home/index.module.scss index 9aa1cc1a9..5ead01a4c 100644 --- a/nkebao/src/pages/home/index.module.scss +++ b/nkebao/src/pages/home/index.module.scss @@ -1,6 +1,6 @@ .home-page { padding: 12px; - background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); + } // 导航栏样式 diff --git a/nkebao/src/pages/home/index.tsx b/nkebao/src/pages/home/index.tsx index fd95fce7c..d7f2002ab 100644 --- a/nkebao/src/pages/home/index.tsx +++ b/nkebao/src/pages/home/index.tsx @@ -36,25 +36,25 @@ const Home: React.FC = () => { getTodayStats().then((res: any) => { const todayStatsData = [ { - label: "朋友圈", + label: "同步朋友圈", value: res.momentsNum, icon: , color: "#ff6b35", }, { - label: "群发", + label: "群发任务", value: res.groupPushNum, icon: , color: "#ffd700", }, { - label: "转化率", + label: "获客转化率", value: res.passRate, icon: , color: "#4caf50", }, { - label: "活跃度", + label: "系统活跃度", value: res.sysActive, icon: ( From b128410346dfc2d3e0fc598a1060615e6807477b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Sat, 19 Jul 2025 11:40:02 +0800 Subject: [PATCH 22/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E5=AE=9A=E7=89=88=E6=9C=AC=EF=BC=8C=E5=87=86=E5=A4=87?= =?UTF-8?q?=E5=A4=A7=E6=89=B9=E9=87=8F=E8=BF=81=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/ANTD_MOBILE_ICONS.md | 269 +++++++++++ nkebao/ROUTE_MIGRATION.md | 140 ++++++ nkebao/WORKSPACE_MIGRATION.md | 159 ++++++ .../src/components/MeauMobile/MeauMoible.tsx | 4 +- nkebao/src/components/PlaceholderPage.tsx | 56 +++ nkebao/src/pages/About.tsx | 7 - .../pages/contact-import/ContactImport.tsx | 14 + nkebao/src/pages/content/Content.tsx | 10 + nkebao/src/pages/content/NewContent.tsx | 8 + nkebao/src/pages/content/materials/List.tsx | 10 + nkebao/src/pages/content/materials/New.tsx | 8 + nkebao/src/pages/devices/DeviceDetail.tsx | 30 ++ nkebao/src/pages/devices/Devices.tsx | 37 ++ nkebao/src/pages/login/login.module.scss | 457 ++---------------- nkebao/src/pages/login/login.tsx | 335 ++----------- nkebao/src/pages/orders/Orders.tsx | 8 + nkebao/src/pages/plans/PlanDetail.tsx | 8 + nkebao/src/pages/plans/Plans.tsx | 10 + nkebao/src/pages/profile/Profile.tsx | 8 + nkebao/src/pages/scenarios/ScenarioList.tsx | 8 + nkebao/src/pages/scenarios/Scenarios.tsx | 10 + nkebao/src/pages/scenarios/new/page.tsx | 8 + nkebao/src/pages/scene/api.ts | 28 +- nkebao/src/pages/scene/index.module.scss | 358 +++++++++++--- nkebao/src/pages/scene/index.tsx | 272 ++++++----- nkebao/src/pages/traffic-pool/TrafficPool.tsx | 10 + .../pages/traffic-pool/TrafficPoolDetail.tsx | 8 + .../wechat-accounts/WechatAccountDetail.tsx | 30 ++ .../pages/wechat-accounts/WechatAccounts.tsx | 37 ++ .../src/pages/workspace/Workspace.module.scss | 185 +++++++ nkebao/src/pages/workspace/Workspace.tsx | 254 ++++++++++ .../workspace/ai-assistant/AIAssistant.tsx | 8 + .../pages/workspace/auto-group/AutoGroup.tsx | 10 + .../src/pages/workspace/auto-group/Detail.tsx | 8 + .../pages/workspace/auto-like/AutoLike.tsx | 38 ++ .../workspace/auto-like/AutoLikeDetail.tsx | 8 + .../pages/workspace/auto-like/NewAutoLike.tsx | 30 ++ .../pages/workspace/group-push/GroupPush.tsx | 10 + nkebao/src/pages/workspace/group-push/new.tsx | 8 + .../src/pages/{work => workspace/main}/api.ts | 0 .../main}/index.module.scss | 0 .../pages/{work => workspace/main}/index.tsx | 0 .../pages/workspace/moments-sync/Detail.tsx | 8 + .../workspace/moments-sync/MomentsSync.tsx | 14 + .../src/pages/workspace/moments-sync/new.tsx | 8 + .../workspace/traffic-distribution/Detail.tsx | 8 + .../traffic-distribution/NewDistribution.tsx | 8 + .../TrafficDistribution.tsx | 10 + nkebao/src/router/config.ts | 171 +++++++ nkebao/src/router/module/auth.tsx | 11 + nkebao/src/router/module/content.tsx | 39 ++ nkebao/src/router/module/devices.tsx | 17 + nkebao/src/router/module/index.tsx | 11 +- nkebao/src/router/module/other.tsx | 40 ++ nkebao/src/router/module/scenarios.tsx | 38 ++ nkebao/src/router/module/traffic-pool.tsx | 17 + nkebao/src/router/module/wechat-accounts.tsx | 17 + nkebao/src/router/module/workspace.tsx | 145 ++++++ nkebao/src/styles/global.scss | 11 +- 59 files changed, 2565 insertions(+), 914 deletions(-) create mode 100644 nkebao/ANTD_MOBILE_ICONS.md create mode 100644 nkebao/ROUTE_MIGRATION.md create mode 100644 nkebao/WORKSPACE_MIGRATION.md create mode 100644 nkebao/src/components/PlaceholderPage.tsx delete mode 100644 nkebao/src/pages/About.tsx create mode 100644 nkebao/src/pages/contact-import/ContactImport.tsx create mode 100644 nkebao/src/pages/content/Content.tsx create mode 100644 nkebao/src/pages/content/NewContent.tsx create mode 100644 nkebao/src/pages/content/materials/List.tsx create mode 100644 nkebao/src/pages/content/materials/New.tsx create mode 100644 nkebao/src/pages/devices/DeviceDetail.tsx create mode 100644 nkebao/src/pages/devices/Devices.tsx create mode 100644 nkebao/src/pages/orders/Orders.tsx create mode 100644 nkebao/src/pages/plans/PlanDetail.tsx create mode 100644 nkebao/src/pages/plans/Plans.tsx create mode 100644 nkebao/src/pages/profile/Profile.tsx create mode 100644 nkebao/src/pages/scenarios/ScenarioList.tsx create mode 100644 nkebao/src/pages/scenarios/Scenarios.tsx create mode 100644 nkebao/src/pages/scenarios/new/page.tsx create mode 100644 nkebao/src/pages/traffic-pool/TrafficPool.tsx create mode 100644 nkebao/src/pages/traffic-pool/TrafficPoolDetail.tsx create mode 100644 nkebao/src/pages/wechat-accounts/WechatAccountDetail.tsx create mode 100644 nkebao/src/pages/wechat-accounts/WechatAccounts.tsx create mode 100644 nkebao/src/pages/workspace/Workspace.module.scss create mode 100644 nkebao/src/pages/workspace/Workspace.tsx create mode 100644 nkebao/src/pages/workspace/ai-assistant/AIAssistant.tsx create mode 100644 nkebao/src/pages/workspace/auto-group/AutoGroup.tsx create mode 100644 nkebao/src/pages/workspace/auto-group/Detail.tsx create mode 100644 nkebao/src/pages/workspace/auto-like/AutoLike.tsx create mode 100644 nkebao/src/pages/workspace/auto-like/AutoLikeDetail.tsx create mode 100644 nkebao/src/pages/workspace/auto-like/NewAutoLike.tsx create mode 100644 nkebao/src/pages/workspace/group-push/GroupPush.tsx create mode 100644 nkebao/src/pages/workspace/group-push/new.tsx rename nkebao/src/pages/{work => workspace/main}/api.ts (100%) rename nkebao/src/pages/{work => workspace/main}/index.module.scss (100%) rename nkebao/src/pages/{work => workspace/main}/index.tsx (100%) create mode 100644 nkebao/src/pages/workspace/moments-sync/Detail.tsx create mode 100644 nkebao/src/pages/workspace/moments-sync/MomentsSync.tsx create mode 100644 nkebao/src/pages/workspace/moments-sync/new.tsx create mode 100644 nkebao/src/pages/workspace/traffic-distribution/Detail.tsx create mode 100644 nkebao/src/pages/workspace/traffic-distribution/NewDistribution.tsx create mode 100644 nkebao/src/pages/workspace/traffic-distribution/TrafficDistribution.tsx create mode 100644 nkebao/src/router/config.ts create mode 100644 nkebao/src/router/module/auth.tsx create mode 100644 nkebao/src/router/module/content.tsx create mode 100644 nkebao/src/router/module/devices.tsx create mode 100644 nkebao/src/router/module/other.tsx create mode 100644 nkebao/src/router/module/scenarios.tsx create mode 100644 nkebao/src/router/module/traffic-pool.tsx create mode 100644 nkebao/src/router/module/wechat-accounts.tsx create mode 100644 nkebao/src/router/module/workspace.tsx diff --git a/nkebao/ANTD_MOBILE_ICONS.md b/nkebao/ANTD_MOBILE_ICONS.md new file mode 100644 index 000000000..4c03bba63 --- /dev/null +++ b/nkebao/ANTD_MOBILE_ICONS.md @@ -0,0 +1,269 @@ +# Ant Design Mobile 图标完整列表 + +## 📋 图标分类 + +### 🔗 基础图标 (Outline) +- `AddOutline` - 添加 +- `AppOutline` - 应用 +- `ArrowDownOutline` - 向下箭头 +- `ArrowLeftOutline` - 向左箭头 +- `ArrowRightOutline` - 向右箭头 +- `ArrowUpOutline` - 向上箭头 +- `AudioOutline` - 音频 +- `BarChartOutline` - 柱状图 +- `BellOutline` - 铃铛 +- `BookOutline` - 书本 +- `CalendarOutline` - 日历 +- `CameraOutline` - 相机 +- `CheckOutline` - 勾选 +- `ClockCircleOutline` - 时钟 +- `CloseOutline` - 关闭 +- `DeleteOutline` - 删除 +- `DownlandOutline` - 下载 +- `EditSOutline` - 编辑 +- `EllipsisOutline` - 省略号 +- `EyeOutline` - 眼睛 +- `FileOutline` - 文件 +- `FilterOutline` - 筛选 +- `FireOutline` - 火焰 +- `FlagOutline` - 旗帜 +- `FolderOutline` - 文件夹 +- `GiftOutline` - 礼物 +- `HeartOutline` - 心形 +- `HomeOutline` - 首页 +- `ImageOutline` - 图片 +- `LeftOutline` - 左箭头 +- `LikeOutline` - 点赞 +- `LinkOutline` - 链接 +- `LocationOutline` - 位置 +- `LockOutline` - 锁定 +- `MailOutline` - 邮件 +- `MessageOutline` - 消息 +- `MoreOutline` - 更多 +- `PayCircleOutline` - 支付 +- `PhoneOutline` - 电话 +- `PictureOutline` - 图片 +- `PlayOutline` - 播放 +- `PlusOutline` - 加号 +- `RightOutline` - 右箭头 +- `ScanOutline` - 扫描 +- `SearchOutline` - 搜索 +- `SendOutline` - 发送 +- `SettingOutline` - 设置 +- `ShareOutline` - 分享 +- `ShopbagOutline` - 购物袋 +- `StarOutline` - 星星 +- `TeamOutline` - 团队 +- `ThumbsUpOutline` - 点赞 +- `TimeOutline` - 时间 +- `UnlockOutline` - 解锁 +- `UserOutline` - 用户 +- `VideoOutline` - 视频 +- `VoiceOutline` - 语音 + +### 🎨 填充图标 (Fill) +- `AddCircleFill` - 添加圆形 +- `AppFill` - 应用填充 +- `AudioFill` - 音频填充 +- `BellFill` - 铃铛填充 +- `BookFill` - 书本填充 +- `CalendarFill` - 日历填充 +- `CameraFill` - 相机填充 +- `CheckCircleFill` - 勾选圆形 +- `ClockCircleFill` - 时钟圆形 +- `CloseCircleFill` - 关闭圆形 +- `DeleteFill` - 删除填充 +- `DownlandFill` - 下载填充 +- `EditSFill` - 编辑填充 +- `EyeFill` - 眼睛填充 +- `FileFill` - 文件填充 +- `FilterFill` - 筛选填充 +- `FireFill` - 火焰填充 +- `FlagFill` - 旗帜填充 +- `FolderFill` - 文件夹填充 +- `GiftFill` - 礼物填充 +- `HeartFill` - 心形填充 +- `HomeFill` - 首页填充 +- `ImageFill` - 图片填充 +- `LeftFill` - 左箭头填充 +- `LikeFill` - 点赞填充 +- `LinkFill` - 链接填充 +- `LocationFill` - 位置填充 +- `LockFill` - 锁定填充 +- `MailFill` - 邮件填充 +- `MessageFill` - 消息填充 +- `MoreFill` - 更多填充 +- `PayCircleFill` - 支付圆形 +- `PhoneFill` - 电话填充 +- `PictureFill` - 图片填充 +- `PlayFill` - 播放填充 +- `PlusCircleFill` - 加号圆形 +- `RightFill` - 右箭头填充 +- `ScanFill` - 扫描填充 +- `SearchFill` - 搜索填充 +- `SendFill` - 发送填充 +- `SettingFill` - 设置填充 +- `ShareFill` - 分享填充 +- `ShopbagFill` - 购物袋填充 +- `StarFill` - 星星填充 +- `TeamFill` - 团队填充 +- `ThumbsUpFill` - 点赞填充 +- `TimeFill` - 时间填充 +- `UnlockFill` - 解锁填充 +- `UserFill` - 用户填充 +- `VideoFill` - 视频填充 +- `VoiceFill` - 语音填充 + +### 📊 图表图标 +- `BarChartOutline` - 柱状图 +- `LineChartOutline` - 折线图 +- `PieOutline` - 饼图 +- `AreaOutline` - 面积图 +- `RadarOutline` - 雷达图 +- `ScatterOutline` - 散点图 + +### 🔧 功能图标 +- `AddOutline` - 添加 +- `DeleteOutline` - 删除 +- `EditSOutline` - 编辑 +- `SearchOutline` - 搜索 +- `FilterOutline` - 筛选 +- `SettingOutline` - 设置 +- `MoreOutline` - 更多 +- `CloseOutline` - 关闭 +- `CheckOutline` - 勾选 + +### 📱 通信图标 +- `MessageOutline` - 消息 +- `PhoneOutline` - 电话 +- `MailOutline` - 邮件 +- `SendOutline` - 发送 +- `VoiceOutline` - 语音 +- `VideoOutline` - 视频 + +### 👥 用户图标 +- `UserOutline` - 用户 +- `TeamOutline` - 团队 +- `HeartOutline` - 心形 +- `LikeOutline` - 点赞 +- `ThumbsUpOutline` - 点赞 + +### 🛍️ 商业图标 +- `ShopbagOutline` - 购物袋 +- `PayCircleOutline` - 支付 +- `GiftOutline` - 礼物 +- `StarOutline` - 星星 + +### 📁 文件图标 +- `FileOutline` - 文件 +- `FolderOutline` - 文件夹 +- `ImageOutline` - 图片 +- `PictureOutline` - 图片 +- `CameraOutline` - 相机 + +### 🎵 媒体图标 +- `AudioOutline` - 音频 +- `VideoOutline` - 视频 +- `PlayOutline` - 播放 +- `VoiceOutline` - 语音 + +### 📅 时间图标 +- `CalendarOutline` - 日历 +- `ClockCircleOutline` - 时钟 +- `TimeOutline` - 时间 + +### 🔒 安全图标 +- `LockOutline` - 锁定 +- `UnlockOutline` - 解锁 +- `EyeOutline` - 眼睛 + +### 📍 位置图标 +- `LocationOutline` - 位置 +- `FlagOutline` - 旗帜 + +### 🔗 导航图标 +- `ArrowUpOutline` - 向上箭头 +- `ArrowDownOutline` - 向下箭头 +- `ArrowLeftOutline` - 向左箭头 +- `ArrowRightOutline` - 向右箭头 +- `LeftOutline` - 左箭头 +- `RightOutline` - 右箭头 + +## 🎨 使用示例 + +### 基础用法 +```tsx +import { AddOutline, UserOutline, SettingOutline } from 'antd-mobile-icons'; + +// 在组件中使用 + + + +``` + +### 自定义样式 +```tsx +import { HeartOutline } from 'antd-mobile-icons'; + +// 自定义颜色和大小 + +``` + +### 在项目中的使用 +```tsx +// 工作台页面示例 +const features = [ + { + icon: , + name: "自动点赞" + }, + { + icon: , + name: "AI对话助手" + }, + { + icon: , + name: "AI数据分析" + } +]; +``` + +## 📝 注意事项 + +1. **图标命名规则**: 所有图标都以 `Outline` 或 `Fill` 结尾 +2. **导入方式**: 从 `antd-mobile-icons` 包中导入 +3. **样式定制**: 可以通过 `style` 属性自定义颜色、大小等 +4. **响应式**: 图标支持响应式设计,可以配合 CSS 媒体查询使用 +5. **可访问性**: 图标支持 `aria-label` 等无障碍属性 + +## 🔗 相关资源 + +- [Ant Design Mobile 官方文档](https://mobile.ant.design/) +- [图标组件文档](https://mobile.ant.design/zh/components/icon) +- [图标库 GitHub](https://github.com/ant-design/ant-design-mobile) + +## 📊 项目中已使用的图标 + +### 工作台页面 +- `ThumbsUpOutline` - 自动点赞 +- `MessageOutline` - AI对话助手 +- `SendOutline` - 群消息推送 +- `TeamOutline` - 自动建群 +- `ShareOutline` - 流量分发 +- `AppOutline` - AI策略优化 +- `BarChartOutline` - AI数据分析 +- `LineChartOutline` - AI销售预测 +- `ClockCircleOutline` - 朋友圈同步 + +### 底部导航 +- `AppOutline` - 首页 +- `ShopbagOutline` - 场景获客 +- `PieOutline` - 工作台 +- `UserOutline` - 我的 + +### 其他页面 +- `AddOutline` - 添加按钮 +- `UserAddOutline` - 用户添加 +- `BellOutline` - 通知 +- `LoopOutline` - 循环/同步 +- `TravelOutline` - 旅行/任务 \ No newline at end of file diff --git a/nkebao/ROUTE_MIGRATION.md b/nkebao/ROUTE_MIGRATION.md new file mode 100644 index 000000000..9f1794832 --- /dev/null +++ b/nkebao/ROUTE_MIGRATION.md @@ -0,0 +1,140 @@ +# 路由迁移总结 + +## 📋 迁移概述 + +已成功将旧项目 @Cunkebao 的路由结构迁移到新项目 @nkebao,并进行了合理的文件拆分和归类。 + +## 🗂️ 路由模块结构 + +### 1. 基础路由模块 (`auth.tsx`) +- `/login` - 登录页面 + +### 2. 设备管理模块 (`devices.tsx`) +- `/devices` - 设备列表 +- `/devices/:id` - 设备详情 + +### 3. 微信号管理模块 (`wechat-accounts.tsx`) +- `/wechat-accounts` - 微信号列表 +- `/wechat-accounts/:id` - 微信号详情 + +### 4. 工作台模块 (`workspace.tsx`) +- `/workspace` - 工作台主页 +- `/workspace/auto-like` - 自动点赞 +- `/workspace/auto-like/new` - 新建自动点赞 +- `/workspace/auto-like/:id` - 自动点赞详情 +- `/workspace/auto-like/:id/edit` - 编辑自动点赞 +- `/workspace/auto-group` - 自动分组 +- `/workspace/auto-group/:id` - 自动分组详情 +- `/workspace/group-push` - 群发推送 +- `/workspace/group-push/new` - 新建群发推送 +- `/workspace/group-push/:id` - 群发推送详情 +- `/workspace/group-push/:id/edit` - 编辑群发推送 +- `/workspace/moments-sync` - 朋友圈同步 +- `/workspace/moments-sync/new` - 新建朋友圈同步 +- `/workspace/moments-sync/:id` - 朋友圈同步详情 +- `/workspace/moments-sync/edit/:id` - 编辑朋友圈同步 +- `/workspace/ai-assistant` - AI助手 +- `/workspace/traffic-distribution` - 流量分发 +- `/workspace/traffic-distribution/new` - 新建流量分发 +- `/workspace/traffic-distribution/edit/:id` - 编辑流量分发 +- `/workspace/traffic-distribution/:id` - 流量分发详情 + +### 5. 场景管理模块 (`scenarios.tsx`) +- `/scenarios` - 场景列表 +- `/scenarios/new` - 新建场景 +- `/scenarios/new/:scenarioId` - 新建场景(带场景ID) +- `/scenarios/edit/:planId` - 编辑场景 +- `/scenarios/list/:scenarioId/:scenarioName` - 场景列表详情 + +### 6. 内容管理模块 (`content.tsx`) +- `/content` - 内容管理 +- `/content/new` - 新建内容 +- `/content/edit/:id` - 编辑内容 +- `/content/materials/:id` - 素材管理 +- `/content/materials/new/:id` - 新建素材 +- `/content/materials/edit/:id/:materialId` - 编辑素材 + +### 7. 流量池模块 (`traffic-pool.tsx`) +- `/traffic-pool` - 流量池列表 +- `/traffic-pool/:id` - 流量池详情 + +### 8. 其他功能模块 (`other.tsx`) +- `/profile` - 个人中心 +- `/plans` - 计划管理 +- `/plans/:planId` - 计划详情 +- `/orders` - 订单管理 +- `/contact-import` - 联系人导入 + +## 🔧 技术实现 + +### 路由配置 +- 使用 React Router DOM v6 +- 支持动态路由参数 +- 统一的权限控制 +- 模块化路由管理 + +### 权限控制 +- 每个路由都配置了 `auth` 属性 +- 支持角色权限控制 +- 自动权限检查 + +### 组件结构 +- 使用 Layout 组件统一布局 +- 使用 MeauMobile 组件作为底部导航 +- 创建了 PlaceholderPage 通用占位组件 + +## 📁 文件结构 + +``` +src/ +├── router/ +│ ├── module/ +│ │ ├── index.tsx # 主路由文件 +│ │ ├── auth.tsx # 认证路由 +│ │ ├── devices.tsx # 设备管理路由 +│ │ ├── wechat-accounts.tsx # 微信号管理路由 +│ │ ├── workspace.tsx # 工作台路由 +│ │ ├── scenarios.tsx # 场景管理路由 +│ │ ├── content.tsx # 内容管理路由 +│ │ ├── traffic-pool.tsx # 流量池路由 +│ │ └── other.tsx # 其他功能路由 +│ ├── index.tsx # 路由入口 +│ ├── permissionRoute.tsx # 权限路由组件 +│ └── config.ts # 路由配置 +├── pages/ +│ ├── login/ # 登录页面 +│ ├── devices/ # 设备管理页面 +│ ├── wechat-accounts/ # 微信号管理页面 +│ ├── workspace/ # 工作台页面 +│ ├── scenarios/ # 场景管理页面 +│ ├── content/ # 内容管理页面 +│ ├── traffic-pool/ # 流量池页面 +│ ├── profile/ # 个人中心页面 +│ ├── plans/ # 计划管理页面 +│ ├── orders/ # 订单管理页面 +│ └── contact-import/ # 联系人导入页面 +└── components/ + └── PlaceholderPage.tsx # 通用占位页面组件 +``` + +## 🎯 主要特性 + +1. **模块化设计**: 按功能模块拆分路由,便于维护 +2. **权限控制**: 统一的权限管理机制 +3. **类型安全**: 完整的 TypeScript 类型定义 +4. **响应式设计**: 适配移动端和桌面端 +5. **代码复用**: 使用通用组件减少重复代码 + +## 🚀 下一步计划 + +1. 实现具体的页面功能 +2. 完善权限控制逻辑 +3. 添加路由懒加载 +4. 优化页面性能 +5. 添加路由守卫 + +## 📝 注意事项 + +- 所有页面目前都是占位页面,需要根据业务需求逐步实现 +- 路由权限控制需要根据实际业务逻辑调整 +- 建议按照模块优先级逐步开发页面功能 \ No newline at end of file diff --git a/nkebao/WORKSPACE_MIGRATION.md b/nkebao/WORKSPACE_MIGRATION.md new file mode 100644 index 000000000..436747bff --- /dev/null +++ b/nkebao/WORKSPACE_MIGRATION.md @@ -0,0 +1,159 @@ +# 工作台页面迁移总结 + +## 📋 迁移概述 + +已成功将旧项目 @Cunkebao 的工作台页面迁移到新项目 @nkebao,并进行了技术栈转换和样式优化。 + +## 🔄 技术栈转换 + +### 旧项目技术栈 +- React + TypeScript +- Lucide React 图标库 +- Tailwind CSS +- React Router DOM v6 +- 自定义UI组件库 + +### 新项目技术栈 +- React + TypeScript +- Ant Design Mobile +- SCSS Modules +- React Router DOM v6 +- 统一的Layout组件 + +## 📱 页面结构 + +### 主要功能模块 + +1. **任务统计卡片** + - 总任务数(带进度条) + - 今日任务(带活跃度指标) + +2. **常用功能** + - 自动点赞(New标签) + - 朋友圈同步 + - 群消息推送 + - 自动建群 + - 流量分发 + - AI对话助手(New标签) + +3. **AI智能助手** + - AI数据分析(New标签) + - AI策略优化(New标签) + - AI销售预测 + +## 🎨 设计优化 + +### 视觉改进 +- 使用Ant Design Mobile组件,保持移动端一致性 +- 采用SCSS Modules进行样式管理 +- 统一的颜色主题和间距规范 +- 响应式设计,适配不同屏幕尺寸 + +### 交互优化 +- 卡片悬停效果 +- 统一的图标样式和颜色 +- 清晰的层级结构 +- 流畅的页面跳转 + +## 📁 文件结构 + +``` +src/pages/workspace/ +├── Workspace.tsx # 工作台主页 +├── Workspace.module.scss # 工作台样式 +├── auto-like/ # 自动点赞模块 +├── auto-group/ # 自动分组模块 +├── group-push/ # 群发推送模块 +├── moments-sync/ # 朋友圈同步模块 +├── ai-assistant/ # AI助手模块 +└── traffic-distribution/ # 流量分发模块 +``` + +## 🔗 路由配置 + +### 主要路由 +- `/workspace` - 工作台主页 +- `/workspace/auto-like` - 自动点赞 +- `/workspace/group-push` - 群发推送 +- `/workspace/moments-sync` - 朋友圈同步 +- `/workspace/auto-group` - 自动分组 +- `/workspace/traffic-distribution` - 流量分发 +- `/workspace/ai-assistant` - AI助手 + +### AI功能路由 +- `/workspace/ai-analyzer` - AI数据分析 +- `/workspace/ai-strategy` - AI策略优化 +- `/workspace/ai-forecast` - AI销售预测 + +## 🎯 功能特性 + +### 数据展示 +- 实时任务统计 +- 进度条可视化 +- 活跃度指标 +- 功能分类展示 + +### 导航优化 +- 底部导航集成 +- 路由自动激活 +- 页面跳转优化 +- 返回按钮处理 + +### 用户体验 +- 加载状态处理 +- 错误边界处理 +- 响应式布局 +- 触摸友好设计 + +## 🔧 技术实现 + +### 组件化设计 +- 使用Layout组件统一布局 +- 复用PlaceholderPage组件 +- 模块化的功能卡片 +- 统一的样式规范 + +### 状态管理 +- 模拟数据展示 +- 路由状态同步 +- 组件生命周期管理 +- 性能优化 + +### 样式系统 +- SCSS Modules避免样式冲突 +- CSS变量统一主题 +- 响应式断点设计 +- 移动端优化 + +## 🚀 下一步计划 + +1. **数据集成** + - 连接真实API接口 + - 实现数据获取和更新 + - 添加加载状态 + - 错误处理机制 + +2. **功能完善** + - 实现具体功能页面 + - 添加表单验证 + - 完善交互逻辑 + - 优化用户体验 + +3. **性能优化** + - 路由懒加载 + - 组件代码分割 + - 图片资源优化 + - 缓存策略 + +4. **测试完善** + - 单元测试 + - 集成测试 + - 端到端测试 + - 性能测试 + +## 📝 注意事项 + +- 所有功能页面目前使用占位组件 +- 需要根据实际业务需求调整数据展示 +- 建议按照用户使用频率优先开发核心功能 +- 注意保持与整体设计风格的一致性 \ No newline at end of file diff --git a/nkebao/src/components/MeauMobile/MeauMoible.tsx b/nkebao/src/components/MeauMobile/MeauMoible.tsx index 4fe432f81..52a71d209 100644 --- a/nkebao/src/components/MeauMobile/MeauMoible.tsx +++ b/nkebao/src/components/MeauMobile/MeauMoible.tsx @@ -25,7 +25,7 @@ const tabs = [ key: "work", title: "工作台", icon: , - path: "/work", + path: "/workspace", }, { key: "mine", @@ -36,7 +36,7 @@ const tabs = [ ]; // 需要展示菜单的路由白名单(可根据实际业务调整) -const menuPaths = ["/", "/scene", "/work", "/mine"]; +const menuPaths = ["/", "/scene", "/workspace", "/mine"]; const MeauMobile: React.FC = () => { const location = useLocation(); diff --git a/nkebao/src/components/PlaceholderPage.tsx b/nkebao/src/components/PlaceholderPage.tsx new file mode 100644 index 000000000..1e5f87d73 --- /dev/null +++ b/nkebao/src/components/PlaceholderPage.tsx @@ -0,0 +1,56 @@ +import React from "react"; +import { NavBar, Button } from "antd-mobile"; +import { AddOutline } from "antd-mobile-icons"; +import Layout from "@/components/Layout/Layout"; +import MeauMobile from "@/components/MeauMobile/MeauMoible"; + +interface PlaceholderPageProps { + title: string; + showBack?: boolean; + showAddButton?: boolean; + addButtonText?: string; + showFooter?: boolean; +} + +const PlaceholderPage: React.FC = ({ + title, + showBack = true, + showAddButton = false, + addButtonText = "新建", + showFooter = true, +}) => { + return ( + window.history.back() : undefined} + left={ +
+ {title} +
+ } + right={ + showAddButton ? ( + + ) : undefined + } + /> + } + footer={showFooter ? : undefined} + > +
+

{title}页面

+

此页面正在开发中...

+
+
+ ); +}; + +export default PlaceholderPage; diff --git a/nkebao/src/pages/About.tsx b/nkebao/src/pages/About.tsx deleted file mode 100644 index 370fdfe27..000000000 --- a/nkebao/src/pages/About.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import React from "react"; - -const About: React.FC = () => { - return

关于 About

; -}; - -export default About; diff --git a/nkebao/src/pages/contact-import/ContactImport.tsx b/nkebao/src/pages/contact-import/ContactImport.tsx new file mode 100644 index 000000000..9d03b6b24 --- /dev/null +++ b/nkebao/src/pages/contact-import/ContactImport.tsx @@ -0,0 +1,14 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const ContactImport: React.FC = () => { + return ( + + ); +}; + +export default ContactImport; diff --git a/nkebao/src/pages/content/Content.tsx b/nkebao/src/pages/content/Content.tsx new file mode 100644 index 000000000..55e68abd8 --- /dev/null +++ b/nkebao/src/pages/content/Content.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const Content: React.FC = () => { + return ( + + ); +}; + +export default Content; diff --git a/nkebao/src/pages/content/NewContent.tsx b/nkebao/src/pages/content/NewContent.tsx new file mode 100644 index 000000000..3bd822887 --- /dev/null +++ b/nkebao/src/pages/content/NewContent.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const NewContent: React.FC = () => { + return ; +}; + +export default NewContent; diff --git a/nkebao/src/pages/content/materials/List.tsx b/nkebao/src/pages/content/materials/List.tsx new file mode 100644 index 000000000..e11ae6745 --- /dev/null +++ b/nkebao/src/pages/content/materials/List.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const Materials: React.FC = () => { + return ( + + ); +}; + +export default Materials; diff --git a/nkebao/src/pages/content/materials/New.tsx b/nkebao/src/pages/content/materials/New.tsx new file mode 100644 index 000000000..62afef95a --- /dev/null +++ b/nkebao/src/pages/content/materials/New.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const MaterialsNew: React.FC = () => { + return ; +}; + +export default MaterialsNew; diff --git a/nkebao/src/pages/devices/DeviceDetail.tsx b/nkebao/src/pages/devices/DeviceDetail.tsx new file mode 100644 index 000000000..69876d6e0 --- /dev/null +++ b/nkebao/src/pages/devices/DeviceDetail.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import { NavBar } from "antd-mobile"; +import Layout from "@/components/Layout/Layout"; +import MeauMobile from "@/components/MeauMobile/MeauMoible"; + +const DeviceDetail: React.FC = () => { + return ( + window.history.back()} + > +
+ 设备详情 +
+ + } + footer={} + > +
+

设备详情页面

+

此页面正在开发中...

+
+
+ ); +}; + +export default DeviceDetail; diff --git a/nkebao/src/pages/devices/Devices.tsx b/nkebao/src/pages/devices/Devices.tsx new file mode 100644 index 000000000..66895d54e --- /dev/null +++ b/nkebao/src/pages/devices/Devices.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import { NavBar, Button } from "antd-mobile"; +import { AddOutline } from "antd-mobile-icons"; +import Layout from "@/components/Layout/Layout"; +import MeauMobile from "@/components/MeauMobile/MeauMoible"; + +const Devices: React.FC = () => { + return ( + + 设备管理 +
+ } + right={ + + } + /> + } + footer={} + > +
+

设备管理页面

+

此页面正在开发中...

+
+ + ); +}; + +export default Devices; diff --git a/nkebao/src/pages/login/login.module.scss b/nkebao/src/pages/login/login.module.scss index a13baa521..21ebe95ab 100644 --- a/nkebao/src/pages/login/login.module.scss +++ b/nkebao/src/pages/login/login.module.scss @@ -1,439 +1,52 @@ .login-page { min-height: 100vh; - background: var(--primary-gradient); + background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); +} + +.nav-title { + font-size: 18px; + font-weight: 600; + color: var(--primary-color); +} + +.login-content { + padding: 40px 20px; display: flex; + flex-direction: column; align-items: center; justify-content: center; - padding: 15px; - position: relative; - overflow: hidden; -} - -// 背景装饰 -.bg-decoration { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - pointer-events: none; - z-index: 0; -} - -.bg-circle { - position: absolute; - border-radius: 50%; - background: rgba(255, 255, 255, 0.1); - animation: float 6s ease-in-out infinite; - - &:nth-child(1) { - width: 200px; - height: 200px; - top: -100px; - right: -100px; - animation-delay: 0s; - } - - &:nth-child(2) { - width: 150px; - height: 150px; - bottom: -75px; - left: -75px; - animation-delay: 2s; - } - - &:nth-child(3) { - width: 100px; - height: 100px; - top: 50%; - right: 10%; - animation-delay: 4s; - } -} - -@keyframes float { - 0%, 100% { - transform: translateY(0px) rotate(0deg); - } - 50% { - transform: translateY(-20px) rotate(180deg); - } -} - -.login-container { - width: 100%; - max-width: 420px; - background: #ffffff; - backdrop-filter: blur(20px); - border-radius: 24px; - padding: 24px 20px; - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); - position: relative; - z-index: 1; - border: 1px solid rgba(255, 255, 255, 0.2); + min-height: calc(100vh - 60px); } .login-header { text-align: center; - margin-bottom: 24px; + margin-bottom: 40px; } -.logo-section { - display: flex; - align-items: center; - justify-content: center; - gap: 12px; - margin-bottom: 16px; -} - -.logo-icon { - width: 40px; - height: 40px; - background: var(--primary-gradient); - border-radius: 10px; - display: flex; - align-items: center; - justify-content: center; - color: white; - font-size: 20px; - box-shadow: 0 6px 12px var(--primary-shadow); -} - -.app-name { - font-size: 24px; - font-weight: 800; - background: var(--primary-gradient); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - margin: 0; -} - -.subtitle { - font-size: 13px; - color: #666; - margin: 0; -} - -.form-container { - margin-bottom: 20px; -} - -// 标签页样式 -.tab-container { - display: flex; - background: #f8f9fa; - border-radius: 10px; - padding: 3px; - margin-bottom: 24px; - position: relative; -} - -.tab-item { - flex: 1; - text-align: center; - padding: 10px 12px; - font-size: 13px; - font-weight: 500; - color: #666; - cursor: pointer; - border-radius: 7px; - transition: all 0.3s ease; - position: relative; - z-index: 2; - - &.active { - color: var(--primary-color); - font-weight: 600; - background: white; - box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); - } -} - -.tab-indicator { - display: none; // 隐藏分割线指示器 -} - -// 表单样式 -.login-form { - :global(.adm-form) { - --adm-font-size-main: 14px; - } -} - -.input-group { - margin-bottom: 18px; -} - -.input-label { - display: block; - font-size: 13px; - font-weight: 600; - color: #333; - margin-bottom: 6px; -} - -.input-wrapper { - position: relative; - display: flex; - align-items: center; - background: #f8f9fa; - border: 2px solid transparent; - border-radius: 10px; - transition: all 0.3s ease; - - &:focus-within { - border-color: var(--primary-color); - background: white; - box-shadow: 0 0 0 3px var(--primary-shadow-light); - } -} - -.input-prefix { - padding: 0 12px; - color: #666; - font-size: 13px; - font-weight: 500; - border-right: 1px solid #e5e5e5; -} - -.phone-input, -.password-input, -.code-input { - flex: 1; - border: none !important; - background: transparent !important; - padding: 12px 14px !important; - font-size: 15px !important; - color: #333 !important; - - &::placeholder { - color: #999; - } - - &:focus { - box-shadow: none !important; - } -} - -.eye-icon { - padding: 0 12px; - color: #666; - cursor: pointer; - transition: color 0.3s ease; - - &:hover { - color: var(--primary-color); - } -} - -.send-code-btn { - padding: 6px 12px; - margin-right: 6px; - background: var(--primary-gradient); - color: white; - border: none; - border-radius: 6px; - font-size: 11px; - font-weight: 500; - cursor: pointer; - transition: all 0.3s ease; - - &:hover:not(.disabled) { - transform: translateY(-1px); - box-shadow: 0 3px 8px var(--primary-shadow); - } - - &.disabled { - background: #e5e5e5; - color: #999; - cursor: not-allowed; - transform: none; - box-shadow: none; - } -} - -.agreement-section { - margin-bottom: 24px; -} - -.agreement-checkbox { - display: flex; - align-items: center; - gap: 6px; - font-size: 12px; - color: #666; - line-height: 1.3; - white-space: nowrap; - - :global(.adm-checkbox) { - margin-top: 0; - flex-shrink: 0; - transform: scale(0.8); - } -} - -.agreement-text { - flex: 1; - display: flex; - align-items: center; - flex-wrap: nowrap; - white-space: nowrap; - overflow: visible; - text-overflow: clip; - font-size: 13px; -} - -.agreement-link { +.login-title { + font-size: 28px; + font-weight: 700; color: var(--primary-color); - cursor: pointer; - text-decoration: none; - white-space: nowrap; - font-size: 11px; - - &:hover { - text-decoration: underline; - } + margin-bottom: 8px; } -.login-btn { - height: 46px; - font-size: 15px; - font-weight: 600; - border-radius: 10px; +.login-subtitle { + font-size: 14px; + color: #666; + line-height: 1.4; +} + +.login-form { + width: 100%; + max-width: 320px; +} + +.login-button { + margin-top: 20px; + border-radius: 20px; background: var(--primary-gradient); border: none; - box-shadow: 0 6px 12px var(--primary-shadow); - transition: all 0.3s ease; - - &:hover:not(:disabled) { - transform: translateY(-1px); - box-shadow: 0 8px 16px var(--primary-shadow-dark); - } - - &:disabled { - background: #e5e5e5; - color: #999; - transform: none; - box-shadow: none; - } -} - -.divider { - position: relative; - text-align: center; - margin: 24px 0; - - &::before { - content: ''; - position: absolute; - top: 50%; - left: 0; - right: 0; - height: 1px; - background: #e5e5e5; - } - - span { - background: rgba(255, 255, 255, 0.95); - padding: 0 12px; - color: #999; - font-size: 11px; - font-weight: 500; - } -} - -.third-party-login { - display: flex; - justify-content: center; - gap: 20px; -} - -.third-party-item { - display: flex; - flex-direction: column; - align-items: center; - gap: 6px; - cursor: pointer; - padding: 12px; - border-radius: 10px; - transition: all 0.3s ease; - - &:hover { - background: #f8f9fa; - transform: translateY(-1px); - } - - span { - font-size: 11px; - color: #666; - font-weight: 500; - } -} - -.wechat-icon, -.apple-icon { - width: 36px; - height: 36px; - border-radius: 10px; - display: flex; - align-items: center; - justify-content: center; - color: white; - font-size: 18px; - transition: all 0.3s ease; -} - -.wechat-icon { - background: #07c160; - box-shadow: 0 3px 8px rgba(7, 193, 96, 0.3); - - &:hover { - box-shadow: 0 4px 12px rgba(7, 193, 96, 0.4); - } - - svg { - width: 20px; - height: 20px; - } -} - -.apple-icon { - background: #000; - box-shadow: 0 3px 8px rgba(0, 0, 0, 0.3); - - &:hover { - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); - } - - svg { - width: 20px; - height: 20px; - } -} - -// 响应式设计 -@media (max-width: 480px) { - .login-container { - padding: 24px 20px; - margin: 0 12px; - } - - .app-name { - font-size: 22px; - } - - .third-party-login { - gap: 16px; - } - - .third-party-item { - padding: 10px; - } - - .wechat-icon, - .apple-icon { - width: 32px; - height: 32px; - } + box-shadow: 0 2px 8px var(--primary-shadow); + height: 44px; + font-weight: 600; } diff --git a/nkebao/src/pages/login/login.tsx b/nkebao/src/pages/login/login.tsx index f53ccc6d0..c30137295 100644 --- a/nkebao/src/pages/login/login.tsx +++ b/nkebao/src/pages/login/login.tsx @@ -1,331 +1,64 @@ -import React, { useState, useEffect } from "react"; -import { useNavigate, useSearchParams } from "react-router-dom"; -import { Form, Input, Button, Toast, Tabs, Checkbox } from "antd-mobile"; -import { - EyeInvisibleOutline, - EyeOutline, - UserOutline, -} from "antd-mobile-icons"; -import { useUserStore } from "@/store/module/user"; -import { loginWithPassword, loginWithCode, sendVerificationCode } from "./api"; +import React from "react"; +import { NavBar, Form, Input, Button, Toast } from "antd-mobile"; +import Layout from "@/components/Layout/Layout"; import style from "./login.module.scss"; const Login: React.FC = () => { - const [form] = Form.useForm(); - const [activeTab, setActiveTab] = useState(1); // 1: 密码登录, 2: 验证码登录 - const [loading, setLoading] = useState(false); - const [countdown, setCountdown] = useState(0); - const [showPassword, setShowPassword] = useState(false); - const [agreeToTerms, setAgreeToTerms] = useState(false); - - const navigate = useNavigate(); - const [searchParams] = useSearchParams(); - const { login } = useUserStore(); - - // 倒计时效果 - useEffect(() => { - if (countdown > 0) { - const timer = setTimeout(() => setCountdown(countdown - 1), 1000); - return () => clearTimeout(timer); - } - }, [countdown]); - - // 检查URL是否为登录页面 - const isLoginPage = (url: string) => { - try { - const urlObj = new URL(url, window.location.origin); - return urlObj.pathname === "/login" || urlObj.pathname.endsWith("/login"); - } catch { - return false; - } - }; - - // 发送验证码 - const handleSendVerificationCode = async () => { - const account = form.getFieldValue("account"); - - if (!account) { - Toast.show({ content: "请输入手机号", position: "top" }); - return; - } - - // 手机号格式验证 - const phoneRegex = /^1[3-9]\d{9}$/; - if (!phoneRegex.test(account)) { - Toast.show({ content: "请输入正确的11位手机号", position: "top" }); - return; - } - - try { - setLoading(true); - await sendVerificationCode({ - mobile: account, - type: "login", - }); - - Toast.show({ content: "验证码已发送", position: "top" }); - setCountdown(60); - } catch (error) { - // 错误已在request中处理,这里不需要额外处理 - } finally { - setLoading(false); - } - }; - - // 登录处理 - const handleLogin = async (values: any) => { - if (!agreeToTerms) { - Toast.show({ content: "请同意用户协议和隐私政策", position: "top" }); - return; - } - - setLoading(true); - try { - // 添加typeId参数 - const loginParams = { - ...values, - typeId: activeTab as number, - }; - - let response; - if (activeTab === 1) { - response = await loginWithPassword(loginParams); - } else { - response = await loginWithCode(loginParams); - } - console.log(response, "response"); - - // 更新状态管理(token会自动存储到localStorage,用户信息存储在状态管理中) - login(response.token, response.member); - - Toast.show({ content: "登录成功", position: "top" }); - - // 跳转到首页或重定向URL - const returnUrl = searchParams.get("returnUrl"); - if (returnUrl) { - const decodedUrl = decodeURIComponent(returnUrl); - if (isLoginPage(decodedUrl)) { - navigate("/"); - } else { - window.location.href = decodedUrl; - } - } else { - navigate("/"); - } - } catch (error: any) { - // 错误已在request中处理,这里不需要额外处理 - } finally { - setLoading(false); - } - }; - - // 第三方登录处理 - const handleWechatLogin = () => { - Toast.show({ content: "微信登录功能开发中", position: "top" }); - }; - - const handleAppleLogin = () => { - Toast.show({ content: "Apple登录功能开发中", position: "top" }); + const onFinish = (values: any) => { + console.log("登录信息:", values); + Toast.show({ + content: "登录功能待实现", + position: "top", + }); }; return ( -
- {/* 背景装饰 */} -
-
-
-
-
+ +
+ +
登录
+
-
- {/* Logo和标题区域 */} -
-
-
- -
-

存客宝

-
-

登录您的账户继续使用

-
- - {/* 登录表单 */} -
- {/* 标签页切换 */} -
-
setActiveTab(1)} - > - 密码登录 -
-
setActiveTab(2)} - > - 验证码登录 -
-
+
+
+

存客宝

+

欢迎回来,请登录您的账户

- {/* 手机号输入 */} -
- +86 - -
+
- {/* 密码输入 */} - {activeTab === 1 && ( - -
- -
setShowPassword(!showPassword)} - > - {showPassword ? : } -
-
-
- )} + + + - {/* 验证码输入 */} - {activeTab === 2 && ( - -
- - -
-
- )} - - {/* 用户协议 */} -
- - - 我已阅读并同意 - - 《存客宝用户协议》 - - 和 - 《隐私政策》 - - -
- - {/* 登录按钮 */}
- - {/* 分割线 */} -
- 其他登录方式 -
- - {/* 第三方登录 */} -
-
-
- - - - -
- 微信 -
-
-
- - - -
- Apple -
-
-
+ ); }; diff --git a/nkebao/src/pages/orders/Orders.tsx b/nkebao/src/pages/orders/Orders.tsx new file mode 100644 index 000000000..daecccd29 --- /dev/null +++ b/nkebao/src/pages/orders/Orders.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const Orders: React.FC = () => { + return ; +}; + +export default Orders; diff --git a/nkebao/src/pages/plans/PlanDetail.tsx b/nkebao/src/pages/plans/PlanDetail.tsx new file mode 100644 index 000000000..1d2e8b086 --- /dev/null +++ b/nkebao/src/pages/plans/PlanDetail.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const PlanDetail: React.FC = () => { + return ; +}; + +export default PlanDetail; diff --git a/nkebao/src/pages/plans/Plans.tsx b/nkebao/src/pages/plans/Plans.tsx new file mode 100644 index 000000000..1f0df647b --- /dev/null +++ b/nkebao/src/pages/plans/Plans.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const Plans: React.FC = () => { + return ( + + ); +}; + +export default Plans; diff --git a/nkebao/src/pages/profile/Profile.tsx b/nkebao/src/pages/profile/Profile.tsx new file mode 100644 index 000000000..87bc2d345 --- /dev/null +++ b/nkebao/src/pages/profile/Profile.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const Profile: React.FC = () => { + return ; +}; + +export default Profile; diff --git a/nkebao/src/pages/scenarios/ScenarioList.tsx b/nkebao/src/pages/scenarios/ScenarioList.tsx new file mode 100644 index 000000000..f1088dc79 --- /dev/null +++ b/nkebao/src/pages/scenarios/ScenarioList.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const ScenarioList: React.FC = () => { + return ; +}; + +export default ScenarioList; diff --git a/nkebao/src/pages/scenarios/Scenarios.tsx b/nkebao/src/pages/scenarios/Scenarios.tsx new file mode 100644 index 000000000..48d337601 --- /dev/null +++ b/nkebao/src/pages/scenarios/Scenarios.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const Scenarios: React.FC = () => { + return ( + + ); +}; + +export default Scenarios; diff --git a/nkebao/src/pages/scenarios/new/page.tsx b/nkebao/src/pages/scenarios/new/page.tsx new file mode 100644 index 000000000..b79b30fee --- /dev/null +++ b/nkebao/src/pages/scenarios/new/page.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const NewPlan: React.FC = () => { + return ; +}; + +export default NewPlan; diff --git a/nkebao/src/pages/scene/api.ts b/nkebao/src/pages/scene/api.ts index 535147142..0fff16626 100644 --- a/nkebao/src/pages/scene/api.ts +++ b/nkebao/src/pages/scene/api.ts @@ -1,14 +1,26 @@ import request from '@/api/request'; -// 设备统计 -export function getDeviceStats() { - return request('/v1/dashboard/device-stats', {}, 'GET'); +// 获取场景列表 +export function getScenarios(params: any) { + return request('/v1/plan/scenes', params, 'GET'); } -// 微信号统计 -export function getWechatStats() { - return request('/v1/dashboard/wechat-stats', {}, 'GET'); +// 获取场景详情 +export function getScenarioDetail(id: string) { + return request(`/v1/scenarios/${id}`, {}, 'GET'); } -// 你可以根据需要继续添加其他接口 -// 例如:场景获客统计、今日数据统计等 +// 创建场景 +export function createScenario(data: any) { + return request('/v1/scenarios', data, 'POST'); +} + +// 更新场景 +export function updateScenario(id: string, data: any) { + return request(`/v1/scenarios/${id}`, data, 'PUT'); +} + +// 删除场景 +export function deleteScenario(id: string) { + return request(`/v1/scenarios/${id}`, {}, 'DELETE'); +} diff --git a/nkebao/src/pages/scene/index.module.scss b/nkebao/src/pages/scene/index.module.scss index 53304f695..972a62f35 100644 --- a/nkebao/src/pages/scene/index.module.scss +++ b/nkebao/src/pages/scene/index.module.scss @@ -1,71 +1,319 @@ -.home-page { - padding: 12px; - background: #f5f5f5; +// 导航栏样式 +.nav-title { + font-size: 18px; + font-weight: 600; + color: var(--primary-color); } -.home-cards { - display: flex; - gap: 12px; - margin-bottom: 12px; - > :global(.adm-card) { - flex: 1; +.nav-text { + color: var(--primary-color); +} + +.nav-right { + margin-left: 4px; + font-size: 12px; +} + +.new-plan-btn { + border-radius: 20px; + padding: 4px 12px; + height: 32px; + font-size: 12px; + background: var(--primary-gradient); + border: none; + box-shadow: 0 2px 8px var(--primary-shadow); + + &:active { + transform: translateY(1px); + box-shadow: 0 1px 4px var(--primary-shadow); } } -.home-section { +// 页面容器 +.scene-page { + padding: 12px; + background: #f5f5f5; + min-height: calc(100vh - 100px); +} + +// 错误提示 +.error-notice { margin-bottom: 12px; + padding: 8px 12px; + background: #fff2e8; + border: 1px solid #ffd591; + border-radius: 8px; + box-shadow: 0 1px 4px rgba(255, 213, 145, 0.2); +} + +.error-notice-text { + font-size: 12px; + color: #d46b08; + font-weight: 500; +} + +// 加载状态 +.loading-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 200px; + gap: 12px; +} + +.loading-text { + font-size: 14px; + color: #666; + font-weight: 500; +} + +// 错误状态 +.error-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 200px; + gap: 16px; +} + +.error-text { + font-size: 14px; + color: #ff4d4f; + text-align: center; + font-weight: 500; +} + +.retry-button { + min-width: 100px; + border-radius: 20px; + background: var(--primary-gradient); + border: none; + box-shadow: 0 2px 8px var(--primary-shadow); +} + +// 页面头部 +.scene-header { + margin-bottom: 16px; + text-align: center; +} + +.header-title { + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + font-size: 18px; + font-weight: 700; + color: var(--primary-color); + margin-bottom: 6px; +} + +.header-icon { + font-size: 20px; + color: var(--primary-color); +} + +.header-subtitle { + font-size: 12px; + color: #666; + line-height: 1.4; +} + +// 场景列表 +.scenarios-list { + display: flex; + flex-direction: column; + gap: 10px; +} + +// 场景卡片 +.scenario-item { + cursor: pointer; + transition: all 0.2s ease; + + &:hover { + transform: translateY(-1px); + } + + &:active { + transform: translateY(0); + } +} + +.scenario-card { background: #fff; border-radius: 12px; - box-shadow: 0 2px 8px rgba(0,0,0,0.03); -} - -.home-section-title { - font-size: 16px; - font-weight: 600; - margin-bottom: 15px; - padding-left: 16px; -} - -.home-scene-stats { + padding: 16px 20px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + border: 1px solid #f0f0f0; + transition: all 0.2s ease; display: flex; justify-content: space-between; - margin: 0 8px 8px 8px; -} -.home-scene-item { - flex: 1; - text-align: center; -} -.home-scene-icon { - margin: 0 auto 4px auto; -} -.home-scene-value { - font-size: 18px; - font-weight: bold; - color: #1677ff; -} -.home-scene-label { - font-size: 12px; - color: #888; + align-items: center; + min-height: 78px; + + &:hover { + box-shadow: 0 4px 16px rgba(24, 142, 238, 0.12); + border-color: var(--primary-color-light); + } } -.home-today-item { - text-align: center; - padding: 8px 0; - background: #f7f8fa; +.scenario-left { + display: flex; + align-items: center; + gap: 16px; + flex: 1; +} + +.scenario-icon { + width: 52px; + height: 52px; + background: linear-gradient(135deg, #f0f8ff 0%, #e6f7ff 100%); + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid rgba(24, 142, 238, 0.1); + flex-shrink: 0; +} + +.scenario-image { + width: 32px; + height: 32px; + object-fit: contain; border-radius: 8px; } -.home-today-value { - font-size: 18px; - font-weight: bold; - color: #1677ff; -} -.home-today-label { - font-size: 12px; - color: #888; + +.scenario-info { + display: flex; + flex-direction: column; + gap: 6px; + flex: 1; + min-width: 0; } -.home-chart { - margin-top: 8px; - width: 100%; - min-height: 100px; -} \ No newline at end of file +.scenario-name { + font-size: 16px; + font-weight: 600; + color: #1a1a1a; + line-height: 1.3; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.scenario-stats { + display: flex; + align-items: center; +} + +.stat-text { + font-size: 13px; + color: #666; + font-weight: 500; +} + +.scenario-right { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 8px; + flex-shrink: 0; +} + +.scenario-status { + display: flex; + justify-content: flex-end; +} + +.status-tag { + font-size: 11px; + padding: 3px 8px; + border-radius: 10px; + font-weight: 500; +} + +.scenario-growth { + display: flex; + align-items: center; + gap: 3px; + font-size: 16px; + font-weight: 700; +} + +.growth-icon { + font-size: 14px; +} + +// 空状态 +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 40px 20px; + text-align: center; +} + +.empty-icon { + font-size: 36px; + margin-bottom: 12px; + opacity: 0.6; +} + +.empty-text { + font-size: 14px; + color: #666; + margin-bottom: 16px; + font-weight: 500; +} + +.empty-action { + border-radius: 20px; + background: var(--primary-gradient); + border: none; + box-shadow: 0 2px 8px var(--primary-shadow); + padding: 6px 16px; + font-weight: 500; + font-size: 12px; +} + +// 响应式设计 +@media (max-width: 480px) { + .scene-page { + padding: 8px; + } + + .scenario-card { + padding: 14px 16px; + min-height: 70px; + } + + .scenario-icon { + width: 46px; + height: 46px; + } + + .scenario-image { + width: 28px; + height: 28px; + } + + .scenario-name { + font-size: 15px; + } + + .stat-text { + font-size: 12px; + } + + .scenario-growth { + font-size: 15px; + } + + .growth-icon { + font-size: 13px; + } +} diff --git a/nkebao/src/pages/scene/index.tsx b/nkebao/src/pages/scene/index.tsx index dc673c00b..4ca7359a1 100644 --- a/nkebao/src/pages/scene/index.tsx +++ b/nkebao/src/pages/scene/index.tsx @@ -1,132 +1,184 @@ -import React, { useState } from "react"; -import { Card, NavBar, TabBar, Grid } from "antd-mobile"; -import { - AppOutline, - UserOutline, - PieOutline, // 替换 BarChartOutline - ShopbagOutline, - BellOutline, -} from "antd-mobile-icons"; +import React, { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { NavBar, Button, SpinLoading, Toast, Tag } from "antd-mobile"; +import { AddOutline, UserAddOutline } from "antd-mobile-icons"; import MeauMobile from "@/components/MeauMobile/MeauMoible"; import Layout from "@/components/Layout/Layout"; +import { getScenarios } from "./api"; import style from "./index.module.scss"; -const sceneStats = [ - { - label: "公众号获客", - value: 234, - icon: , - }, - { - label: "海报获客", - value: 167, - icon: , - }, - { - label: "抖音获客", - value: 156, - icon: , - }, - { - label: "小红书获客", - value: 89, - icon: , - }, -]; -const todayStats = [ - { label: "朋友圈同步", value: 12 }, - { label: "群发任务", value: 8 }, - { label: "获客转化", value: "85%" }, - { label: "系统活跃度", value: "98%" }, -]; +interface Scenario { + id: string; + name: string; + image: string; + count: number; + growth: string; + status: number; +} + +const Scene: React.FC = () => { + const navigate = useNavigate(); + const [scenarios, setScenarios] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(""); + + useEffect(() => { + const fetchScenarios = async () => { + setLoading(true); + setError(""); + + try { + const response = await getScenarios({ page: 1, limit: 20 }); + + // 转换API数据为前端需要的格式 + const transformedScenarios: Scenario[] = response.map((item: any) => ({ + id: item.id.toString(), + name: item.name, + image: + item.image || + "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-api.png", + count: item.count, // 模拟今日数据 + growth: item.growth, // 模拟增长率 + status: item.status, + })); + + setScenarios(transformedScenarios); + } catch (error) { + console.error("获取场景数据失败:", error); + setError("获取场景数据失败,请稍后重试"); + Toast.show({ + content: "获取场景数据失败,请稍后重试", + position: "top", + }); + } finally { + setLoading(false); + } + }; + + fetchScenarios(); + }, []); + + const handleScenarioClick = (scenarioId: string, scenarioName: string) => { + navigate( + `/scenarios/list/${scenarioId}/${encodeURIComponent(scenarioName)}` + ); + }; + + const handleNewPlan = () => { + navigate("/scenarios/new"); + }; + + const getGrowthColor = (growth: string) => { + const value = parseFloat(growth.replace(/[+%]/g, "")); + if (value > 10) return "#52c41a"; + if (value > 5) return "#faad14"; + return "#ff4d4f"; + }; -const Home: React.FC = () => { return ( - 存客宝 - + 场景获客
} + right={ + + } + /> } footer={} > -
- {/* 统计卡片 */} -
- -
设备数量
-
0
-
- -
微信号数量
-
0
-
- -
在线微信号
-
0
-
+
+
+
+ + 热门获客场景 +
+
+ 选择适合的获客场景,提升转化效果 +
- {/* 场景获客统计 */} - -
场景获客统计
-
- {sceneStats.map((item) => ( -
-
{item.icon}
-
{item.value}
-
{item.label}
-
- ))} -
-
- - {/* 今日数据 */} - -
今日数据
- - {todayStats.map((item) => ( - -
-
{item.value}
-
{item.label}
+
+ {scenarios.map((scenario) => ( +
handleScenarioClick(scenario.id, scenario.name)} + > +
+
+
+ {scenario.name} { + e.currentTarget.src = + "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-api.png"; + }} + /> +
+
+
+ {scenario.name} +
+
+ + 今日获客: {scenario.count} + +
+
- - ))} - - - {/* 每日获客趋势(静态图表占位) */} - -
每日获客趋势
-
- - - {/* x轴文字 */} - {["周一", "周二", "周三", "周四", "周五", "周六", "周日"].map( - (d, i) => ( - +
+ + {scenario.status === 1 ? "活跃" : "暂停"} + +
+
- {d} - - ) - )} - + {scenario.growth} +
+
+
+
+ ))} +
+ + {scenarios.length === 0 && !loading && ( +
+
📊
+
暂无场景数据
+
- + )}
); }; -export default Home; +export default Scene; diff --git a/nkebao/src/pages/traffic-pool/TrafficPool.tsx b/nkebao/src/pages/traffic-pool/TrafficPool.tsx new file mode 100644 index 000000000..d64714d2f --- /dev/null +++ b/nkebao/src/pages/traffic-pool/TrafficPool.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const TrafficPool: React.FC = () => { + return ( + + ); +}; + +export default TrafficPool; diff --git a/nkebao/src/pages/traffic-pool/TrafficPoolDetail.tsx b/nkebao/src/pages/traffic-pool/TrafficPoolDetail.tsx new file mode 100644 index 000000000..d4c1ceef5 --- /dev/null +++ b/nkebao/src/pages/traffic-pool/TrafficPoolDetail.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const TrafficPoolDetail: React.FC = () => { + return ; +}; + +export default TrafficPoolDetail; diff --git a/nkebao/src/pages/wechat-accounts/WechatAccountDetail.tsx b/nkebao/src/pages/wechat-accounts/WechatAccountDetail.tsx new file mode 100644 index 000000000..ac5fa6ecb --- /dev/null +++ b/nkebao/src/pages/wechat-accounts/WechatAccountDetail.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import { NavBar } from "antd-mobile"; +import Layout from "@/components/Layout/Layout"; +import MeauMobile from "@/components/MeauMobile/MeauMoible"; + +const WechatAccountDetail: React.FC = () => { + return ( + window.history.back()} + > +
+ 微信号详情 +
+ + } + footer={} + > +
+

微信号详情页面

+

此页面正在开发中...

+
+
+ ); +}; + +export default WechatAccountDetail; diff --git a/nkebao/src/pages/wechat-accounts/WechatAccounts.tsx b/nkebao/src/pages/wechat-accounts/WechatAccounts.tsx new file mode 100644 index 000000000..5354b8267 --- /dev/null +++ b/nkebao/src/pages/wechat-accounts/WechatAccounts.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import { NavBar, Button } from "antd-mobile"; +import { AddOutline } from "antd-mobile-icons"; +import Layout from "@/components/Layout/Layout"; +import MeauMobile from "@/components/MeauMobile/MeauMoible"; + +const WechatAccounts: React.FC = () => { + return ( + + 微信号管理 +
+ } + right={ + + } + /> + } + footer={} + > +
+

微信号管理页面

+

此页面正在开发中...

+
+ + ); +}; + +export default WechatAccounts; diff --git a/nkebao/src/pages/workspace/Workspace.module.scss b/nkebao/src/pages/workspace/Workspace.module.scss new file mode 100644 index 000000000..c073ab282 --- /dev/null +++ b/nkebao/src/pages/workspace/Workspace.module.scss @@ -0,0 +1,185 @@ +.workspace { + padding: 16px; + background-color: #f5f5f5; + min-height: 100vh; +} + +.statsGrid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; + margin-bottom: 24px; +} + +.statsCard { + background: #fff; + border-radius: 12px; + padding: 16px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + + :global(.adm-card-body) { + padding: 0; + } +} + +.statsTitle { + font-size: 14px; + color: #666; + margin-bottom: 8px; +} + +.statsValue { + font-size: 28px; + font-weight: 700; + color: var(--primary-color); + margin-bottom: 12px; +} + +.progress { + margin-bottom: 8px; + + :global(.adm-progress-bar) { + background-color: #f0f0f0; + } + + :global(.adm-progress-bar-fill) { + background-color: var(--primary-color); + } +} + +.statsSubtitle { + font-size: 12px; + color: #999; +} + +.activityRate { + display: flex; + align-items: center; + font-size: 14px; + color: #52c41a; + margin-top: 8px; +} + +.activityIcon { + width: 16px; + height: 16px; + margin-right: 4px; + color: #52c41a; +} + +.section { + margin-bottom: 24px; +} + +.sectionTitle { + font-size: 18px; + font-weight: 600; + color: #333; + margin-bottom: 16px; + padding-left: 4px; +} + +.featuresGrid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; +} + +.featureLink { + text-decoration: none; + color: inherit; +} + +.featureCard { + background: #fff; + border-radius: 12px; + padding: 16px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); + transition: all 0.2s ease; + + &:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + } + + :global(.adm-card-body) { + padding: 0; + } +} + +.featureIcon { + width: 40px; + height: 40px; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 12px; +} + +.icon { + font-size: 20px; +} + +.featureHeader { + display: flex; + align-items: center; + margin-bottom: 4px; +} + +.featureName { + font-size: 16px; + font-weight: 600; + color: #333; +} + +.newBadge { + margin-left: 8px; + + :global(.adm-badge-content) { + background-color: var(--primary-color); + color: #fff; + font-size: 10px; + padding: 2px 6px; + border-radius: 10px; + } +} + +.featureDescription { + font-size: 12px; + color: #666; + line-height: 1.4; +} + +// 响应式设计 +@media (max-width: 375px) { + .workspace { + padding: 12px; + } + + .statsGrid { + gap: 8px; + } + + .featuresGrid { + gap: 8px; + } + + .statsCard, + .featureCard { + padding: 12px; + } + + .statsValue { + font-size: 24px; + } + + .featureIcon { + width: 36px; + height: 36px; + } + + .icon { + font-size: 18px; + } +} \ No newline at end of file diff --git a/nkebao/src/pages/workspace/Workspace.tsx b/nkebao/src/pages/workspace/Workspace.tsx new file mode 100644 index 000000000..81096f03d --- /dev/null +++ b/nkebao/src/pages/workspace/Workspace.tsx @@ -0,0 +1,254 @@ +import React from "react"; +import { Link } from "react-router-dom"; +import { Card, NavBar, Badge, Progress } from "antd-mobile"; +import { + ThumbsUpOutline, + MessageOutline, + SendOutline, + TeamOutline, + ShareOutline, + AppOutline, + BarChartOutline, + LineChartOutline, + ClockCircleOutline, +} from "antd-mobile-icons"; +import Layout from "@/components/Layout/Layout"; +import MeauMobile from "@/components/MeauMobile/MeauMoible"; +import styles from "./Workspace.module.scss"; + +const Workspace: React.FC = () => { + // 模拟任务数据 + const taskStats = { + total: 42, + inProgress: 12, + completed: 30, + todayTasks: 12, + activityRate: 98, + }; + + // 常用功能 + const commonFeatures = [ + { + id: "auto-like", + name: "自动点赞", + description: "智能自动点赞互动", + icon: ( + + ), + path: "/workspace/auto-like", + bgColor: "#fff2f0", + isNew: true, + }, + { + id: "moments-sync", + name: "朋友圈同步", + description: "自动同步朋友圈内容", + icon: ( + + ), + path: "/workspace/moments-sync", + bgColor: "#f9f0ff", + }, + { + id: "group-push", + name: "群消息推送", + description: "智能群发助手", + icon: ( + + ), + path: "/workspace/group-push", + bgColor: "#fff7e6", + }, + { + id: "auto-group", + name: "自动建群", + description: "智能拉好友建群", + icon: ( + + ), + path: "/workspace/auto-group", + bgColor: "#f6ffed", + }, + { + id: "traffic-distribution", + name: "流量分发", + description: "管理流量分发和分配", + icon: ( + + ), + path: "/workspace/traffic-distribution", + bgColor: "#e6f7ff", + }, + { + id: "ai-assistant", + name: "AI对话助手", + description: "智能回复,提高互动质量", + icon: ( + + ), + path: "/workspace/ai-assistant", + bgColor: "#e6f7ff", + isNew: true, + }, + ]; + + // AI智能助手 + const aiFeatures = [ + { + id: "ai-analyzer", + name: "AI数据分析", + description: "智能分析客户行为特征", + icon: ( + + ), + path: "/workspace/ai-analyzer", + bgColor: "#f0f0ff", + isNew: true, + }, + { + id: "ai-strategy", + name: "AI策略优化", + description: "智能优化获客策略", + icon: , + path: "/workspace/ai-strategy", + bgColor: "#e6fffb", + isNew: true, + }, + { + id: "ai-forecast", + name: "AI销售预测", + description: "智能预测销售趋势", + icon: ( + + ), + path: "/workspace/ai-forecast", + bgColor: "#fffbe6", + }, + ]; + + return ( + +
+ 工作台 +
+ + } + footer={} + > +
+ {/* 任务统计卡片 */} +
+ +
总任务数
+
{taskStats.total}
+ +
+ 进行中: {taskStats.inProgress} / 已完成: {taskStats.completed} +
+
+ + +
今日任务
+
+ {taskStats.todayTasks} +
+
+ + + + 活跃度 {taskStats.activityRate}% +
+
+
+ + {/* 常用功能 */} +
+

常用功能

+
+ {commonFeatures.map((feature) => ( + + +
+ {feature.icon} +
+
+
{feature.name}
+ {feature.isNew && ( + + )} +
+
+ {feature.description} +
+
+ + ))} +
+
+ + {/* AI智能助手 */} +
+

AI 智能助手

+
+ {aiFeatures.map((feature) => ( + + +
+ {feature.icon} +
+
+
{feature.name}
+ {feature.isNew && ( + + )} +
+
+ {feature.description} +
+
+ + ))} +
+
+
+
+ ); +}; + +export default Workspace; diff --git a/nkebao/src/pages/workspace/ai-assistant/AIAssistant.tsx b/nkebao/src/pages/workspace/ai-assistant/AIAssistant.tsx new file mode 100644 index 000000000..2727f4534 --- /dev/null +++ b/nkebao/src/pages/workspace/ai-assistant/AIAssistant.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const AIAssistant: React.FC = () => { + return ; +}; + +export default AIAssistant; diff --git a/nkebao/src/pages/workspace/auto-group/AutoGroup.tsx b/nkebao/src/pages/workspace/auto-group/AutoGroup.tsx new file mode 100644 index 000000000..935827d66 --- /dev/null +++ b/nkebao/src/pages/workspace/auto-group/AutoGroup.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const AutoGroup: React.FC = () => { + return ( + + ); +}; + +export default AutoGroup; diff --git a/nkebao/src/pages/workspace/auto-group/Detail.tsx b/nkebao/src/pages/workspace/auto-group/Detail.tsx new file mode 100644 index 000000000..db34f3347 --- /dev/null +++ b/nkebao/src/pages/workspace/auto-group/Detail.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const AutoGroupDetail: React.FC = () => { + return ; +}; + +export default AutoGroupDetail; diff --git a/nkebao/src/pages/workspace/auto-like/AutoLike.tsx b/nkebao/src/pages/workspace/auto-like/AutoLike.tsx new file mode 100644 index 000000000..35f84a96c --- /dev/null +++ b/nkebao/src/pages/workspace/auto-like/AutoLike.tsx @@ -0,0 +1,38 @@ +import React from "react"; +import { NavBar, Button } from "antd-mobile"; +import { AddOutline } from "antd-mobile-icons"; +import Layout from "@/components/Layout/Layout"; +import MeauMobile from "@/components/MeauMobile/MeauMoible"; + +const AutoLike: React.FC = () => { + return ( + window.history.back()} + left={ +
+ 自动点赞 +
+ } + right={ + + } + /> + } + footer={} + > +
+

自动点赞页面

+

此页面正在开发中...

+
+
+ ); +}; + +export default AutoLike; diff --git a/nkebao/src/pages/workspace/auto-like/AutoLikeDetail.tsx b/nkebao/src/pages/workspace/auto-like/AutoLikeDetail.tsx new file mode 100644 index 000000000..088112610 --- /dev/null +++ b/nkebao/src/pages/workspace/auto-like/AutoLikeDetail.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const AutoLikeDetail: React.FC = () => { + return ; +}; + +export default AutoLikeDetail; diff --git a/nkebao/src/pages/workspace/auto-like/NewAutoLike.tsx b/nkebao/src/pages/workspace/auto-like/NewAutoLike.tsx new file mode 100644 index 000000000..12bf9b7ec --- /dev/null +++ b/nkebao/src/pages/workspace/auto-like/NewAutoLike.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import { NavBar } from "antd-mobile"; +import Layout from "@/components/Layout/Layout"; +import MeauMobile from "@/components/MeauMobile/MeauMoible"; + +const NewAutoLike: React.FC = () => { + return ( + window.history.back()} + > +
+ 新建自动点赞 +
+ + } + footer={} + > +
+

新建自动点赞页面

+

此页面正在开发中...

+
+
+ ); +}; + +export default NewAutoLike; diff --git a/nkebao/src/pages/workspace/group-push/GroupPush.tsx b/nkebao/src/pages/workspace/group-push/GroupPush.tsx new file mode 100644 index 000000000..382dab52a --- /dev/null +++ b/nkebao/src/pages/workspace/group-push/GroupPush.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const GroupPush: React.FC = () => { + return ( + + ); +}; + +export default GroupPush; diff --git a/nkebao/src/pages/workspace/group-push/new.tsx b/nkebao/src/pages/workspace/group-push/new.tsx new file mode 100644 index 000000000..7147f70c1 --- /dev/null +++ b/nkebao/src/pages/workspace/group-push/new.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const NewGroupPush: React.FC = () => { + return ; +}; + +export default NewGroupPush; diff --git a/nkebao/src/pages/work/api.ts b/nkebao/src/pages/workspace/main/api.ts similarity index 100% rename from nkebao/src/pages/work/api.ts rename to nkebao/src/pages/workspace/main/api.ts diff --git a/nkebao/src/pages/work/index.module.scss b/nkebao/src/pages/workspace/main/index.module.scss similarity index 100% rename from nkebao/src/pages/work/index.module.scss rename to nkebao/src/pages/workspace/main/index.module.scss diff --git a/nkebao/src/pages/work/index.tsx b/nkebao/src/pages/workspace/main/index.tsx similarity index 100% rename from nkebao/src/pages/work/index.tsx rename to nkebao/src/pages/workspace/main/index.tsx diff --git a/nkebao/src/pages/workspace/moments-sync/Detail.tsx b/nkebao/src/pages/workspace/moments-sync/Detail.tsx new file mode 100644 index 000000000..b46fd03c2 --- /dev/null +++ b/nkebao/src/pages/workspace/moments-sync/Detail.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const MomentsSyncDetail: React.FC = () => { + return ; +}; + +export default MomentsSyncDetail; diff --git a/nkebao/src/pages/workspace/moments-sync/MomentsSync.tsx b/nkebao/src/pages/workspace/moments-sync/MomentsSync.tsx new file mode 100644 index 000000000..b4564f838 --- /dev/null +++ b/nkebao/src/pages/workspace/moments-sync/MomentsSync.tsx @@ -0,0 +1,14 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const MomentsSync: React.FC = () => { + return ( + + ); +}; + +export default MomentsSync; diff --git a/nkebao/src/pages/workspace/moments-sync/new.tsx b/nkebao/src/pages/workspace/moments-sync/new.tsx new file mode 100644 index 000000000..b32d13a68 --- /dev/null +++ b/nkebao/src/pages/workspace/moments-sync/new.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const NewMomentsSync: React.FC = () => { + return ; +}; + +export default NewMomentsSync; diff --git a/nkebao/src/pages/workspace/traffic-distribution/Detail.tsx b/nkebao/src/pages/workspace/traffic-distribution/Detail.tsx new file mode 100644 index 000000000..f3469de15 --- /dev/null +++ b/nkebao/src/pages/workspace/traffic-distribution/Detail.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const TrafficDistributionDetail: React.FC = () => { + return ; +}; + +export default TrafficDistributionDetail; diff --git a/nkebao/src/pages/workspace/traffic-distribution/NewDistribution.tsx b/nkebao/src/pages/workspace/traffic-distribution/NewDistribution.tsx new file mode 100644 index 000000000..06053b35f --- /dev/null +++ b/nkebao/src/pages/workspace/traffic-distribution/NewDistribution.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const NewDistribution: React.FC = () => { + return ; +}; + +export default NewDistribution; diff --git a/nkebao/src/pages/workspace/traffic-distribution/TrafficDistribution.tsx b/nkebao/src/pages/workspace/traffic-distribution/TrafficDistribution.tsx new file mode 100644 index 000000000..701f347b9 --- /dev/null +++ b/nkebao/src/pages/workspace/traffic-distribution/TrafficDistribution.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const TrafficDistribution: React.FC = () => { + return ( + + ); +}; + +export default TrafficDistribution; diff --git a/nkebao/src/router/config.ts b/nkebao/src/router/config.ts new file mode 100644 index 000000000..e41b53726 --- /dev/null +++ b/nkebao/src/router/config.ts @@ -0,0 +1,171 @@ +// 路由配置类型定义 +export interface RouteConfig { + path: string; + element: React.ReactNode; + auth: boolean; + requiredRole?: string; + title?: string; + icon?: string; + children?: RouteConfig[]; +} + +// 路由分组配置 +export const routeGroups = { + // 基础路由 + basic: { + name: "基础功能", + routes: ["/", "/login", "/scene", "/work", "/mine"], + }, + + // 设备管理 + devices: { + name: "设备管理", + routes: ["/devices", "/devices/:id"], + }, + + // 微信号管理 + wechatAccounts: { + name: "微信号管理", + routes: ["/wechat-accounts", "/wechat-accounts/:id"], + }, + + // 工作台 + workspace: { + name: "工作台", + routes: [ + "/workspace", + "/workspace/auto-like", + "/workspace/auto-like/new", + "/workspace/auto-like/:id", + "/workspace/auto-like/:id/edit", + "/workspace/auto-group", + "/workspace/auto-group/:id", + "/workspace/group-push", + "/workspace/group-push/new", + "/workspace/group-push/:id", + "/workspace/group-push/:id/edit", + "/workspace/moments-sync", + "/workspace/moments-sync/new", + "/workspace/moments-sync/:id", + "/workspace/moments-sync/edit/:id", + "/workspace/ai-assistant", + "/workspace/traffic-distribution", + "/workspace/traffic-distribution/new", + "/workspace/traffic-distribution/edit/:id", + "/workspace/traffic-distribution/:id", + ], + }, + + // 场景管理 + scenarios: { + name: "场景管理", + routes: [ + "/scenarios", + "/scenarios/new", + "/scenarios/new/:scenarioId", + "/scenarios/edit/:planId", + "/scenarios/list/:scenarioId/:scenarioName", + ], + }, + + // 内容管理 + content: { + name: "内容管理", + routes: [ + "/content", + "/content/new", + "/content/edit/:id", + "/content/materials/:id", + "/content/materials/new/:id", + "/content/materials/edit/:id/:materialId", + ], + }, + + // 流量池 + trafficPool: { + name: "流量池", + routes: ["/traffic-pool", "/traffic-pool/:id"], + }, + + // 其他功能 + other: { + name: "其他功能", + routes: [ + "/profile", + "/plans", + "/plans/:planId", + "/orders", + "/contact-import", + ], + }, +}; + +// 路由权限配置 +export const routePermissions = { + // 管理员权限 + admin: Object.values(routeGroups).flatMap(group => group.routes), + + // 普通用户权限 + user: [ + "/", + "/login", + "/scene", + "/work", + "/mine", + "/devices", + "/devices/:id", + "/wechat-accounts", + "/wechat-accounts/:id", + "/workspace", + "/scenarios", + "/content", + "/traffic-pool", + "/traffic-pool/:id", + "/profile", + "/plans", + "/plans/:planId", + "/orders", + "/contact-import", + ], + + // 访客权限 + guest: ["/", "/login"], +}; + +// 路由标题映射 +export const routeTitles: Record = { + "/": "首页", + "/login": "登录", + "/scene": "场景获客", + "/work": "工作台", + "/mine": "我的", + "/devices": "设备管理", + "/wechat-accounts": "微信号管理", + "/workspace": "工作台", + "/scenarios": "场景管理", + "/content": "内容管理", + "/traffic-pool": "流量池", + "/profile": "个人中心", + "/plans": "计划管理", + "/orders": "订单管理", + "/contact-import": "联系人导入", +}; + +// 获取路由标题 +export const getRouteTitle = (path: string): string => { + return routeTitles[path] || "页面"; +}; + +// 检查路由权限 +export const checkRoutePermission = ( + path: string, + userRole: string = "user" +): boolean => { + const allowedRoutes = routePermissions[userRole as keyof typeof routePermissions] || []; + return allowedRoutes.some(route => { + // 简单的路径匹配,支持动态参数 + const routePattern = route.replace(/:[^/]+/g, "[^/]+"); + const regex = new RegExp(`^${routePattern}$`); + return regex.test(path); + }); +}; \ No newline at end of file diff --git a/nkebao/src/router/module/auth.tsx b/nkebao/src/router/module/auth.tsx new file mode 100644 index 000000000..d6ddc5cf6 --- /dev/null +++ b/nkebao/src/router/module/auth.tsx @@ -0,0 +1,11 @@ +import Login from "@/pages/login/login"; + +const authRoutes = [ + { + path: "/login", + element: , + auth: false, // 不需要权限 + }, +]; + +export default authRoutes; diff --git a/nkebao/src/router/module/content.tsx b/nkebao/src/router/module/content.tsx new file mode 100644 index 000000000..7e941805f --- /dev/null +++ b/nkebao/src/router/module/content.tsx @@ -0,0 +1,39 @@ +import Content from "@/pages/content/Content"; +import NewContent from "@/pages/content/NewContent"; +import Materials from "@/pages/content/materials/List"; +import MaterialsNew from "@/pages/content/materials/New"; + +const contentRoutes = [ + { + path: "/content", + element: , + auth: true, + }, + { + path: "/content/new", + element: , + auth: true, + }, + { + path: "/content/edit/:id", + element: , + auth: true, + }, + { + path: "/content/materials/:id", + element: , + auth: true, + }, + { + path: "/content/materials/new/:id", + element: , + auth: true, + }, + { + path: "/content/materials/edit/:id/:materialId", + element: , + auth: true, + }, +]; + +export default contentRoutes; diff --git a/nkebao/src/router/module/devices.tsx b/nkebao/src/router/module/devices.tsx new file mode 100644 index 000000000..d31db945d --- /dev/null +++ b/nkebao/src/router/module/devices.tsx @@ -0,0 +1,17 @@ +import Devices from "@/pages/devices/Devices"; +import DeviceDetail from "@/pages/devices/DeviceDetail"; + +const deviceRoutes = [ + { + path: "/devices", + element: , + auth: true, + }, + { + path: "/devices/:id", + element: , + auth: true, + }, +]; + +export default deviceRoutes; diff --git a/nkebao/src/router/module/index.tsx b/nkebao/src/router/module/index.tsx index f92633a82..60033084a 100644 --- a/nkebao/src/router/module/index.tsx +++ b/nkebao/src/router/module/index.tsx @@ -1,20 +1,15 @@ import Home from "@/pages/home/index"; -import Login from "@/pages/login/login"; import Scene from "@/pages/scene/index"; -import Work from "@/pages/work/index"; import Mine from "@/pages/mine/index"; +import Workspace from "@/pages/workspace/main"; const routes = [ + // 基础路由 { path: "/", element: , auth: true, // 需要登录 }, - { - path: "/login", - element: , - auth: false, // 不需要权限 - }, { path: "/scene", element: , @@ -22,7 +17,7 @@ const routes = [ }, { path: "/work", - element: , + element: , auth: true, }, { diff --git a/nkebao/src/router/module/other.tsx b/nkebao/src/router/module/other.tsx new file mode 100644 index 000000000..a458b7dca --- /dev/null +++ b/nkebao/src/router/module/other.tsx @@ -0,0 +1,40 @@ +import Profile from "@/pages/profile/Profile"; +import Plans from "@/pages/plans/Plans"; +import PlanDetail from "@/pages/plans/PlanDetail"; +import Orders from "@/pages/orders/Orders"; +import ContactImport from "@/pages/contact-import/ContactImport"; + +const otherRoutes = [ + { + path: "/mine", + element: , + auth: true, + }, + { + path: "/profile", + element: , + auth: true, + }, + { + path: "/plans", + element: , + auth: true, + }, + { + path: "/plans/:planId", + element: , + auth: true, + }, + { + path: "/orders", + element: , + auth: true, + }, + { + path: "/contact-import", + element: , + auth: true, + }, +]; + +export default otherRoutes; diff --git a/nkebao/src/router/module/scenarios.tsx b/nkebao/src/router/module/scenarios.tsx new file mode 100644 index 000000000..ff561561a --- /dev/null +++ b/nkebao/src/router/module/scenarios.tsx @@ -0,0 +1,38 @@ +import Scenarios from "@/pages/scenarios/Scenarios"; +import NewPlan from "@/pages/scenarios/new/page"; +import ScenarioList from "@/pages/scenarios/ScenarioList"; + +const scenarioRoutes = [ + { + path: "/scene", + element: , + auth: true, + }, + { + path: "/scenarios", + element: , + auth: true, + }, + { + path: "/scenarios/new", + element: , + auth: true, + }, + { + path: "/scenarios/new/:scenarioId", + element: , + auth: true, + }, + { + path: "/scenarios/edit/:planId", + element: , + auth: true, + }, + { + path: "/scenarios/list/:scenarioId/:scenarioName", + element: , + auth: true, + }, +]; + +export default scenarioRoutes; diff --git a/nkebao/src/router/module/traffic-pool.tsx b/nkebao/src/router/module/traffic-pool.tsx new file mode 100644 index 000000000..d0dcd70d5 --- /dev/null +++ b/nkebao/src/router/module/traffic-pool.tsx @@ -0,0 +1,17 @@ +import TrafficPool from "@/pages/traffic-pool/TrafficPool"; +import TrafficPoolDetail from "@/pages/traffic-pool/TrafficPoolDetail"; + +const trafficPoolRoutes = [ + { + path: "/traffic-pool", + element: , + auth: true, + }, + { + path: "/traffic-pool/:id", + element: , + auth: true, + }, +]; + +export default trafficPoolRoutes; diff --git a/nkebao/src/router/module/wechat-accounts.tsx b/nkebao/src/router/module/wechat-accounts.tsx new file mode 100644 index 000000000..496ab0065 --- /dev/null +++ b/nkebao/src/router/module/wechat-accounts.tsx @@ -0,0 +1,17 @@ +import WechatAccounts from "@/pages/wechat-accounts/WechatAccounts"; +import WechatAccountDetail from "@/pages/wechat-accounts/WechatAccountDetail"; + +const wechatAccountRoutes = [ + { + path: "/wechat-accounts", + element: , + auth: true, + }, + { + path: "/wechat-accounts/:id", + element: , + auth: true, + }, +]; + +export default wechatAccountRoutes; diff --git a/nkebao/src/router/module/workspace.tsx b/nkebao/src/router/module/workspace.tsx new file mode 100644 index 000000000..64fdf386a --- /dev/null +++ b/nkebao/src/router/module/workspace.tsx @@ -0,0 +1,145 @@ +import Workspace from "@/pages/workspace/Workspace"; +import AutoLike from "@/pages/workspace/auto-like/AutoLike"; +import NewAutoLike from "@/pages/workspace/auto-like/NewAutoLike"; +import AutoLikeDetail from "@/pages/workspace/auto-like/AutoLikeDetail"; +import AutoGroup from "@/pages/workspace/auto-group/AutoGroup"; +import AutoGroupDetail from "@/pages/workspace/auto-group/Detail"; +import GroupPush from "@/pages/workspace/group-push/GroupPush"; +import NewGroupPush from "@/pages/workspace/group-push/new"; +import MomentsSync from "@/pages/workspace/moments-sync/MomentsSync"; +import MomentsSyncDetail from "@/pages/workspace/moments-sync/Detail"; +import NewMomentsSync from "@/pages/workspace/moments-sync/new"; +import AIAssistant from "@/pages/workspace/ai-assistant/AIAssistant"; +import TrafficDistribution from "@/pages/workspace/traffic-distribution/TrafficDistribution"; +import TrafficDistributionDetail from "@/pages/workspace/traffic-distribution/Detail"; +import NewDistribution from "@/pages/workspace/traffic-distribution/NewDistribution"; +import PlaceholderPage from "@/components/PlaceholderPage"; + +const workspaceRoutes = [ + { + path: "/workspace", + element: , + auth: true, + }, + // 自动点赞 + { + path: "/workspace/auto-like", + element: , + auth: true, + }, + { + path: "/workspace/auto-like/new", + element: , + auth: true, + }, + { + path: "/workspace/auto-like/:id", + element: , + auth: true, + }, + { + path: "/workspace/auto-like/:id/edit", + element: , + auth: true, + }, + // 自动分组 + { + path: "/workspace/auto-group", + element: , + auth: true, + }, + { + path: "/workspace/auto-group/:id", + element: , + auth: true, + }, + // 群发推送 + { + path: "/workspace/group-push", + element: , + auth: true, + }, + { + path: "/workspace/group-push/new", + element: , + auth: true, + }, + { + path: "/workspace/group-push/:id", + element: , + auth: true, + }, + { + path: "/workspace/group-push/:id/edit", + element: , + auth: true, + }, + // 朋友圈同步 + { + path: "/workspace/moments-sync", + element: , + auth: true, + }, + { + path: "/workspace/moments-sync/new", + element: , + auth: true, + }, + { + path: "/workspace/moments-sync/:id", + element: , + auth: true, + }, + { + path: "/workspace/moments-sync/edit/:id", + element: , + auth: true, + }, + // AI助手 + { + path: "/workspace/ai-assistant", + element: , + auth: true, + }, + // AI数据分析 + { + path: "/workspace/ai-analyzer", + element: , + auth: true, + }, + // AI策略优化 + { + path: "/workspace/ai-strategy", + element: , + auth: true, + }, + // AI销售预测 + { + path: "/workspace/ai-forecast", + element: , + auth: true, + }, + // 流量分发 + { + path: "/workspace/traffic-distribution", + element: , + auth: true, + }, + { + path: "/workspace/traffic-distribution/new", + element: , + auth: true, + }, + { + path: "/workspace/traffic-distribution/edit/:id", + element: , + auth: true, + }, + { + path: "/workspace/traffic-distribution/:id", + element: , + auth: true, + }, +]; + +export default workspaceRoutes; diff --git a/nkebao/src/styles/global.scss b/nkebao/src/styles/global.scss index 079b5d0ce..bf7670a8f 100644 --- a/nkebao/src/styles/global.scss +++ b/nkebao/src/styles/global.scss @@ -122,4 +122,13 @@ input, textarea { /* 安卓部分 WebView 点击延迟优化 */ body, input, textarea, select, button { touch-action: manipulation; -} \ No newline at end of file +} +//导航左右结构的样式 +.nav-left { + color: var(--primary-color); + font-weight: 700; + font-size: 16px; +} +.nav-right { + font-size: 12px; +} \ No newline at end of file From 4af615889b1a26e1d3f9238ca0bb397d5297f675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Sat, 19 Jul 2025 14:17:05 +0800 Subject: [PATCH 23/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E5=AD=98=E4=B8=80=E6=B3=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/package-lock.json | 6402 ++++++++++++++++++++++ nkebao/package.json | 1 + nkebao/src/pages/home/index.tsx | 12 +- nkebao/src/pages/mine/index.module.scss | 193 +- nkebao/src/pages/mine/index.tsx | 219 +- nkebao/src/pages/scene/index.module.scss | 162 +- nkebao/src/pages/scene/index.tsx | 173 +- nkebao/src/pages/workspace/Workspace.tsx | 53 +- 8 files changed, 6879 insertions(+), 336 deletions(-) create mode 100644 nkebao/package-lock.json diff --git a/nkebao/package-lock.json b/nkebao/package-lock.json new file mode 100644 index 000000000..9be58e0b7 --- /dev/null +++ b/nkebao/package-lock.json @@ -0,0 +1,6402 @@ +{ + "name": "nkebao-base", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "nkebao-base", + "version": "0.1.0", + "dependencies": { + "@ant-design/icons": "^5.6.1", + "antd": "^5.13.1", + "antd-mobile": "^5.39.1", + "axios": "^1.6.7", + "echarts": "^5.6.0", + "echarts-for-react": "^3.0.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.20.0", + "zustand": "^5.0.6" + }, + "devDependencies": { + "@types/node": "^24.0.14", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@typescript-eslint/eslint-plugin": "^7.7.0", + "@typescript-eslint/parser": "^7.7.0", + "@vitejs/plugin-react": "^4.6.0", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-react": "^7.34.1", + "postcss": "^8.4.38", + "postcss-pxtorem": "^6.0.0", + "prettier": "^3.2.5", + "sass": "^1.75.0", + "typescript": "^5.4.5", + "vite": "^7.0.5" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@ant-design/colors": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-7.2.1.tgz", + "integrity": "sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==", + "license": "MIT", + "dependencies": { + "@ant-design/fast-color": "^2.0.6" + } + }, + "node_modules/@ant-design/cssinjs": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.23.0.tgz", + "integrity": "sha512-7GAg9bD/iC9ikWatU9ym+P9ugJhi/WbsTWzcKN6T4gU0aehsprtke1UAaaSxxkjjmkJb3llet/rbUSLPgwlY4w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@emotion/hash": "^0.8.0", + "@emotion/unitless": "^0.7.5", + "classnames": "^2.3.1", + "csstype": "^3.1.3", + "rc-util": "^5.35.0", + "stylis": "^4.3.4" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/cssinjs-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz", + "integrity": "sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^1.21.0", + "@babel/runtime": "^7.23.2", + "rc-util": "^5.38.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@ant-design/fast-color": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-2.0.6.tgz", + "integrity": "sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@ant-design/icons": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-5.6.1.tgz", + "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^7.0.0", + "@ant-design/icons-svg": "^4.4.0", + "@babel/runtime": "^7.24.8", + "classnames": "^2.2.6", + "rc-util": "^5.31.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", + "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==", + "license": "MIT" + }, + "node_modules/@ant-design/react-slick": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.1.2.tgz", + "integrity": "sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.4", + "classnames": "^2.2.5", + "json2mq": "^0.2.0", + "resize-observer-polyfill": "^1.5.1", + "throttle-debounce": "^5.0.0" + }, + "peerDependencies": { + "react": ">=16.9.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", + "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", + "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.2.tgz", + "integrity": "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.2.tgz", + "integrity": "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.2", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.7.tgz", + "integrity": "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@rc-component/async-validator": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.0.4.tgz", + "integrity": "sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.4" + }, + "engines": { + "node": ">=14.x" + } + }, + "node_modules/@rc-component/color-picker": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-2.0.1.tgz", + "integrity": "sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==", + "license": "MIT", + "dependencies": { + "@ant-design/fast-color": "^2.0.6", + "@babel/runtime": "^7.23.6", + "classnames": "^2.2.6", + "rc-util": "^5.38.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/context": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-1.4.0.tgz", + "integrity": "sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/mini-decimal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz", + "integrity": "sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@rc-component/mutate-observer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz", + "integrity": "sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/portal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz", + "integrity": "sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/qrcode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.0.0.tgz", + "integrity": "sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7", + "classnames": "^2.3.2", + "rc-util": "^5.38.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tour": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-1.15.1.tgz", + "integrity": "sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0", + "@rc-component/portal": "^1.0.0-9", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.3.2", + "rc-util": "^5.24.4" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/trigger": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.2.7.tgz", + "integrity": "sha512-Qggj4Z0AA2i5dJhzlfFSmg1Qrziu8dsdHOihROL5Kl18seO2Eh/ZaTYt2c8a/CyGaTChnFry7BEYew1+/fhSbA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2", + "@rc-component/portal": "^1.1.0", + "classnames": "^2.3.2", + "rc-motion": "^2.0.0", + "rc-resize-observer": "^1.3.1", + "rc-util": "^5.44.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@react-spring/animated": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.6.1.tgz", + "integrity": "sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ==", + "license": "MIT", + "dependencies": { + "@react-spring/shared": "~9.6.1", + "@react-spring/types": "~9.6.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/core": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.6.1.tgz", + "integrity": "sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ==", + "license": "MIT", + "dependencies": { + "@react-spring/animated": "~9.6.1", + "@react-spring/rafz": "~9.6.1", + "@react-spring/shared": "~9.6.1", + "@react-spring/types": "~9.6.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-spring/donate" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/rafz": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.6.1.tgz", + "integrity": "sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ==", + "license": "MIT" + }, + "node_modules/@react-spring/shared": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.6.1.tgz", + "integrity": "sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw==", + "license": "MIT", + "dependencies": { + "@react-spring/rafz": "~9.6.1", + "@react-spring/types": "~9.6.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/types": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.6.1.tgz", + "integrity": "sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q==", + "license": "MIT" + }, + "node_modules/@react-spring/web": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.6.1.tgz", + "integrity": "sha512-X2zR6q2Z+FjsWfGAmAXlQaoUHbPmfuCaXpuM6TcwXPpLE1ZD4A1eys/wpXboFQmDkjnrlTmKvpVna1MjWpZ5Hw==", + "license": "MIT", + "dependencies": { + "@react-spring/animated": "~9.6.1", + "@react-spring/core": "~9.6.1", + "@react-spring/shared": "~9.6.1", + "@react-spring/types": "~9.6.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@remix-run/router": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", + "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.19", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz", + "integrity": "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", + "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.0.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.14.tgz", + "integrity": "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@types/react": { + "version": "19.1.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", + "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", + "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", + "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/type-utils": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", + "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", + "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "7.18.0", + "@typescript-eslint/utils": "7.18.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@use-gesture/core": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.0.tgz", + "integrity": "sha512-rh+6MND31zfHcy9VU3dOZCqGY511lvGcfyJenN4cWZe0u1BH6brBpBddLVXhF2r4BMqWbvxfsbL7D287thJU2A==", + "license": "MIT" + }, + "node_modules/@use-gesture/react": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.0.tgz", + "integrity": "sha512-3zc+Ve99z4usVP6l9knYVbVnZgfqhKah7sIG+PS2w+vpig2v2OLct05vs+ZXMzwxdNCMka8B+8WlOo0z6Pn6DA==", + "license": "MIT", + "dependencies": { + "@use-gesture/core": "10.3.0" + }, + "peerDependencies": { + "react": ">= 16.8.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.6.0.tgz", + "integrity": "sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.19", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ahooks": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ahooks/-/ahooks-3.9.0.tgz", + "integrity": "sha512-r20/C38aFyU3Zqp3620gkdLnxmQhnmWORB3eGGTDlM4i/fOc0GUvM+f2oleMzEu7b3+pHXyzz+FB6ojxsUdYdw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0", + "dayjs": "^1.9.1", + "intersection-observer": "^0.12.0", + "js-cookie": "^3.0.5", + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.2", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.0.0", + "tslib": "^2.4.1" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/ahooks/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/antd": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/antd/-/antd-5.26.5.tgz", + "integrity": "sha512-HB7Cr0tPZMMeAyDDH8KZU0aP8/uO51oasmflJhDBzaRRZmLT8Pyjtt8qS22Sc839glm8gpKQcaG0mln66Gt9Fg==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^7.2.1", + "@ant-design/cssinjs": "^1.23.0", + "@ant-design/cssinjs-utils": "^1.1.3", + "@ant-design/fast-color": "^2.0.6", + "@ant-design/icons": "^5.6.1", + "@ant-design/react-slick": "~1.1.2", + "@babel/runtime": "^7.26.0", + "@rc-component/color-picker": "~2.0.1", + "@rc-component/mutate-observer": "^1.1.0", + "@rc-component/qrcode": "~1.0.0", + "@rc-component/tour": "~1.15.1", + "@rc-component/trigger": "^2.2.7", + "classnames": "^2.5.1", + "copy-to-clipboard": "^3.3.3", + "dayjs": "^1.11.11", + "rc-cascader": "~3.34.0", + "rc-checkbox": "~3.5.0", + "rc-collapse": "~3.9.0", + "rc-dialog": "~9.6.0", + "rc-drawer": "~7.3.0", + "rc-dropdown": "~4.2.1", + "rc-field-form": "~2.7.0", + "rc-image": "~7.12.0", + "rc-input": "~1.8.0", + "rc-input-number": "~9.5.0", + "rc-mentions": "~2.20.0", + "rc-menu": "~9.16.1", + "rc-motion": "^2.9.5", + "rc-notification": "~5.6.4", + "rc-pagination": "~5.1.0", + "rc-picker": "~4.11.3", + "rc-progress": "~4.0.0", + "rc-rate": "~2.13.1", + "rc-resize-observer": "^1.4.3", + "rc-segmented": "~2.7.0", + "rc-select": "~14.16.8", + "rc-slider": "~11.1.8", + "rc-steps": "~6.0.1", + "rc-switch": "~4.1.0", + "rc-table": "~7.51.1", + "rc-tabs": "~15.6.1", + "rc-textarea": "~1.10.0", + "rc-tooltip": "~6.4.0", + "rc-tree": "~5.13.1", + "rc-tree-select": "~5.27.0", + "rc-upload": "~4.9.2", + "rc-util": "^5.44.4", + "scroll-into-view-if-needed": "^3.1.0", + "throttle-debounce": "^5.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ant-design" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/antd-mobile": { + "version": "5.40.0", + "resolved": "https://registry.npmjs.org/antd-mobile/-/antd-mobile-5.40.0.tgz", + "integrity": "sha512-nNfkTLiYPsa7A2i7eoG/hr2BM0agR4cfugE2+5HTyGnCg5xQT04Pmt9qEoKv7MOW5BaiiMyjO462Kh8KRF5QBA==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.4.2", + "@rc-component/mini-decimal": "^1.1.0", + "@react-spring/web": "~9.6.1", + "@use-gesture/react": "10.3.0", + "ahooks": "^3.7.6", + "antd-mobile-icons": "^0.3.0", + "antd-mobile-v5-count": "^1.0.1", + "classnames": "^2.3.2", + "dayjs": "^1.11.7", + "deepmerge": "^4.3.1", + "nano-memoize": "^3.0.16", + "rc-field-form": "^1.34.2", + "rc-segmented": "~2.4.1", + "rc-util": "^5.44.4", + "react-fast-compare": "^3.2.2", + "react-is": "^18.2.0", + "runes2": "^1.1.2", + "staged-components": "^1.1.3", + "tslib": "^2.5.0", + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/antd-mobile-icons": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/antd-mobile-icons/-/antd-mobile-icons-0.3.0.tgz", + "integrity": "sha512-rqINQpJWZWrva9moCd1Ye695MZYWmqLPE+bY8d2xLRy7iSQwPsinCdZYjpUPp2zL/LnKYSyXxP2ut2A+DC+whQ==", + "license": "MIT" + }, + "node_modules/antd-mobile-v5-count": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/antd-mobile-v5-count/-/antd-mobile-v5-count-1.0.1.tgz", + "integrity": "sha512-YGsiEDCPUDz3SzfXi6gLZn/HpeSMW+jgPc4qiYUr1fSopg3hkUie2TnooJdExgfiETHefH3Ggs58He0OVfegLA==", + "license": "MIT" + }, + "node_modules/antd-mobile/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/antd/node_modules/rc-field-form": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.7.0.tgz", + "integrity": "sha512-hgKsCay2taxzVnBPZl+1n4ZondsV78G++XVsMIJCAoioMjlMQR9YwAp7JZDIECzIu2Z66R+f4SFIRrO2DjDNAA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0", + "@rc-component/async-validator": "^5.0.3", + "rc-util": "^5.32.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/antd/node_modules/rc-segmented": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.7.0.tgz", + "integrity": "sha512-liijAjXz+KnTRVnxxXG2sYDGd6iLL7VpGGdR8gwoxAXy2KglviKCxLWZdjKYJzYzGSUwKDSTdYk8brj54Bn5BA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-motion": "^2.4.4", + "rc-util": "^5.17.0" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001727", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz", + "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "license": "MIT", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/echarts": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.6.0.tgz", + "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.6.1" + } + }, + "node_modules/echarts-for-react": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/echarts-for-react/-/echarts-for-react-3.0.2.tgz", + "integrity": "sha512-DRwIiTzx8JfwPOVgGttDytBqdp5VzCSyMRIxubgU/g2n9y3VLUmF2FK7Icmg/sNVkv4+rktmrLN9w22U2yy3fA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "size-sensor": "^1.0.1" + }, + "peerDependencies": { + "echarts": "^3.0.0 || ^4.0.0 || ^5.0.0", + "react": "^15.0.0 || >=16.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.186", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.186.tgz", + "integrity": "sha512-lur7L4BFklgepaJxj4DqPk7vKbTEl0pajNlg2QjE5shefmlmBLm2HvQ7PMf1R/GvlevT/581cop33/quQcfX3A==", + "dev": true, + "license": "ISC" + }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.25.6", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", + "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.6", + "@esbuild/android-arm": "0.25.6", + "@esbuild/android-arm64": "0.25.6", + "@esbuild/android-x64": "0.25.6", + "@esbuild/darwin-arm64": "0.25.6", + "@esbuild/darwin-x64": "0.25.6", + "@esbuild/freebsd-arm64": "0.25.6", + "@esbuild/freebsd-x64": "0.25.6", + "@esbuild/linux-arm": "0.25.6", + "@esbuild/linux-arm64": "0.25.6", + "@esbuild/linux-ia32": "0.25.6", + "@esbuild/linux-loong64": "0.25.6", + "@esbuild/linux-mips64el": "0.25.6", + "@esbuild/linux-ppc64": "0.25.6", + "@esbuild/linux-riscv64": "0.25.6", + "@esbuild/linux-s390x": "0.25.6", + "@esbuild/linux-x64": "0.25.6", + "@esbuild/netbsd-arm64": "0.25.6", + "@esbuild/netbsd-x64": "0.25.6", + "@esbuild/openbsd-arm64": "0.25.6", + "@esbuild/openbsd-x64": "0.25.6", + "@esbuild/openharmony-arm64": "0.25.6", + "@esbuild/sunos-x64": "0.25.6", + "@esbuild/win32-arm64": "0.25.6", + "@esbuild/win32-ia32": "0.25.6", + "@esbuild/win32-x64": "0.25.6" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", + "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/intersection-observer": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.12.2.tgz", + "integrity": "sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==", + "license": "Apache-2.0" + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "license": "MIT", + "dependencies": { + "string-convert": "^0.2.0" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nano-memoize": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/nano-memoize/-/nano-memoize-3.0.16.tgz", + "integrity": "sha512-JyK96AKVGAwVeMj3MoMhaSXaUNqgMbCRSQB3trUV8tYZfWEzqUBKdK1qJpfuNXgKeHOx1jv/IEYTM659ly7zUA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-pxtorem": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-pxtorem/-/postcss-pxtorem-6.1.0.tgz", + "integrity": "sha512-ROODSNci9ADal3zUcPHOF/K83TiCgNSPXQFSbwyPHNV8ioHIE4SaC+FPOufd8jsr5jV2uIz29v1Uqy1c4ov42g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/rc-cascader": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.34.0.tgz", + "integrity": "sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.7", + "classnames": "^2.3.1", + "rc-select": "~14.16.2", + "rc-tree": "~5.13.0", + "rc-util": "^5.43.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-checkbox": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-3.5.0.tgz", + "integrity": "sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.3.2", + "rc-util": "^5.25.2" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-collapse": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.9.0.tgz", + "integrity": "sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.3.4", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-dialog": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.6.0.tgz", + "integrity": "sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/portal": "^1.0.0-8", + "classnames": "^2.2.6", + "rc-motion": "^2.3.0", + "rc-util": "^5.21.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-drawer": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-7.3.0.tgz", + "integrity": "sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@rc-component/portal": "^1.1.1", + "classnames": "^2.2.6", + "rc-motion": "^2.6.1", + "rc-util": "^5.38.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-dropdown": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.2.1.tgz", + "integrity": "sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.6", + "rc-util": "^5.44.1" + }, + "peerDependencies": { + "react": ">=16.11.0", + "react-dom": ">=16.11.0" + } + }, + "node_modules/rc-field-form": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-1.44.0.tgz", + "integrity": "sha512-el7w87fyDUsca63Y/s8qJcq9kNkf/J5h+iTdqG5WsSHLH0e6Usl7QuYSmSVzJMgtp40mOVZIY/W/QP9zwrp1FA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0", + "async-validator": "^4.1.0", + "rc-util": "^5.32.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-image": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/rc-image/-/rc-image-7.12.0.tgz", + "integrity": "sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.2", + "@rc-component/portal": "^1.0.2", + "classnames": "^2.2.6", + "rc-dialog": "~9.6.0", + "rc-motion": "^2.6.2", + "rc-util": "^5.34.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-input": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/rc-input/-/rc-input-1.8.0.tgz", + "integrity": "sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.18.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/rc-input-number": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.5.0.tgz", + "integrity": "sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/mini-decimal": "^1.0.1", + "classnames": "^2.2.5", + "rc-input": "~1.8.0", + "rc-util": "^5.40.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-mentions": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.20.0.tgz", + "integrity": "sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.22.5", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.6", + "rc-input": "~1.8.0", + "rc-menu": "~9.16.0", + "rc-textarea": "~1.10.0", + "rc-util": "^5.34.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-menu": { + "version": "9.16.1", + "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-9.16.1.tgz", + "integrity": "sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/trigger": "^2.0.0", + "classnames": "2.x", + "rc-motion": "^2.4.3", + "rc-overflow": "^1.3.1", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-motion": { + "version": "2.9.5", + "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.5.tgz", + "integrity": "sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-util": "^5.44.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-notification": { + "version": "5.6.4", + "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-5.6.4.tgz", + "integrity": "sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.9.0", + "rc-util": "^5.20.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-overflow": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.4.1.tgz", + "integrity": "sha512-3MoPQQPV1uKyOMVNd6SZfONi+f3st0r8PksexIdBTeIYbMX0Jr+k7pHEDvsXtR4BpCv90/Pv2MovVNhktKrwvw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.37.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-pagination": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-5.1.0.tgz", + "integrity": "sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.3.2", + "rc-util": "^5.38.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-picker": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-4.11.3.tgz", + "integrity": "sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.2.1", + "rc-overflow": "^1.3.2", + "rc-resize-observer": "^1.4.0", + "rc-util": "^5.43.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "date-fns": ">= 2.x", + "dayjs": ">= 1.x", + "luxon": ">= 3.x", + "moment": ">= 2.x", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + }, + "peerDependenciesMeta": { + "date-fns": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + } + } + }, + "node_modules/rc-progress": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-4.0.0.tgz", + "integrity": "sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.6", + "rc-util": "^5.16.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-rate": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.13.1.tgz", + "integrity": "sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.0.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-resize-observer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz", + "integrity": "sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.7", + "classnames": "^2.2.1", + "rc-util": "^5.44.1", + "resize-observer-polyfill": "^1.5.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-segmented": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.4.1.tgz", + "integrity": "sha512-KUi+JJFdKnumV9iXlm+BJ00O4NdVBp2TEexLCk6bK1x/RH83TvYKQMzIz/7m3UTRPD08RM/8VG/JNjWgWbd4cw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "rc-motion": "^2.4.4", + "rc-util": "^5.17.0" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/rc-select": { + "version": "14.16.8", + "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.16.8.tgz", + "integrity": "sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/trigger": "^2.1.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-overflow": "^1.3.1", + "rc-util": "^5.16.1", + "rc-virtual-list": "^3.5.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-slider": { + "version": "11.1.8", + "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.8.tgz", + "integrity": "sha512-2gg/72YFSpKP+Ja5AjC5DPL1YnV8DEITDQrcc1eASrUYjl0esptaBVJBh5nLTXCCp15eD8EuGjwezVGSHhs9tQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.5", + "rc-util": "^5.36.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-steps": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-6.0.1.tgz", + "integrity": "sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.16.7", + "classnames": "^2.2.3", + "rc-util": "^5.16.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-switch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-4.1.0.tgz", + "integrity": "sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0", + "classnames": "^2.2.1", + "rc-util": "^5.30.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-table": { + "version": "7.51.1", + "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.51.1.tgz", + "integrity": "sha512-5iq15mTHhvC42TlBLRCoCBLoCmGlbRZAlyF21FonFnS/DIC8DeRqnmdyVREwt2CFbPceM0zSNdEeVfiGaqYsKw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/context": "^1.4.0", + "classnames": "^2.2.5", + "rc-resize-observer": "^1.1.0", + "rc-util": "^5.44.3", + "rc-virtual-list": "^3.14.2" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tabs": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.6.1.tgz", + "integrity": "sha512-/HzDV1VqOsUWyuC0c6AkxVYFjvx9+rFPKZ32ejxX0Uc7QCzcEjTA9/xMgv4HemPKwzBNX8KhGVbbumDjnj92aA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.2", + "classnames": "2.x", + "rc-dropdown": "~4.2.0", + "rc-menu": "~9.16.0", + "rc-motion": "^2.6.2", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.34.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-textarea": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.10.0.tgz", + "integrity": "sha512-ai9IkanNuyBS4x6sOL8qu/Ld40e6cEs6pgk93R+XLYg0mDSjNBGey6/ZpDs5+gNLD7urQ14po3V6Ck2dJLt9SA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "^2.2.1", + "rc-input": "~1.8.0", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.27.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tooltip": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-6.4.0.tgz", + "integrity": "sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.2", + "@rc-component/trigger": "^2.0.0", + "classnames": "^2.3.1", + "rc-util": "^5.44.3" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-tree": { + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-5.13.1.tgz", + "integrity": "sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "rc-motion": "^2.0.1", + "rc-util": "^5.16.1", + "rc-virtual-list": "^3.5.1" + }, + "engines": { + "node": ">=10.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-tree-select": { + "version": "5.27.0", + "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.27.0.tgz", + "integrity": "sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.7", + "classnames": "2.x", + "rc-select": "~14.16.2", + "rc-tree": "~5.13.0", + "rc-util": "^5.43.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/rc-upload": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.9.2.tgz", + "integrity": "sha512-nHx+9rbd1FKMiMRYsqQ3NkXUv7COHPBo3X1Obwq9SWS6/diF/A0aJ5OHubvwUAIDs+4RMleljV0pcrNUc823GQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "classnames": "^2.2.5", + "rc-util": "^5.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-util": { + "version": "5.44.4", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz", + "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "react-is": "^18.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/rc-virtual-list": { + "version": "3.19.1", + "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.19.1.tgz", + "integrity": "sha512-DCapO2oyPqmooGhxBuXHM4lFuX+sshQwWqqkuyFA+4rShLe//+GEPVwiDgO+jKtKHtbeYwZoNvetwfHdOf+iUQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.0", + "classnames": "^2.2.6", + "rc-resize-observer": "^1.0.0", + "rc-util": "^5.36.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.30.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.1.tgz", + "integrity": "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz", + "integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0", + "react-router": "6.30.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", + "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.45.1", + "@rollup/rollup-android-arm64": "4.45.1", + "@rollup/rollup-darwin-arm64": "4.45.1", + "@rollup/rollup-darwin-x64": "4.45.1", + "@rollup/rollup-freebsd-arm64": "4.45.1", + "@rollup/rollup-freebsd-x64": "4.45.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", + "@rollup/rollup-linux-arm-musleabihf": "4.45.1", + "@rollup/rollup-linux-arm64-gnu": "4.45.1", + "@rollup/rollup-linux-arm64-musl": "4.45.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-musl": "4.45.1", + "@rollup/rollup-linux-s390x-gnu": "4.45.1", + "@rollup/rollup-linux-x64-gnu": "4.45.1", + "@rollup/rollup-linux-x64-musl": "4.45.1", + "@rollup/rollup-win32-arm64-msvc": "4.45.1", + "@rollup/rollup-win32-ia32-msvc": "4.45.1", + "@rollup/rollup-win32-x64-msvc": "4.45.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/runes2": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/runes2/-/runes2-1.1.4.tgz", + "integrity": "sha512-LNPnEDPOOU4ehF71m5JoQyzT2yxwD6ZreFJ7MxZUAoMKNMY1XrAo60H1CUoX5ncSm0rIuKlqn9JZNRrRkNou2g==", + "license": "MIT" + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sass": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.2.tgz", + "integrity": "sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/screenfull": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz", + "integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", + "license": "MIT", + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/size-sensor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/size-sensor/-/size-sensor-1.0.2.tgz", + "integrity": "sha512-2NCmWxY7A9pYKGXNBfteo4hy14gWu47rg5692peVMst6lQLPKrVjhY+UTEsPI5ceFRJSl3gVgMYaUi/hKuaiKw==", + "license": "ISC" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/staged-components": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/staged-components/-/staged-components-1.1.3.tgz", + "integrity": "sha512-9EIswzDqjwlEu+ymkV09TTlJfzSbKgEnNteUnZSTxkpMgr5Wx2CzzA9WcMFWBNCldqVPsHVnRGGrApduq2Se5A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==", + "license": "MIT" + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", + "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.4" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "license": "MIT", + "engines": { + "node": ">=12.22" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", + "license": "MIT" + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/vite": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz", + "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.6", + "picomatch": "^4.0.2", + "postcss": "^8.5.6", + "rollup": "^4.40.0", + "tinyglobby": "^0.2.14" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zrender": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.1.tgz", + "integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==", + "license": "BSD-3-Clause", + "dependencies": { + "tslib": "2.3.0" + } + }, + "node_modules/zustand": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.6.tgz", + "integrity": "sha512-ihAqNeUVhe0MAD+X8M5UzqyZ9k3FFZLBTtqo6JLPwV53cbRB/mJwBI0PxcIgqhBBHlEs8G45OTDTMq3gNcLq3A==", + "license": "MIT", + "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 + } + } + } + } +} diff --git a/nkebao/package.json b/nkebao/package.json index 8d148544e..88746967e 100644 --- a/nkebao/package.json +++ b/nkebao/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@ant-design/icons": "^5.6.1", "antd": "^5.13.1", "antd-mobile": "^5.39.1", "axios": "^1.6.7", diff --git a/nkebao/src/pages/home/index.tsx b/nkebao/src/pages/home/index.tsx index d7f2002ab..fa34d364d 100644 --- a/nkebao/src/pages/home/index.tsx +++ b/nkebao/src/pages/home/index.tsx @@ -3,9 +3,9 @@ import { NavBar } from "antd-mobile"; import { AppOutline, UserOutline, - LoopOutline, - TravelOutline, ClockCircleOutline, + SendOutline, + StarOutline, } from "antd-mobile-icons"; import MeauMobile from "@/components/MeauMobile/MeauMoible"; import Layout from "@/components/Layout/Layout"; @@ -38,19 +38,21 @@ const Home: React.FC = () => { { label: "同步朋友圈", value: res.momentsNum, - icon: , + icon: ( + + ), color: "#ff6b35", }, { label: "群发任务", value: res.groupPushNum, - icon: , + icon: , color: "#ffd700", }, { label: "获客转化率", value: res.passRate, - icon: , + icon: , color: "#4caf50", }, { diff --git a/nkebao/src/pages/mine/index.module.scss b/nkebao/src/pages/mine/index.module.scss index 53304f695..98aa403b9 100644 --- a/nkebao/src/pages/mine/index.module.scss +++ b/nkebao/src/pages/mine/index.module.scss @@ -1,71 +1,150 @@ -.home-page { - padding: 12px; - background: #f5f5f5; +.mine-page { + padding: 16px; + background-color: #f5f5f5; + min-height: 100vh; } -.home-cards { - display: flex; - gap: 12px; - margin-bottom: 12px; - > :global(.adm-card) { - flex: 1; +.user-card { + margin-bottom: 16px; + border-radius: 12px; + overflow: hidden; + + :global(.adm-card-body) { + padding: 20px; } } -.home-section { - margin-bottom: 12px; - background: #fff; - border-radius: 12px; - box-shadow: 0 2px 8px rgba(0,0,0,0.03); -} - -.home-section-title { - font-size: 16px; - font-weight: 600; - margin-bottom: 15px; - padding-left: 16px; -} - -.home-scene-stats { +.user-info { display: flex; - justify-content: space-between; - margin: 0 8px 8px 8px; + align-items: center; + gap: 16px; } -.home-scene-item { + +.user-avatar { + width: 60px; + height: 60px; + border-radius: 50%; + overflow: hidden; + border: 2px solid var(--primary-color); + + img { + width: 100%; + height: 100%; + object-fit: cover; + } +} + +.user-details { flex: 1; - text-align: center; -} -.home-scene-icon { - margin: 0 auto 4px auto; -} -.home-scene-value { - font-size: 18px; - font-weight: bold; - color: #1677ff; -} -.home-scene-label { - font-size: 12px; - color: #888; } -.home-today-item { - text-align: center; - padding: 8px 0; - background: #f7f8fa; +.user-name { + font-size: 18px; + font-weight: 600; + color: #333; + margin-bottom: 4px; +} + +.user-level { + font-size: 14px; + color: var(--primary-color); + margin-bottom: 4px; +} + +.user-points { + font-size: 12px; + color: #666; +} + +.menu-card { + margin-bottom: 16px; + border-radius: 12px; + overflow: hidden; + + :global(.adm-card-body) { + padding: 0; + } + + :global(.adm-list-item) { + padding: 16px; + border-bottom: 1px solid #f0f0f0; + + &:last-child { + border-bottom: none; + } + + :global(.adm-list-item-content) { + padding: 0; + } + + :global(.adm-list-item-content-prefix) { + margin-right: 12px; + color: var(--primary-color); + font-size: 20px; + } + + :global(.adm-list-item-content-main) { + flex: 1; + } + + :global(.adm-list-item-title) { + font-size: 16px; + color: #333; + margin-bottom: 4px; + } + + :global(.adm-list-item-description) { + font-size: 12px; + color: #666; + } + + :global(.adm-list-item-content-arrow) { + color: #ccc; + } + } +} + +.logout-section { + padding: 0 16px; +} + +.logout-btn { border-radius: 8px; -} -.home-today-value { - font-size: 18px; - font-weight: bold; - color: #1677ff; -} -.home-today-label { - font-size: 12px; - color: #888; + height: 48px; + font-size: 16px; + font-weight: 500; } -.home-chart { - margin-top: 8px; - width: 100%; - min-height: 100px; +// 响应式设计 +@media (max-width: 375px) { + .mine-page { + padding: 12px; + } + + .user-info { + gap: 12px; + } + + .user-avatar { + width: 50px; + height: 50px; + } + + .user-name { + font-size: 16px; + } + + .menu-card { + :global(.adm-list-item) { + padding: 12px; + + :global(.adm-list-item-content-prefix) { + font-size: 18px; + } + + :global(.adm-list-item-title) { + font-size: 14px; + } + } + } } \ No newline at end of file diff --git a/nkebao/src/pages/mine/index.tsx b/nkebao/src/pages/mine/index.tsx index dc673c00b..9cdc3a288 100644 --- a/nkebao/src/pages/mine/index.tsx +++ b/nkebao/src/pages/mine/index.tsx @@ -1,132 +1,143 @@ -import React, { useState } from "react"; -import { Card, NavBar, TabBar, Grid } from "antd-mobile"; +import React from "react"; +import { Card, NavBar, List, Button } from "antd-mobile"; import { - AppOutline, UserOutline, - PieOutline, // 替换 BarChartOutline - ShopbagOutline, + AppOutline, BellOutline, + HeartOutline, + StarOutline, + MessageOutline, + SendOutline, + MailOutline, } from "antd-mobile-icons"; import MeauMobile from "@/components/MeauMobile/MeauMoible"; import Layout from "@/components/Layout/Layout"; import style from "./index.module.scss"; -const sceneStats = [ - { - label: "公众号获客", - value: 234, - icon: , - }, - { - label: "海报获客", - value: 167, - icon: , - }, - { - label: "抖音获客", - value: 156, - icon: , - }, - { - label: "小红书获客", - value: 89, - icon: , - }, -]; -const todayStats = [ - { label: "朋友圈同步", value: 12 }, - { label: "群发任务", value: 8 }, - { label: "获客转化", value: "85%" }, - { label: "系统活跃度", value: "98%" }, -]; +const Mine: React.FC = () => { + const userInfo = { + name: "张三", + avatar: "https://via.placeholder.com/60", + level: "VIP会员", + points: 1280, + }; + + const menuItems = [ + { + icon: , + title: "个人资料", + subtitle: "修改个人信息", + path: "/profile", + }, + { + icon: , + title: "系统设置", + subtitle: "应用设置与偏好", + path: "/settings", + }, + { + icon: , + title: "消息通知", + subtitle: "通知设置", + path: "/notifications", + }, + { + icon: , + title: "我的收藏", + subtitle: "收藏的内容", + path: "/favorites", + }, + { + icon: , + title: "我的评价", + subtitle: "查看评价记录", + path: "/reviews", + }, + { + icon: , + title: "意见反馈", + subtitle: "问题反馈与建议", + path: "/feedback", + }, + { + icon: , + title: "联系客服", + subtitle: "在线客服", + path: "/customer-service", + }, + { + icon: , + title: "关于我们", + subtitle: "版本信息", + path: "/about", + }, + ]; -const Home: React.FC = () => { return ( - 存客宝 +
+ 我的 +
} footer={} > -
- {/* 统计卡片 */} -
- -
设备数量
-
0
-
- -
微信号数量
-
0
-
- -
在线微信号
-
0
-
-
- - {/* 场景获客统计 */} - -
场景获客统计
-
- {sceneStats.map((item) => ( -
-
{item.icon}
-
{item.value}
-
{item.label}
+
+ {/* 用户信息卡片 */} + +
+
+ 头像 +
+
+
{userInfo.name}
+
{userInfo.level}
+
+ 积分: {userInfo.points}
- ))} +
- {/* 今日数据 */} - -
今日数据
- - {todayStats.map((item) => ( - -
-
{item.value}
-
{item.label}
-
-
- ))} -
-
- - {/* 每日获客趋势(静态图表占位) */} - -
每日获客趋势
-
- - + + {menuItems.map((item, index) => ( + { + // 这里可以添加导航逻辑 + console.log(`点击了: ${item.title}`); + }} /> - {/* x轴文字 */} - {["周一", "周二", "周三", "周四", "周五", "周六", "周日"].map( - (d, i) => ( - - {d} - - ) - )} - -
+ ))} +
+ + {/* 退出登录按钮 */} +
+ +
); }; -export default Home; +export default Mine; diff --git a/nkebao/src/pages/scene/index.module.scss b/nkebao/src/pages/scene/index.module.scss index 972a62f35..e7c76ea58 100644 --- a/nkebao/src/pages/scene/index.module.scss +++ b/nkebao/src/pages/scene/index.module.scss @@ -31,9 +31,9 @@ // 页面容器 .scene-page { - padding: 12px; - background: #f5f5f5; - min-height: calc(100vh - 100px); + background: #f5f6fa; + min-height: 100vh; + padding: 0 0 60px 0; } // 错误提示 @@ -142,109 +142,89 @@ } } +.scenarios-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 16px; + padding: 16px; +} + .scenario-card { background: #fff; - border-radius: 12px; - padding: 16px 20px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); - border: 1px solid #f0f0f0; - transition: all 0.2s ease; - display: flex; - justify-content: space-between; - align-items: center; - min-height: 78px; - + border-radius: 16px; + box-shadow: 0 2px 8px rgba(0,0,0,0.06); + transition: box-shadow 0.2s, transform 0.2s; + cursor: pointer; + overflow: hidden; &:hover { - box-shadow: 0 4px 16px rgba(24, 142, 238, 0.12); - border-color: var(--primary-color-light); + box-shadow: 0 6px 16px rgba(0,0,0,0.12); + transform: translateY(-2px) scale(1.02); } } -.scenario-left { +.card-inner { display: flex; + flex-direction: column; align-items: center; - gap: 16px; - flex: 1; + padding: 18px 10px 14px 10px; } -.scenario-icon { - width: 52px; - height: 52px; - background: linear-gradient(135deg, #f0f8ff 0%, #e6f7ff 100%); - border-radius: 12px; +.card-img-wrap { + margin-bottom: 8px; +} +.card-img-bg { + width: 48px; + height: 48px; + background: #f0f2f5; + border-radius: 50%; display: flex; align-items: center; justify-content: center; - border: 1px solid rgba(24, 142, 238, 0.1); - flex-shrink: 0; } - -.scenario-image { +.card-img { width: 32px; height: 32px; object-fit: contain; - border-radius: 8px; } -.scenario-info { - display: flex; - flex-direction: column; - gap: 6px; - flex: 1; - min-width: 0; -} - -.scenario-name { +.card-title { font-size: 16px; font-weight: 600; - color: #1a1a1a; - line-height: 1.3; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + color: #1677ff; + text-align: center; + margin-bottom: 2px; } -.scenario-stats { +.card-desc { + font-size: 12px; + color: #888; + text-align: center; + margin-bottom: 6px; + line-height: 1.4; + min-height: 32px; + max-height: 32px; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; +} + +.card-stats { display: flex; align-items: center; + justify-content: center; + gap: 10px; + margin-top: 4px; } - -.stat-text { +.card-count { font-size: 13px; color: #666; - font-weight: 500; } - -.scenario-right { - display: flex; - flex-direction: column; - align-items: flex-end; - gap: 8px; - flex-shrink: 0; -} - -.scenario-status { - display: flex; - justify-content: flex-end; -} - -.status-tag { - font-size: 11px; - padding: 3px 8px; - border-radius: 10px; - font-weight: 500; -} - -.scenario-growth { +.card-growth { + font-size: 12px; + color: #52c41a; display: flex; align-items: center; - gap: 3px; - font-size: 16px; - font-weight: 700; -} - -.growth-icon { - font-size: 14px; } // 空状态 @@ -317,3 +297,35 @@ font-size: 13px; } } + +@media (max-width: 500px) { + .scenarios-grid { + gap: 10px; + padding: 10px; + } + .card-inner { + padding: 12px 4px 10px 4px; + } + .card-img-bg { + width: 40px; + height: 40px; + } + .card-img { + width: 26px; + height: 26px; + } + .card-title { + font-size: 15px; + } + .card-desc { + font-size: 11px; + min-height: 24px; + max-height: 24px; + } + .card-count { + font-size: 12px; + } + .card-growth { + font-size: 11px; + } +} diff --git a/nkebao/src/pages/scene/index.tsx b/nkebao/src/pages/scene/index.tsx index 4ca7359a1..a5d12b8dc 100644 --- a/nkebao/src/pages/scene/index.tsx +++ b/nkebao/src/pages/scene/index.tsx @@ -1,7 +1,8 @@ import React, { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; -import { NavBar, Button, SpinLoading, Toast, Tag } from "antd-mobile"; -import { AddOutline, UserAddOutline } from "antd-mobile-icons"; +import { NavBar, Button, Toast, SpinLoading } from "antd-mobile"; +import { AddOutline } from "antd-mobile-icons"; +import { RiseOutlined } from "@ant-design/icons"; import MeauMobile from "@/components/MeauMobile/MeauMoible"; import Layout from "@/components/Layout/Layout"; import { getScenarios } from "./api"; @@ -11,11 +12,23 @@ interface Scenario { id: string; name: string; image: string; + description?: string; count: number; growth: string; status: number; } +const scenarioDescriptions: Record = { + douyin: "通过抖音平台进行精准获客", + xiaohongshu: "利用小红书平台进行内容营销获客", + gongzhonghao: "通过微信公众号进行获客", + haibao: "通过海报分享进行获客", + phone: "通过电话营销进行获客", + weixinqun: "通过微信群进行获客", + payment: "通过付款码进行获客", + api: "通过API接口进行获客", +}; + const Scene: React.FC = () => { const navigate = useNavigate(); const [scenarios, setScenarios] = useState([]); @@ -26,25 +39,23 @@ const Scene: React.FC = () => { const fetchScenarios = async () => { setLoading(true); setError(""); - try { const response = await getScenarios({ page: 1, limit: 20 }); - - // 转换API数据为前端需要的格式 const transformedScenarios: Scenario[] = response.map((item: any) => ({ id: item.id.toString(), name: item.name, image: item.image || "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-api.png", - count: item.count, // 模拟今日数据 - growth: item.growth, // 模拟增长率 + description: + scenarioDescriptions[item.name?.toLowerCase()] || + "通过该平台进行获客", + count: item.count, + growth: item.growth, status: item.status, })); - setScenarios(transformedScenarios); } catch (error) { - console.error("获取场景数据失败:", error); setError("获取场景数据失败,请稍后重试"); Toast.show({ content: "获取场景数据失败,请稍后重试", @@ -54,7 +65,6 @@ const Scene: React.FC = () => { setLoading(false); } }; - fetchScenarios(); }, []); @@ -68,12 +78,61 @@ const Scene: React.FC = () => { navigate("/scenarios/new"); }; - const getGrowthColor = (growth: string) => { - const value = parseFloat(growth.replace(/[+%]/g, "")); - if (value > 10) return "#52c41a"; - if (value > 5) return "#faad14"; - return "#ff4d4f"; - }; + if (loading) { + return ( + +
场景获客
+ + + } + footer={} + > +
+ +
加载场景数据中...
+
+
+ ); + } + + if (error && scenarios.length === 0) { + return ( + +
场景获客
+ + + } + footer={} + > +
+
{error}
+ +
+
+ ); + } return ( { color="primary" onClick={handleNewPlan} className={style["new-plan-btn"]} + style={{ marginLeft: "auto" }} > - - 新建计划 + 新建计划 } - /> + > } footer={} >
-
-
- - 热门获客场景 -
-
- 选择适合的获客场景,提升转化效果 -
-
- -
+
{scenarios.map((scenario) => (
handleScenarioClick(scenario.id, scenario.name)} > -
-
-
+
+
+
{scenario.name} { e.currentTarget.src = "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-api.png"; }} />
-
-
- {scenario.name} -
-
- - 今日获客: {scenario.count} - -
-
- -
-
- - {scenario.status === 1 ? "活跃" : "暂停"} - +
{scenario.name}
+ {scenario.description && ( +
+ {scenario.description}
-
+ )} +
+ + 今日: {scenario.count} + + + {scenario.growth} -
+
))}
- - {scenarios.length === 0 && !loading && ( -
-
📊
-
暂无场景数据
- -
- )}
); diff --git a/nkebao/src/pages/workspace/Workspace.tsx b/nkebao/src/pages/workspace/Workspace.tsx index 81096f03d..f83779058 100644 --- a/nkebao/src/pages/workspace/Workspace.tsx +++ b/nkebao/src/pages/workspace/Workspace.tsx @@ -1,15 +1,14 @@ import React from "react"; import { Link } from "react-router-dom"; -import { Card, NavBar, Badge, Progress } from "antd-mobile"; +import { Card, NavBar, Badge } from "antd-mobile"; import { - ThumbsUpOutline, + LikeOutline, MessageOutline, SendOutline, TeamOutline, - ShareOutline, + LinkOutline, AppOutline, - BarChartOutline, - LineChartOutline, + PieOutline, ClockCircleOutline, } from "antd-mobile-icons"; import Layout from "@/components/Layout/Layout"; @@ -33,7 +32,7 @@ const Workspace: React.FC = () => { name: "自动点赞", description: "智能自动点赞互动", icon: ( - + ), path: "/workspace/auto-like", bgColor: "#fff2f0", @@ -77,7 +76,7 @@ const Workspace: React.FC = () => { name: "流量分发", description: "管理流量分发和分配", icon: ( - + ), path: "/workspace/traffic-distribution", bgColor: "#e6f7ff", @@ -101,9 +100,7 @@ const Workspace: React.FC = () => { id: "ai-analyzer", name: "AI数据分析", description: "智能分析客户行为特征", - icon: ( - - ), + icon: , path: "/workspace/ai-analyzer", bgColor: "#f0f0ff", isNew: true, @@ -121,17 +118,17 @@ const Workspace: React.FC = () => { id: "ai-forecast", name: "AI销售预测", description: "智能预测销售趋势", - icon: ( - - ), + icon: , path: "/workspace/ai-forecast", bgColor: "#fffbe6", }, ]; + // 进度条宽度 + const progressPercent = Math.round( + (taskStats.inProgress / taskStats.total) * 100 + ); + return ( {
总任务数
{taskStats.total}
- +
+
+
+
进行中: {taskStats.inProgress} / 已完成: {taskStats.completed}
From b1938a76c007ca07b72d23ab86f5a1e59667551a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=AC=94=E8=AE=B0=E6=9C=AC=E9=87=8C=E7=9A=84=E6=B0=B8?= =?UTF-8?q?=E5=B9=B3?= Date: Sat, 19 Jul 2025 16:10:26 +0800 Subject: [PATCH 24/36] =?UTF-8?q?feat:=20=E6=9C=AC=E6=AC=A1=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85=E5=AE=B9=E5=A6=82=E4=B8=8B?= =?UTF-8?q?=20=E6=A2=B3=E7=90=86=E4=BA=86icon=20=EF=BC=8C=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=BA=86layout=E7=9A=84loading?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nkebao/AI_ICON_GUIDE.md | 274 ++++++++ nkebao/ANTD_MOBILE_ICONS.md | 269 -------- nkebao/ICON_DETAILED_MAPPING.md | 205 ++++++ nkebao/ICON_MAPPING_GUIDE.md | 230 +++++++ nkebao/ICON_QUICK_REFERENCE.md | 271 ++++++++ nkebao/README.md | 44 -- nkebao/ROUTE_MIGRATION.md | 140 ---- nkebao/WORKSPACE_MIGRATION.md | 159 ----- nkebao/src/components/Layout/Layout.tsx | 20 +- .../src/components/Layout/layout.module.scss | 17 + .../src/components/MeauMobile/MeauMoible.tsx | 162 ++--- nkebao/src/components/PlaceholderPage.tsx | 112 ++-- .../pages/scenarios/ScenarioList.module.scss | 250 +++++++ nkebao/src/pages/scenarios/ScenarioList.tsx | 626 +++++++++++++++++- nkebao/src/pages/scenarios/api.ts | 51 ++ .../src/pages/scenarios/new/page.module.scss | 43 ++ nkebao/src/pages/scenarios/new/page.tsx | 301 ++++++++- .../new/steps/BasicSettings.module.scss | 63 ++ .../scenarios/new/steps/BasicSettings.tsx | 210 ++++++ .../steps/FriendRequestSettings.module.scss | 56 ++ .../new/steps/FriendRequestSettings.tsx | 113 ++++ .../new/steps/MessageSettings.module.scss | 64 ++ .../scenarios/new/steps/MessageSettings.tsx | 140 ++++ nkebao/src/pages/scene/index.tsx | 11 +- .../pages/wechat-accounts/WechatAccounts.tsx | 74 +-- nkebao/src/pages/workspace/Workspace.tsx | 51 +- .../pages/workspace/auto-like/AutoLike.tsx | 76 +-- nkebao/src/pages/workspace/main/index.tsx | 20 +- nkebao/src/router/module/scenarios.tsx | 5 +- nkebao/src/router/permissionRoute.tsx | 4 +- nkebao/迁移提示词.md | 28 - 31 files changed, 3181 insertions(+), 908 deletions(-) create mode 100644 nkebao/AI_ICON_GUIDE.md delete mode 100644 nkebao/ANTD_MOBILE_ICONS.md create mode 100644 nkebao/ICON_DETAILED_MAPPING.md create mode 100644 nkebao/ICON_MAPPING_GUIDE.md create mode 100644 nkebao/ICON_QUICK_REFERENCE.md delete mode 100644 nkebao/README.md delete mode 100644 nkebao/ROUTE_MIGRATION.md delete mode 100644 nkebao/WORKSPACE_MIGRATION.md create mode 100644 nkebao/src/pages/scenarios/ScenarioList.module.scss create mode 100644 nkebao/src/pages/scenarios/api.ts create mode 100644 nkebao/src/pages/scenarios/new/page.module.scss create mode 100644 nkebao/src/pages/scenarios/new/steps/BasicSettings.module.scss create mode 100644 nkebao/src/pages/scenarios/new/steps/BasicSettings.tsx create mode 100644 nkebao/src/pages/scenarios/new/steps/FriendRequestSettings.module.scss create mode 100644 nkebao/src/pages/scenarios/new/steps/FriendRequestSettings.tsx create mode 100644 nkebao/src/pages/scenarios/new/steps/MessageSettings.module.scss create mode 100644 nkebao/src/pages/scenarios/new/steps/MessageSettings.tsx delete mode 100644 nkebao/迁移提示词.md diff --git a/nkebao/AI_ICON_GUIDE.md b/nkebao/AI_ICON_GUIDE.md new file mode 100644 index 000000000..cef30a6a7 --- /dev/null +++ b/nkebao/AI_ICON_GUIDE.md @@ -0,0 +1,274 @@ +# AI图标使用指南 + +## 概述 + +本文档为AI助手提供标准化的图标选择和使用流程,确保在代码生成时使用正确的图标包和图标名称。 + +## AI使用流程 + +### 1. 项目类型判断 +首先判断项目类型: +- **PC端项目**: 使用 `@ant-design/icons` +- **移动端项目**: 使用 `antd-mobile-icons` + +### 2. 图标查找流程 +1. 根据功能需求确定图标类型 +2. 在对应图标包中查找合适的图标 +3. 如果图标不存在,查找替代方案 +4. 使用正确的导入语法 + +### 3. 代码生成模板 + +#### PC端项目模板 +```typescript +import { + // 导航类 + HomeOutlined, + UserOutlined, + SettingOutlined, + + // 操作类 + PlusOutlined, + EditOutlined, + DeleteOutlined, + CopyOutlined, + SearchOutlined, + ReloadOutlined, + + // 状态类 + CheckOutlined, + CloseOutlined, + CheckCircleOutlined, + CloseCircleOutlined, + ExclamationCircleOutlined, + InfoCircleOutlined, + LoadingOutlined, + + // 方向类 + UpOutlined, + DownOutlined, + LeftOutlined, + RightOutlined, + ArrowLeftOutlined, + + // 其他 + MessageOutlined, + CalendarOutlined, + ClockCircleOutlined, + PictureOutlined, + FileOutlined, + CameraOutlined, + QrcodeOutlined, +} from '@ant-design/icons'; +``` + +#### 移动端项目模板 +```typescript +import { + // 导航类 + HomeOutline, + UserOutline, + SettingOutline, + + // 操作类 + AddOutline, + EditSOutline, + DeleteOutline, + CopyOutline, + SearchOutline, + RefreshOutline, + + // 状态类 + CheckOutline, + CloseOutline, + CheckCircleOutline, + CloseCircleOutline, + ExclamationCircleOutline, + InfoCircleOutline, + LoadingOutline, + + // 方向类 + UpOutline, + DownOutline, + LeftOutline, + RightOutline, + + // 其他 + MessageOutline, + CalendarOutline, + ClockCircleOutline, + PictureOutline, + FileOutline, + CameraOutline, + QrCodeOutline, +} from 'antd-mobile-icons'; +``` + +## 功能到图标映射 + +### 基础功能映射 +| 功能需求 | PC端图标 | 移动端图标 | 说明 | +|---------|---------|-----------|------| +| 添加/新建 | PlusOutlined | AddOutline | 通用添加功能 | +| 编辑/修改 | EditOutlined | EditSOutline | 编辑功能 | +| 删除/移除 | DeleteOutlined | DeleteOutline | 删除功能 | +| 复制/克隆 | CopyOutlined | CopyOutline | 复制功能 | +| 搜索/查找 | SearchOutlined | SearchOutline | 搜索功能 | +| 刷新/重新加载 | ReloadOutlined | RefreshOutline | 刷新功能 | +| 设置/配置 | SettingOutlined | SettingOutline | 设置功能 | +| 用户/个人 | UserOutlined | UserOutline | 用户相关 | +| 首页/主页 | HomeOutlined | HomeOutline | 首页导航 | +| 返回/后退 | ArrowLeftOutlined | LeftOutline | 返回功能 | +| 关闭/取消 | CloseOutlined | CloseOutline | 关闭功能 | +| 确认/确定 | CheckOutlined | CheckOutline | 确认功能 | + +### 状态指示映射 +| 状态需求 | PC端图标 | 移动端图标 | 说明 | +|---------|---------|-----------|------| +| 成功/完成 | CheckCircleOutlined | CheckCircleOutline | 成功状态 | +| 错误/失败 | CloseCircleOutlined | CloseCircleOutline | 错误状态 | +| 警告/注意 | ExclamationCircleOutlined | ExclamationCircleOutline | 警告状态 | +| 信息/提示 | InfoCircleOutlined | InfoCircleOutline | 信息提示 | +| 加载/等待 | LoadingOutlined | LoadingOutline | 加载状态 | +| 时间/等待 | ClockCircleOutlined | ClockCircleOutline | 时间相关 | + +### 方向导航映射 +| 方向需求 | PC端图标 | 移动端图标 | 说明 | +|---------|---------|-----------|------| +| 向上/上升 | UpOutlined | UpOutline | 向上方向 | +| 向下/下降 | DownOutlined | DownOutline | 向下方向 | +| 向左/后退 | LeftOutlined | LeftOutline | 向左方向 | +| 向右/前进 | RightOutlined | RightOutline | 向右方向 | + +### 业务功能映射 +| 业务需求 | PC端图标 | 移动端图标 | 说明 | +|---------|---------|-----------|------| +| 消息/通知 | MessageOutlined | MessageOutline | 消息功能 | +| 日历/日期 | CalendarOutlined | CalendarOutline | 日历功能 | +| 图片/照片 | PictureOutlined | PictureOutline | 图片功能 | +| 文件/文档 | FileOutlined | FileOutline | 文件功能 | +| 相机/拍照 | CameraOutlined | CameraOutline | 相机功能 | +| 二维码 | QrcodeOutlined | QrCodeOutline | 二维码功能 | +| 微信/社交 | WechatOutlined | WechatOutline | 微信功能 | +| 设备/手机 | MobileOutlined | MobileOutline | 设备功能 | +| 团队/群组 | TeamOutlined | TeamOutline | 团队功能 | +| 订单/购物 | ShoppingOutlined | ShoppingOutline | 订单功能 | +| 支付/钱包 | PayCircleOutlined | PayCircleOutline | 支付功能 | + +## 特殊替换规则 + +### 移动端不存在的图标替换 +| 原需求 | 替换方案 | 说明 | +|--------|----------|------| +| RiseOutlined | UpOutline | 上升趋势 | +| ThumbsUpOutlined | LikeOutline | 点赞功能 | +| ShareAltOutlined | LinkOutline | 分享功能 | +| BarChartOutlined | PieOutline | 图表功能 | +| LineChartOutlined | PieOutline | 图表功能 | +| UserAddOutlined | UserOutline | 用户添加 | +| Progress | 自定义div | 进度条组件 | + +### PC端不存在的图标替换 +| 原需求 | 替换方案 | 说明 | +|--------|----------|------| +| AntOutline | HomeOutlined | 蚂蚁图标 | +| AppOutline | AppstoreOutlined | 应用图标 | + +## AI代码生成示例 + +### 场景1: 移动端列表页面 +```typescript +// AI应该生成的代码 +import { + AddOutline, // 添加按钮 + EditSOutline, // 编辑按钮 + DeleteOutline, // 删除按钮 + CopyOutline, // 复制按钮 + SearchOutline, // 搜索框 + RefreshOutline, // 刷新按钮 + UserOutline, // 用户信息 + CalendarOutline, // 时间信息 + UpOutline, // 上升趋势(替换RiseOutlined) +} from 'antd-mobile-icons'; + +// 使用示例 + + + +``` + +### 场景2: PC端管理页面 +```typescript +// AI应该生成的代码 +import { + PlusOutlined, // 添加按钮 + EditOutlined, // 编辑按钮 + DeleteOutlined, // 删除按钮 + CopyOutlined, // 复制按钮 + SearchOutlined, // 搜索框 + ReloadOutlined, // 刷新按钮 + UserOutlined, // 用户信息 + CalendarOutlined, // 时间信息 + RiseOutlined, // 上升趋势(PC端存在) +} from '@ant-design/icons'; + +// 使用示例 + + + +``` + +## 错误检测和修正 + +### 常见错误模式 +1. **混用图标包**: 同时导入PC端和移动端图标 +2. **使用不存在的图标**: 在移动端使用PC端特有的图标 +3. **命名错误**: 图标名称大小写错误 +4. **导入路径错误**: 使用错误的包名 + +### 修正策略 +1. **统一图标包**: 根据项目类型选择单一图标包 +2. **查找替代**: 使用对照表查找替代图标 +3. **验证存在**: 确保图标在目标包中存在 +4. **测试验证**: 在代码中测试图标是否正常显示 + +## AI使用检查清单 + +### 代码生成前 +- [ ] 确认项目类型(PC端/移动端) +- [ ] 选择对应的图标包 +- [ ] 根据功能需求选择合适图标 +- [ ] 检查图标是否存在 + +### 代码生成中 +- [ ] 使用正确的导入语法 +- [ ] 图标名称大小写正确 +- [ ] 避免混用不同包的图标 +- [ ] 为不存在的图标提供替代方案 + +### 代码生成后 +- [ ] 验证图标导入正确 +- [ ] 检查图标使用语法 +- [ ] 确保样式设置合理 +- [ ] 提供使用示例 + +## 更新和维护 + +- 定期更新图标对照表 +- 记录新发现的图标差异 +- 更新替换规则 +- 优化AI使用流程 + +## 注意事项 + +1. **优先使用语义化图标**: 选择最能表达功能的图标 +2. **保持一致性**: 在同一项目中保持图标风格一致 +3. **考虑可访问性**: 为图标添加适当的aria-label +4. **性能优化**: 按需导入图标,避免全量导入 +5. **版本兼容**: 注意图标包版本与UI框架版本的兼容性 \ No newline at end of file diff --git a/nkebao/ANTD_MOBILE_ICONS.md b/nkebao/ANTD_MOBILE_ICONS.md deleted file mode 100644 index 4c03bba63..000000000 --- a/nkebao/ANTD_MOBILE_ICONS.md +++ /dev/null @@ -1,269 +0,0 @@ -# Ant Design Mobile 图标完整列表 - -## 📋 图标分类 - -### 🔗 基础图标 (Outline) -- `AddOutline` - 添加 -- `AppOutline` - 应用 -- `ArrowDownOutline` - 向下箭头 -- `ArrowLeftOutline` - 向左箭头 -- `ArrowRightOutline` - 向右箭头 -- `ArrowUpOutline` - 向上箭头 -- `AudioOutline` - 音频 -- `BarChartOutline` - 柱状图 -- `BellOutline` - 铃铛 -- `BookOutline` - 书本 -- `CalendarOutline` - 日历 -- `CameraOutline` - 相机 -- `CheckOutline` - 勾选 -- `ClockCircleOutline` - 时钟 -- `CloseOutline` - 关闭 -- `DeleteOutline` - 删除 -- `DownlandOutline` - 下载 -- `EditSOutline` - 编辑 -- `EllipsisOutline` - 省略号 -- `EyeOutline` - 眼睛 -- `FileOutline` - 文件 -- `FilterOutline` - 筛选 -- `FireOutline` - 火焰 -- `FlagOutline` - 旗帜 -- `FolderOutline` - 文件夹 -- `GiftOutline` - 礼物 -- `HeartOutline` - 心形 -- `HomeOutline` - 首页 -- `ImageOutline` - 图片 -- `LeftOutline` - 左箭头 -- `LikeOutline` - 点赞 -- `LinkOutline` - 链接 -- `LocationOutline` - 位置 -- `LockOutline` - 锁定 -- `MailOutline` - 邮件 -- `MessageOutline` - 消息 -- `MoreOutline` - 更多 -- `PayCircleOutline` - 支付 -- `PhoneOutline` - 电话 -- `PictureOutline` - 图片 -- `PlayOutline` - 播放 -- `PlusOutline` - 加号 -- `RightOutline` - 右箭头 -- `ScanOutline` - 扫描 -- `SearchOutline` - 搜索 -- `SendOutline` - 发送 -- `SettingOutline` - 设置 -- `ShareOutline` - 分享 -- `ShopbagOutline` - 购物袋 -- `StarOutline` - 星星 -- `TeamOutline` - 团队 -- `ThumbsUpOutline` - 点赞 -- `TimeOutline` - 时间 -- `UnlockOutline` - 解锁 -- `UserOutline` - 用户 -- `VideoOutline` - 视频 -- `VoiceOutline` - 语音 - -### 🎨 填充图标 (Fill) -- `AddCircleFill` - 添加圆形 -- `AppFill` - 应用填充 -- `AudioFill` - 音频填充 -- `BellFill` - 铃铛填充 -- `BookFill` - 书本填充 -- `CalendarFill` - 日历填充 -- `CameraFill` - 相机填充 -- `CheckCircleFill` - 勾选圆形 -- `ClockCircleFill` - 时钟圆形 -- `CloseCircleFill` - 关闭圆形 -- `DeleteFill` - 删除填充 -- `DownlandFill` - 下载填充 -- `EditSFill` - 编辑填充 -- `EyeFill` - 眼睛填充 -- `FileFill` - 文件填充 -- `FilterFill` - 筛选填充 -- `FireFill` - 火焰填充 -- `FlagFill` - 旗帜填充 -- `FolderFill` - 文件夹填充 -- `GiftFill` - 礼物填充 -- `HeartFill` - 心形填充 -- `HomeFill` - 首页填充 -- `ImageFill` - 图片填充 -- `LeftFill` - 左箭头填充 -- `LikeFill` - 点赞填充 -- `LinkFill` - 链接填充 -- `LocationFill` - 位置填充 -- `LockFill` - 锁定填充 -- `MailFill` - 邮件填充 -- `MessageFill` - 消息填充 -- `MoreFill` - 更多填充 -- `PayCircleFill` - 支付圆形 -- `PhoneFill` - 电话填充 -- `PictureFill` - 图片填充 -- `PlayFill` - 播放填充 -- `PlusCircleFill` - 加号圆形 -- `RightFill` - 右箭头填充 -- `ScanFill` - 扫描填充 -- `SearchFill` - 搜索填充 -- `SendFill` - 发送填充 -- `SettingFill` - 设置填充 -- `ShareFill` - 分享填充 -- `ShopbagFill` - 购物袋填充 -- `StarFill` - 星星填充 -- `TeamFill` - 团队填充 -- `ThumbsUpFill` - 点赞填充 -- `TimeFill` - 时间填充 -- `UnlockFill` - 解锁填充 -- `UserFill` - 用户填充 -- `VideoFill` - 视频填充 -- `VoiceFill` - 语音填充 - -### 📊 图表图标 -- `BarChartOutline` - 柱状图 -- `LineChartOutline` - 折线图 -- `PieOutline` - 饼图 -- `AreaOutline` - 面积图 -- `RadarOutline` - 雷达图 -- `ScatterOutline` - 散点图 - -### 🔧 功能图标 -- `AddOutline` - 添加 -- `DeleteOutline` - 删除 -- `EditSOutline` - 编辑 -- `SearchOutline` - 搜索 -- `FilterOutline` - 筛选 -- `SettingOutline` - 设置 -- `MoreOutline` - 更多 -- `CloseOutline` - 关闭 -- `CheckOutline` - 勾选 - -### 📱 通信图标 -- `MessageOutline` - 消息 -- `PhoneOutline` - 电话 -- `MailOutline` - 邮件 -- `SendOutline` - 发送 -- `VoiceOutline` - 语音 -- `VideoOutline` - 视频 - -### 👥 用户图标 -- `UserOutline` - 用户 -- `TeamOutline` - 团队 -- `HeartOutline` - 心形 -- `LikeOutline` - 点赞 -- `ThumbsUpOutline` - 点赞 - -### 🛍️ 商业图标 -- `ShopbagOutline` - 购物袋 -- `PayCircleOutline` - 支付 -- `GiftOutline` - 礼物 -- `StarOutline` - 星星 - -### 📁 文件图标 -- `FileOutline` - 文件 -- `FolderOutline` - 文件夹 -- `ImageOutline` - 图片 -- `PictureOutline` - 图片 -- `CameraOutline` - 相机 - -### 🎵 媒体图标 -- `AudioOutline` - 音频 -- `VideoOutline` - 视频 -- `PlayOutline` - 播放 -- `VoiceOutline` - 语音 - -### 📅 时间图标 -- `CalendarOutline` - 日历 -- `ClockCircleOutline` - 时钟 -- `TimeOutline` - 时间 - -### 🔒 安全图标 -- `LockOutline` - 锁定 -- `UnlockOutline` - 解锁 -- `EyeOutline` - 眼睛 - -### 📍 位置图标 -- `LocationOutline` - 位置 -- `FlagOutline` - 旗帜 - -### 🔗 导航图标 -- `ArrowUpOutline` - 向上箭头 -- `ArrowDownOutline` - 向下箭头 -- `ArrowLeftOutline` - 向左箭头 -- `ArrowRightOutline` - 向右箭头 -- `LeftOutline` - 左箭头 -- `RightOutline` - 右箭头 - -## 🎨 使用示例 - -### 基础用法 -```tsx -import { AddOutline, UserOutline, SettingOutline } from 'antd-mobile-icons'; - -// 在组件中使用 - - - -``` - -### 自定义样式 -```tsx -import { HeartOutline } from 'antd-mobile-icons'; - -// 自定义颜色和大小 - -``` - -### 在项目中的使用 -```tsx -// 工作台页面示例 -const features = [ - { - icon: , - name: "自动点赞" - }, - { - icon: , - name: "AI对话助手" - }, - { - icon: , - name: "AI数据分析" - } -]; -``` - -## 📝 注意事项 - -1. **图标命名规则**: 所有图标都以 `Outline` 或 `Fill` 结尾 -2. **导入方式**: 从 `antd-mobile-icons` 包中导入 -3. **样式定制**: 可以通过 `style` 属性自定义颜色、大小等 -4. **响应式**: 图标支持响应式设计,可以配合 CSS 媒体查询使用 -5. **可访问性**: 图标支持 `aria-label` 等无障碍属性 - -## 🔗 相关资源 - -- [Ant Design Mobile 官方文档](https://mobile.ant.design/) -- [图标组件文档](https://mobile.ant.design/zh/components/icon) -- [图标库 GitHub](https://github.com/ant-design/ant-design-mobile) - -## 📊 项目中已使用的图标 - -### 工作台页面 -- `ThumbsUpOutline` - 自动点赞 -- `MessageOutline` - AI对话助手 -- `SendOutline` - 群消息推送 -- `TeamOutline` - 自动建群 -- `ShareOutline` - 流量分发 -- `AppOutline` - AI策略优化 -- `BarChartOutline` - AI数据分析 -- `LineChartOutline` - AI销售预测 -- `ClockCircleOutline` - 朋友圈同步 - -### 底部导航 -- `AppOutline` - 首页 -- `ShopbagOutline` - 场景获客 -- `PieOutline` - 工作台 -- `UserOutline` - 我的 - -### 其他页面 -- `AddOutline` - 添加按钮 -- `UserAddOutline` - 用户添加 -- `BellOutline` - 通知 -- `LoopOutline` - 循环/同步 -- `TravelOutline` - 旅行/任务 \ No newline at end of file diff --git a/nkebao/ICON_DETAILED_MAPPING.md b/nkebao/ICON_DETAILED_MAPPING.md new file mode 100644 index 000000000..5e849544d --- /dev/null +++ b/nkebao/ICON_DETAILED_MAPPING.md @@ -0,0 +1,205 @@ +# 详细图标对照表 + +## 基础图标对照 + +### 导航类 +| 功能 | PC端 | 移动端 | 状态 | +|------|------|--------|------| +| 首页 | HomeOutlined | HomeOutline | ✅ | +| 返回 | ArrowLeftOutlined | LeftOutline | ✅ | +| 菜单 | MenuOutlined | MenuOutline | ✅ | +| 设置 | SettingOutlined | SettingOutline | ✅ | +| 用户 | UserOutlined | UserOutline | ✅ | +| 个人中心 | UserOutlined | UserOutline | ✅ | + +### 操作类 +| 功能 | PC端 | 移动端 | 状态 | +|------|------|--------|------| +| 添加 | PlusOutlined | AddOutline | ✅ | +| 编辑 | EditOutlined | EditSOutline | ✅ | +| 删除 | DeleteOutlined | DeleteOutline | ✅ | +| 复制 | CopyOutlined | CopyOutline | ✅ | +| 保存 | SaveOutlined | SaveOutline | ✅ | +| 刷新 | ReloadOutlined | RefreshOutline | ✅ | +| 搜索 | SearchOutlined | SearchOutline | ✅ | +| 关闭 | CloseOutlined | CloseOutline | ✅ | +| 确认 | CheckOutlined | CheckOutline | ✅ | +| 取消 | CloseOutlined | CloseOutline | ✅ | + +### 状态类 +| 功能 | PC端 | 移动端 | 状态 | +|------|------|--------|------| +| 成功 | CheckCircleOutlined | CheckCircleOutline | ✅ | +| 错误 | CloseCircleOutlined | CloseCircleOutline | ✅ | +| 警告 | ExclamationCircleOutlined | ExclamationCircleOutline | ✅ | +| 信息 | InfoCircleOutlined | InfoCircleOutline | ✅ | +| 加载 | LoadingOutlined | LoadingOutline | ✅ | +| 等待 | ClockCircleOutlined | ClockCircleOutline | ✅ | + +### 方向类 +| 功能 | PC端 | 移动端 | 状态 | +|------|------|--------|------| +| 向上 | UpOutlined | UpOutline | ✅ | +| 向下 | DownOutlined | DownOutline | ✅ | +| 向左 | LeftOutlined | LeftOutline | ✅ | +| 向右 | RightOutlined | RightOutline | ✅ | +| 向上圆形 | UpCircleOutlined | UpCircleOutline | ✅ | +| 向下圆形 | DownCircleOutlined | DownCircleOutline | ✅ | + +### 通信类 +| 功能 | PC端 | 移动端 | 状态 | +|------|------|--------|------| +| 消息 | MessageOutlined | MessageOutline | ✅ | +| 邮件 | MailOutlined | MailOutline | ✅ | +| 电话 | PhoneOutlined | PhoneOutline | ✅ | +| 视频 | VideoCameraOutlined | VideoCameraOutline | ✅ | +| 语音 | AudioOutlined | AudioOutline | ✅ | + +### 媒体类 +| 功能 | PC端 | 移动端 | 状态 | +|------|------|--------|------| +| 图片 | PictureOutlined | PictureOutline | ✅ | +| 文件 | FileOutlined | FileOutline | ✅ | +| 文件夹 | FolderOutlined | FolderOutline | ✅ | +| 相机 | CameraOutlined | CameraOutline | ✅ | +| 二维码 | QrcodeOutlined | QrCodeOutline | ✅ | + +### 时间类 +| 功能 | PC端 | 移动端 | 状态 | +|------|------|--------|------| +| 日历 | CalendarOutlined | CalendarOutline | ✅ | +| 时钟 | ClockCircleOutlined | ClockCircleOutline | ✅ | +| 历史 | HistoryOutlined | HistoryOutline | ✅ | + +### 数据类 +| 功能 | PC端 | 移动端 | 状态 | +|------|------|--------|------| +| 统计 | BarChartOutlined | BarChartOutline | ✅ | +| 饼图 | PieChartOutlined | PieChartOutline | ✅ | +| 折线图 | LineChartOutlined | LineChartOutline | ✅ | +| 表格 | TableOutlined | TableOutline | ✅ | +| 列表 | UnorderedListOutlined | UnorderedListOutline | ✅ | + +## 特殊图标对照 + +### 业务相关 +| 功能 | PC端 | 移动端 | 状态 | +|------|------|--------|------| +| 设备 | MobileOutlined | MobileOutline | ✅ | +| 微信 | WechatOutlined | WechatOutline | ✅ | +| 群组 | TeamOutlined | TeamOutline | ✅ | +| 客户 | UserAddOutlined | UserAddOutline | ❌ | +| 订单 | ShoppingOutlined | ShoppingOutline | ✅ | +| 支付 | PayCircleOutlined | PayCircleOutline | ✅ | + +### 工具类 +| 功能 | PC端 | 移动端 | 状态 | +|------|------|--------|------| +| 工具 | ToolOutlined | ToolOutline | ✅ | +| 配置 | SettingOutlined | SettingOutline | ✅ | +| 帮助 | QuestionCircleOutlined | QuestionCircleOutline | ✅ | +| 反馈 | MessageOutlined | MessageOutline | ✅ | +| 分享 | ShareAltOutlined | ShareOutline | ❌ | + +## 不存在的图标替换方案 + +### PC端存在但移动端不存在的图标 +| PC端图标 | 推荐替换 | 说明 | +|----------|----------|------| +| UserAddOutlined | UserOutline | 用户添加功能 | +| ShareAltOutlined | LinkOutline | 分享功能 | +| RiseOutlined | UpOutline | 上升趋势 | +| ThumbsUpOutlined | LikeOutline | 点赞功能 | +| BarChartOutlined | PieOutline | 图表功能 | +| LineChartOutlined | PieOutline | 图表功能 | + +### 移动端存在但PC端不存在的图标 +| 移动端图标 | 推荐替换 | 说明 | +|------------|----------|------| +| AntOutline | HomeOutlined | 蚂蚁图标 | +| AppOutline | AppstoreOutlined | 应用图标 | + +## 使用规范 + +### 1. 导入规范 +```typescript +// PC端项目 +import { + HomeOutlined, + UserOutlined, + SettingOutlined, +} from '@ant-design/icons'; + +// 移动端项目 +import { + HomeOutline, + UserOutline, + SettingOutline, +} from 'antd-mobile-icons'; +``` + +### 2. 命名规范 +- PC端:使用 `Outlined` 后缀 +- 移动端:使用 `Outline` 后缀 +- 保持语义化命名 + +### 3. 使用建议 +- 优先使用语义明确的图标 +- 保持图标风格一致性 +- 考虑图标在不同尺寸下的清晰度 +- 为图标添加适当的aria-label + +### 4. 错误处理 +当图标不存在时: +1. 查找语义相近的图标 +2. 使用通用图标(如QuestionOutlined) +3. 考虑使用文字替代 +4. 创建自定义图标组件 + +## 项目中的实际应用 + +### 场景获客模块使用的图标 +```typescript +// 移动端项目中的图标使用 +import { + AddOutline, // 添加 + UpOutline, // 上升趋势(替换RiseOutline) + UserOutline, // 用户 + CalendarOutline, // 日历 + CopyOutline, // 复制 + DeleteOutline, // 删除 + EditSOutline, // 编辑 + SettingOutline, // 设置 + SearchOutline, // 搜索 + RefreshOutline, // 刷新 + QrCodeOutline, // 二维码 +} from 'antd-mobile-icons'; +``` + +### 工作台模块使用的图标 +```typescript +// 移动端项目中的图标使用 +import { + LikeOutline, // 点赞(替换ThumbsUpOutline) + LinkOutline, // 链接(替换ShareOutline) + PieOutline, // 饼图(替换BarChartOutline/LineChartOutline) + UserOutline, // 用户 + TeamOutline, // 团队 + MessageOutline, // 消息 +} from 'antd-mobile-icons'; +``` + +## 更新和维护 + +1. **定期检查**: 定期检查新版本中新增的图标 +2. **文档更新**: 及时更新图标对照表 +3. **团队协作**: 团队成员共享图标使用规范 +4. **代码审查**: 在代码审查中检查图标使用是否正确 + +## 注意事项 + +1. **包版本**: 确保图标包版本与UI框架版本兼容 +2. **按需导入**: 避免全量导入图标,影响打包体积 +3. **样式覆盖**: 可以通过CSS自定义图标样式 +4. **无障碍**: 为图标添加适当的无障碍属性 +5. **性能**: 大量使用图标时注意性能优化 \ No newline at end of file diff --git a/nkebao/ICON_MAPPING_GUIDE.md b/nkebao/ICON_MAPPING_GUIDE.md new file mode 100644 index 000000000..81d901d07 --- /dev/null +++ b/nkebao/ICON_MAPPING_GUIDE.md @@ -0,0 +1,230 @@ +# PC端与移动端图标对照文档 + +## 概述 + +本文档记录了PC端(@ant-design/icons)和移动端(antd-mobile-icons)的图标名称对照,以及正确的导入方式。 + +## 导入方式 + +### PC端图标 (@ant-design/icons) +```typescript +import { + HomeOutlined, + UserOutlined, + SettingOutlined, + // ... 其他图标 +} from '@ant-design/icons'; +``` + +### 移动端图标 (antd-mobile-icons) +```typescript +import { + AntOutline, + ArrowDownCircleOutline, + UserOutline, + // ... 其他图标 +} from 'antd-mobile-icons'; +``` + +## 图标对照表 + +### 常用图标对照 + +| 功能描述 | PC端图标 | 移动端图标 | 备注 | +|---------|---------|-----------|------| +| 首页 | HomeOutlined | HomeOutline | 完全对应 | +| 用户 | UserOutlined | UserOutline | 完全对应 | +| 设置 | SettingOutlined | SettingOutline | 完全对应 | +| 搜索 | SearchOutlined | SearchOutline | 完全对应 | +| 添加 | PlusOutlined | AddOutline | 完全对应 | +| 编辑 | EditOutlined | EditSOutline | 移动端略有不同 | +| 删除 | DeleteOutlined | DeleteOutline | 完全对应 | +| 复制 | CopyOutlined | CopyOutline | 完全对应 | +| 刷新 | ReloadOutlined | RefreshOutline | 完全对应 | +| 二维码 | QrcodeOutlined | QrCodeOutline | 完全对应 | +| 日历 | CalendarOutlined | CalendarOutline | 完全对应 | +| 时钟 | ClockCircleOutlined | ClockCircleOutline | 完全对应 | +| 箭头向上 | UpOutlined | UpOutline | 完全对应 | +| 箭头向下 | DownOutlined | DownOutline | 完全对应 | +| 箭头向左 | LeftOutlined | LeftOutline | 完全对应 | +| 箭头向右 | RightOutlined | RightOutline | 完全对应 | +| 返回 | ArrowLeftOutlined | LeftOutline | 移动端使用LeftOutline | +| 关闭 | CloseOutlined | CloseOutline | 完全对应 | +| 检查 | CheckOutlined | CheckOutline | 完全对应 | +| 警告 | ExclamationCircleOutlined | ExclamationCircleOutline | 完全对应 | +| 信息 | InfoCircleOutlined | InfoCircleOutline | 完全对应 | +| 成功 | CheckCircleOutlined | CheckCircleOutline | 完全对应 | +| 错误 | CloseCircleOutlined | CloseCircleOutline | 完全对应 | + +### 方向性图标 + +| 功能描述 | PC端图标 | 移动端图标 | +|---------|---------|-----------| +| 向上 | UpOutlined | UpOutline | +| 向下 | DownOutlined | DownOutline | +| 向左 | LeftOutlined | LeftOutline | +| 向右 | RightOutlined | RightOutline | +| 向上圆形 | UpCircleOutlined | UpCircleOutline | +| 向下圆形 | DownCircleOutlined | DownCircleOutline | +| 向左圆形 | LeftCircleOutlined | LeftCircleOutline | +| 向右圆形 | RightCircleOutlined | RightCircleOutline | + +### 编辑类图标 + +| 功能描述 | PC端图标 | 移动端图标 | +|---------|---------|-----------| +| 编辑 | EditOutlined | EditSOutline | +| 删除 | DeleteOutlined | DeleteOutline | +| 复制 | CopyOutlined | CopyOutline | +| 剪切 | ScissorOutlined | ScissorOutline | +| 撤销 | UndoOutlined | UndoOutline | +| 重做 | RedoOutlined | RedoOutline | +| 保存 | SaveOutlined | SaveOutline | + +### 通信类图标 + +| 功能描述 | PC端图标 | 移动端图标 | +|---------|---------|-----------| +| 消息 | MessageOutlined | MessageOutline | +| 邮件 | MailOutlined | MailOutline | +| 电话 | PhoneOutlined | PhoneOutline | +| 视频通话 | VideoCameraOutlined | VideoCameraOutline | +| 语音 | AudioOutlined | AudioOutline | + +### 媒体类图标 + +| 功能描述 | PC端图标 | 移动端图标 | +|---------|---------|-----------| +| 图片 | PictureOutlined | PictureOutline | +| 视频 | VideoCameraOutlined | VideoCameraOutline | +| 音频 | AudioOutlined | AudioOutline | +| 文件 | FileOutlined | FileOutline | +| 文件夹 | FolderOutlined | FolderOutline | +| 相机 | CameraOutlined | CameraOutline | + +### 导航类图标 + +| 功能描述 | PC端图标 | 移动端图标 | +|---------|---------|-----------| +| 菜单 | MenuOutlined | MenuOutline | +| 汉堡菜单 | MenuFoldOutlined | MenuOutline | +| 展开菜单 | MenuUnfoldOutlined | MenuOutline | +| 面包屑 | BreadcrumbOutlined | BreadcrumbOutline | +| 分页 | PaginationOutlined | PaginationOutline | + +### 数据展示类图标 + +| 功能描述 | PC端图标 | 移动端图标 | +|---------|---------|-----------| +| 表格 | TableOutlined | TableOutline | +| 列表 | UnorderedListOutlined | UnorderedListOutline | +| 卡片 | CreditCardOutlined | CreditCardOutline | +| 统计 | BarChartOutlined | BarChartOutline | +| 饼图 | PieChartOutlined | PieChartOutline | +| 折线图 | LineChartOutlined | LineChartOutline | +| 仪表盘 | DashboardOutlined | DashboardOutline | + +### 反馈类图标 + +| 功能描述 | PC端图标 | 移动端图标 | +|---------|---------|-----------| +| 成功 | CheckCircleOutlined | CheckCircleOutline | +| 错误 | CloseCircleOutlined | CloseCircleOutline | +| 警告 | ExclamationCircleOutlined | ExclamationCircleOutline | +| 信息 | InfoCircleOutlined | InfoCircleOutline | +| 加载 | LoadingOutlined | LoadingOutline | +| 等待 | ClockCircleOutlined | ClockCircleOutline | + +## 使用建议 + +### 1. 项目类型判断 +- **PC端项目**: 使用 `@ant-design/icons` +- **移动端项目**: 使用 `antd-mobile-icons` + +### 2. 图标选择原则 +- 优先选择语义化图标 +- 保持图标风格一致性 +- 考虑图标在不同尺寸下的清晰度 + +### 3. 常见错误避免 +- 不要混用PC端和移动端图标 +- 注意图标名称的大小写 +- 确保图标在对应包中存在 + +### 4. 图标替换策略 +当某个图标在目标包中不存在时: +1. 查找语义相近的图标 +2. 使用通用图标(如QuestionOutlined) +3. 考虑使用文字替代 +4. 创建自定义图标组件 + +## 实际项目中的使用示例 + +### PC端项目示例 +```typescript +import { + HomeOutlined, + UserOutlined, + SettingOutlined, + SearchOutlined, + PlusOutlined, + EditOutlined, + DeleteOutlined, + CopyOutlined, + ReloadOutlined, + QrcodeOutlined, + CalendarOutlined, + ClockCircleOutlined, + UpOutlined, + DownOutlined, + LeftOutlined, + RightOutlined, + CloseOutlined, + CheckOutlined, + ExclamationCircleOutlined, + InfoCircleOutlined, + CheckCircleOutlined, + CloseCircleOutlined, +} from '@ant-design/icons'; +``` + +### 移动端项目示例 +```typescript +import { + HomeOutline, + UserOutline, + SettingOutline, + SearchOutline, + AddOutline, + EditSOutline, + DeleteOutline, + CopyOutline, + RefreshOutline, + QrCodeOutline, + CalendarOutline, + ClockCircleOutline, + UpOutline, + DownOutline, + LeftOutline, + RightOutline, + CloseOutline, + CheckOutline, + ExclamationCircleOutline, + InfoCircleOutline, + CheckCircleOutline, + CloseCircleOutline, +} from 'antd-mobile-icons'; +``` + +## 注意事项 + +1. **包依赖**: 确保项目中已安装对应的图标包 +2. **版本兼容**: 注意图标包版本与UI框架版本的兼容性 +3. **性能考虑**: 按需导入图标,避免全量导入 +4. **样式覆盖**: 可以通过CSS自定义图标颜色和大小 +5. **无障碍**: 为图标添加适当的aria-label属性 + +## 更新记录 + +- 2024-01-XX: 初始版本,包含常用图标对照 +- 后续根据实际使用情况持续更新 \ No newline at end of file diff --git a/nkebao/ICON_QUICK_REFERENCE.md b/nkebao/ICON_QUICK_REFERENCE.md new file mode 100644 index 000000000..03b9c2339 --- /dev/null +++ b/nkebao/ICON_QUICK_REFERENCE.md @@ -0,0 +1,271 @@ +# 图标快速参考表 + +## 快速查找 + +### 🔍 按功能查找 + +| 功能 | PC端 | 移动端 | 导入方式 | +|------|------|--------|----------| +| **添加** | PlusOutlined | AddOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **编辑** | EditOutlined | EditSOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **删除** | DeleteOutlined | DeleteOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **复制** | CopyOutlined | CopyOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **搜索** | SearchOutlined | SearchOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **刷新** | ReloadOutlined | RefreshOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **设置** | SettingOutlined | SettingOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **用户** | UserOutlined | UserOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **首页** | HomeOutlined | HomeOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **返回** | ArrowLeftOutlined | LeftOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **关闭** | CloseOutlined | CloseOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **确认** | CheckOutlined | CheckOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **成功** | CheckCircleOutlined | CheckCircleOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **错误** | CloseCircleOutlined | CloseCircleOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **警告** | ExclamationCircleOutlined | ExclamationCircleOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **信息** | InfoCircleOutlined | InfoCircleOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **加载** | LoadingOutlined | LoadingOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **向上** | UpOutlined | UpOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **向下** | DownOutlined | DownOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **向左** | LeftOutlined | LeftOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **向右** | RightOutlined | RightOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **消息** | MessageOutlined | MessageOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **电话** | PhoneOutlined | PhoneOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **日历** | CalendarOutlined | CalendarOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **时钟** | ClockCircleOutlined | ClockCircleOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **图片** | PictureOutlined | PictureOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **文件** | FileOutlined | FileOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **相机** | CameraOutlined | CameraOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **二维码** | QrcodeOutlined | QrCodeOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **微信** | WechatOutlined | WechatOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **设备** | MobileOutlined | MobileOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **团队** | TeamOutlined | TeamOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **订单** | ShoppingOutlined | ShoppingOutline | `@ant-design/icons` / `antd-mobile-icons` | +| **支付** | PayCircleOutlined | PayCircleOutline | `@ant-design/icons` / `antd-mobile-icons` | + +### 🔄 常见替换 + +| 原图标 | 替换为 | 说明 | +|--------|--------|------| +| RiseOutlined | UpOutline | 上升趋势 | +| ThumbsUpOutlined | LikeOutline | 点赞功能 | +| ShareAltOutlined | LinkOutline | 分享功能 | +| BarChartOutlined | PieOutline | 图表功能 | +| LineChartOutlined | PieOutline | 图表功能 | +| UserAddOutlined | UserOutline | 用户添加 | +| SettingOutline | SettingOutline | 设置(移动端) | + +## 导入模板 + +### PC端项目模板 +```typescript +import { + HomeOutlined, + UserOutlined, + SettingOutlined, + SearchOutlined, + PlusOutlined, + EditOutlined, + DeleteOutlined, + CopyOutlined, + ReloadOutlined, + CloseOutlined, + CheckOutlined, + UpOutlined, + DownOutlined, + LeftOutlined, + RightOutlined, + MessageOutlined, + CalendarOutlined, + ClockCircleOutlined, + PictureOutlined, + FileOutlined, + CameraOutlined, + QrcodeOutlined, + WechatOutlined, + MobileOutlined, + TeamOutlined, + ShoppingOutlined, + PayCircleOutlined, + CheckCircleOutlined, + CloseCircleOutlined, + ExclamationCircleOutlined, + InfoCircleOutlined, + LoadingOutlined, +} from '@ant-design/icons'; +``` + +### 移动端项目模板 +```typescript +import { + HomeOutline, + UserOutline, + SettingOutline, + SearchOutline, + AddOutline, + EditSOutline, + DeleteOutline, + CopyOutline, + RefreshOutline, + CloseOutline, + CheckOutline, + UpOutline, + DownOutline, + LeftOutline, + RightOutline, + MessageOutline, + CalendarOutline, + ClockCircleOutline, + PictureOutline, + FileOutline, + CameraOutline, + QrCodeOutline, + WechatOutline, + MobileOutline, + TeamOutline, + ShoppingOutline, + PayCircleOutline, + CheckCircleOutline, + CloseCircleOutline, + ExclamationCircleOutline, + InfoCircleOutline, + LoadingOutline, +} from 'antd-mobile-icons'; +``` + +## 使用示例 + +### 基础使用 +```typescript +// PC端 +import { HomeOutlined, UserOutlined } from '@ant-design/icons'; + + + + +// 移动端 +import { HomeOutline, UserOutline } from 'antd-mobile-icons'; + + + +``` + +### 按钮中使用 +```typescript +// PC端 +import { PlusOutlined, EditOutlined } from '@ant-design/icons'; + + + + +// 移动端 +import { AddOutline, EditSOutline } from 'antd-mobile-icons'; + + + +``` + +### 列表中使用 +```typescript +// PC端 +import { DeleteOutlined, CopyOutlined } from '@ant-design/icons'; + + + + +// 移动端 +import { DeleteOutline, CopyOutline } from 'antd-mobile-icons'; + + + +``` + +## 常见错误 + +### ❌ 错误示例 +```typescript +// 错误:混用PC端和移动端图标 +import { HomeOutlined } from '@ant-design/icons'; // PC端 +import { UserOutline } from 'antd-mobile-icons'; // 移动端 + +// 错误:使用不存在的图标 +import { RiseOutlined } from 'antd-mobile-icons'; // 不存在 +import { UserAddOutline } from 'antd-mobile-icons'; // 不存在 +``` + +### ✅ 正确示例 +```typescript +// 正确:统一使用移动端图标 +import { + HomeOutline, + UserOutline, + UpOutline, // 替换RiseOutlined + UserOutline // 替换UserAddOutline +} from 'antd-mobile-icons'; + +// 正确:统一使用PC端图标 +import { + HomeOutlined, + UserOutlined, + RiseOutlined, // PC端存在 + UserAddOutlined // PC端存在 +} from '@ant-design/icons'; +``` + +## 快速检查清单 + +### 开发前检查 +- [ ] 确认项目类型(PC端/移动端) +- [ ] 选择对应的图标包 +- [ ] 检查图标是否存在 +- [ ] 准备替换方案 + +### 开发中检查 +- [ ] 使用正确的导入方式 +- [ ] 图标名称大小写正确 +- [ ] 避免混用不同包的图标 +- [ ] 为图标添加适当的样式 + +### 开发后检查 +- [ ] 图标显示正常 +- [ ] 样式符合设计要求 +- [ ] 无障碍属性完整 +- [ ] 性能影响最小 + +## 紧急替换方案 + +当遇到图标不存在时,使用以下通用图标: + +```typescript +// 移动端通用图标 +import { + QuestionCircleOutline, // 通用问号 + AppOutline, // 通用应用 + ToolOutline, // 通用工具 + SettingOutline, // 通用设置 + UserOutline, // 通用用户 +} from 'antd-mobile-icons'; + +// PC端通用图标 +import { + QuestionCircleOutlined, // 通用问号 + AppstoreOutlined, // 通用应用 + ToolOutlined, // 通用工具 + SettingOutlined, // 通用设置 + UserOutlined, // 通用用户 +} from '@ant-design/icons'; +``` + +## 更新日志 + +- 2024-01-XX: 初始版本 +- 添加常用图标对照 +- 添加错误示例和正确示例 +- 添加快速检查清单 +- 添加紧急替换方案 \ No newline at end of file diff --git a/nkebao/README.md b/nkebao/README.md deleted file mode 100644 index 88c0217ec..000000000 --- a/nkebao/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# Nkebao Base - -一个现代化的 React + TypeScript 前端项目模板,适用于中后台管理系统、移动端 H5 等场景。 -集成了 Vite 极速构建、Zustand 轻量状态管理、模块化路由与权限拦截、axios 请求封装、移动端 rem 适配等主流工程能力。 - ---- - -## 启动方式 - -```bash -yarn install -yarn dev -``` - ---- - -## 技术栈 - -- **React 18**:主流前端框架 -- **TypeScript**:类型安全开发 -- **Vite**:极速开发与构建工具 -- **axios**:请求库,统一封装 -- **sass**:CSS 预处理 -- **React Router v6**:路由管理,支持模块化与权限拦截 -- **antd / antd-mobile**:桌面端与移动端 UI 组件库 -- **postcss-pxtorem**:rem 适配,移动端友好 -- **Zustand**:轻量状态管理,支持持久化 -- **ESLint + Prettier**:代码规范与自动格式化 -- **路径别名 @**:简化 import - ---- - -## 目录结构 - -``` -src/ - api/ # axios 封装与接口模块 - pages/ # 页面组件 - styles/ # 全局样式 - App.tsx # 入口组件 - main.tsx # 入口文件 -public/ - index.html # HTML 模板 -``` \ No newline at end of file diff --git a/nkebao/ROUTE_MIGRATION.md b/nkebao/ROUTE_MIGRATION.md deleted file mode 100644 index 9f1794832..000000000 --- a/nkebao/ROUTE_MIGRATION.md +++ /dev/null @@ -1,140 +0,0 @@ -# 路由迁移总结 - -## 📋 迁移概述 - -已成功将旧项目 @Cunkebao 的路由结构迁移到新项目 @nkebao,并进行了合理的文件拆分和归类。 - -## 🗂️ 路由模块结构 - -### 1. 基础路由模块 (`auth.tsx`) -- `/login` - 登录页面 - -### 2. 设备管理模块 (`devices.tsx`) -- `/devices` - 设备列表 -- `/devices/:id` - 设备详情 - -### 3. 微信号管理模块 (`wechat-accounts.tsx`) -- `/wechat-accounts` - 微信号列表 -- `/wechat-accounts/:id` - 微信号详情 - -### 4. 工作台模块 (`workspace.tsx`) -- `/workspace` - 工作台主页 -- `/workspace/auto-like` - 自动点赞 -- `/workspace/auto-like/new` - 新建自动点赞 -- `/workspace/auto-like/:id` - 自动点赞详情 -- `/workspace/auto-like/:id/edit` - 编辑自动点赞 -- `/workspace/auto-group` - 自动分组 -- `/workspace/auto-group/:id` - 自动分组详情 -- `/workspace/group-push` - 群发推送 -- `/workspace/group-push/new` - 新建群发推送 -- `/workspace/group-push/:id` - 群发推送详情 -- `/workspace/group-push/:id/edit` - 编辑群发推送 -- `/workspace/moments-sync` - 朋友圈同步 -- `/workspace/moments-sync/new` - 新建朋友圈同步 -- `/workspace/moments-sync/:id` - 朋友圈同步详情 -- `/workspace/moments-sync/edit/:id` - 编辑朋友圈同步 -- `/workspace/ai-assistant` - AI助手 -- `/workspace/traffic-distribution` - 流量分发 -- `/workspace/traffic-distribution/new` - 新建流量分发 -- `/workspace/traffic-distribution/edit/:id` - 编辑流量分发 -- `/workspace/traffic-distribution/:id` - 流量分发详情 - -### 5. 场景管理模块 (`scenarios.tsx`) -- `/scenarios` - 场景列表 -- `/scenarios/new` - 新建场景 -- `/scenarios/new/:scenarioId` - 新建场景(带场景ID) -- `/scenarios/edit/:planId` - 编辑场景 -- `/scenarios/list/:scenarioId/:scenarioName` - 场景列表详情 - -### 6. 内容管理模块 (`content.tsx`) -- `/content` - 内容管理 -- `/content/new` - 新建内容 -- `/content/edit/:id` - 编辑内容 -- `/content/materials/:id` - 素材管理 -- `/content/materials/new/:id` - 新建素材 -- `/content/materials/edit/:id/:materialId` - 编辑素材 - -### 7. 流量池模块 (`traffic-pool.tsx`) -- `/traffic-pool` - 流量池列表 -- `/traffic-pool/:id` - 流量池详情 - -### 8. 其他功能模块 (`other.tsx`) -- `/profile` - 个人中心 -- `/plans` - 计划管理 -- `/plans/:planId` - 计划详情 -- `/orders` - 订单管理 -- `/contact-import` - 联系人导入 - -## 🔧 技术实现 - -### 路由配置 -- 使用 React Router DOM v6 -- 支持动态路由参数 -- 统一的权限控制 -- 模块化路由管理 - -### 权限控制 -- 每个路由都配置了 `auth` 属性 -- 支持角色权限控制 -- 自动权限检查 - -### 组件结构 -- 使用 Layout 组件统一布局 -- 使用 MeauMobile 组件作为底部导航 -- 创建了 PlaceholderPage 通用占位组件 - -## 📁 文件结构 - -``` -src/ -├── router/ -│ ├── module/ -│ │ ├── index.tsx # 主路由文件 -│ │ ├── auth.tsx # 认证路由 -│ │ ├── devices.tsx # 设备管理路由 -│ │ ├── wechat-accounts.tsx # 微信号管理路由 -│ │ ├── workspace.tsx # 工作台路由 -│ │ ├── scenarios.tsx # 场景管理路由 -│ │ ├── content.tsx # 内容管理路由 -│ │ ├── traffic-pool.tsx # 流量池路由 -│ │ └── other.tsx # 其他功能路由 -│ ├── index.tsx # 路由入口 -│ ├── permissionRoute.tsx # 权限路由组件 -│ └── config.ts # 路由配置 -├── pages/ -│ ├── login/ # 登录页面 -│ ├── devices/ # 设备管理页面 -│ ├── wechat-accounts/ # 微信号管理页面 -│ ├── workspace/ # 工作台页面 -│ ├── scenarios/ # 场景管理页面 -│ ├── content/ # 内容管理页面 -│ ├── traffic-pool/ # 流量池页面 -│ ├── profile/ # 个人中心页面 -│ ├── plans/ # 计划管理页面 -│ ├── orders/ # 订单管理页面 -│ └── contact-import/ # 联系人导入页面 -└── components/ - └── PlaceholderPage.tsx # 通用占位页面组件 -``` - -## 🎯 主要特性 - -1. **模块化设计**: 按功能模块拆分路由,便于维护 -2. **权限控制**: 统一的权限管理机制 -3. **类型安全**: 完整的 TypeScript 类型定义 -4. **响应式设计**: 适配移动端和桌面端 -5. **代码复用**: 使用通用组件减少重复代码 - -## 🚀 下一步计划 - -1. 实现具体的页面功能 -2. 完善权限控制逻辑 -3. 添加路由懒加载 -4. 优化页面性能 -5. 添加路由守卫 - -## 📝 注意事项 - -- 所有页面目前都是占位页面,需要根据业务需求逐步实现 -- 路由权限控制需要根据实际业务逻辑调整 -- 建议按照模块优先级逐步开发页面功能 \ No newline at end of file diff --git a/nkebao/WORKSPACE_MIGRATION.md b/nkebao/WORKSPACE_MIGRATION.md deleted file mode 100644 index 436747bff..000000000 --- a/nkebao/WORKSPACE_MIGRATION.md +++ /dev/null @@ -1,159 +0,0 @@ -# 工作台页面迁移总结 - -## 📋 迁移概述 - -已成功将旧项目 @Cunkebao 的工作台页面迁移到新项目 @nkebao,并进行了技术栈转换和样式优化。 - -## 🔄 技术栈转换 - -### 旧项目技术栈 -- React + TypeScript -- Lucide React 图标库 -- Tailwind CSS -- React Router DOM v6 -- 自定义UI组件库 - -### 新项目技术栈 -- React + TypeScript -- Ant Design Mobile -- SCSS Modules -- React Router DOM v6 -- 统一的Layout组件 - -## 📱 页面结构 - -### 主要功能模块 - -1. **任务统计卡片** - - 总任务数(带进度条) - - 今日任务(带活跃度指标) - -2. **常用功能** - - 自动点赞(New标签) - - 朋友圈同步 - - 群消息推送 - - 自动建群 - - 流量分发 - - AI对话助手(New标签) - -3. **AI智能助手** - - AI数据分析(New标签) - - AI策略优化(New标签) - - AI销售预测 - -## 🎨 设计优化 - -### 视觉改进 -- 使用Ant Design Mobile组件,保持移动端一致性 -- 采用SCSS Modules进行样式管理 -- 统一的颜色主题和间距规范 -- 响应式设计,适配不同屏幕尺寸 - -### 交互优化 -- 卡片悬停效果 -- 统一的图标样式和颜色 -- 清晰的层级结构 -- 流畅的页面跳转 - -## 📁 文件结构 - -``` -src/pages/workspace/ -├── Workspace.tsx # 工作台主页 -├── Workspace.module.scss # 工作台样式 -├── auto-like/ # 自动点赞模块 -├── auto-group/ # 自动分组模块 -├── group-push/ # 群发推送模块 -├── moments-sync/ # 朋友圈同步模块 -├── ai-assistant/ # AI助手模块 -└── traffic-distribution/ # 流量分发模块 -``` - -## 🔗 路由配置 - -### 主要路由 -- `/workspace` - 工作台主页 -- `/workspace/auto-like` - 自动点赞 -- `/workspace/group-push` - 群发推送 -- `/workspace/moments-sync` - 朋友圈同步 -- `/workspace/auto-group` - 自动分组 -- `/workspace/traffic-distribution` - 流量分发 -- `/workspace/ai-assistant` - AI助手 - -### AI功能路由 -- `/workspace/ai-analyzer` - AI数据分析 -- `/workspace/ai-strategy` - AI策略优化 -- `/workspace/ai-forecast` - AI销售预测 - -## 🎯 功能特性 - -### 数据展示 -- 实时任务统计 -- 进度条可视化 -- 活跃度指标 -- 功能分类展示 - -### 导航优化 -- 底部导航集成 -- 路由自动激活 -- 页面跳转优化 -- 返回按钮处理 - -### 用户体验 -- 加载状态处理 -- 错误边界处理 -- 响应式布局 -- 触摸友好设计 - -## 🔧 技术实现 - -### 组件化设计 -- 使用Layout组件统一布局 -- 复用PlaceholderPage组件 -- 模块化的功能卡片 -- 统一的样式规范 - -### 状态管理 -- 模拟数据展示 -- 路由状态同步 -- 组件生命周期管理 -- 性能优化 - -### 样式系统 -- SCSS Modules避免样式冲突 -- CSS变量统一主题 -- 响应式断点设计 -- 移动端优化 - -## 🚀 下一步计划 - -1. **数据集成** - - 连接真实API接口 - - 实现数据获取和更新 - - 添加加载状态 - - 错误处理机制 - -2. **功能完善** - - 实现具体功能页面 - - 添加表单验证 - - 完善交互逻辑 - - 优化用户体验 - -3. **性能优化** - - 路由懒加载 - - 组件代码分割 - - 图片资源优化 - - 缓存策略 - -4. **测试完善** - - 单元测试 - - 集成测试 - - 端到端测试 - - 性能测试 - -## 📝 注意事项 - -- 所有功能页面目前使用占位组件 -- 需要根据实际业务需求调整数据展示 -- 建议按照用户使用频率优先开发核心功能 -- 注意保持与整体设计风格的一致性 \ No newline at end of file diff --git a/nkebao/src/components/Layout/Layout.tsx b/nkebao/src/components/Layout/Layout.tsx index 2cef351d8..eea22b398 100644 --- a/nkebao/src/components/Layout/Layout.tsx +++ b/nkebao/src/components/Layout/Layout.tsx @@ -1,5 +1,7 @@ import React from "react"; +import { SpinLoading } from "antd-mobile"; import styles from "./layout.module.scss"; + interface LayoutProps { loading?: boolean; children?: React.ReactNode; @@ -7,11 +9,25 @@ interface LayoutProps { footer?: React.ReactNode; } -const Layout: React.FC = ({ children, header, footer }) => { +const Layout: React.FC = ({ + children, + header, + footer, + loading = false, +}) => { return (
{header &&
{header}
} -
{children}
+
+ {loading ? ( +
+ +
加载中...
+
+ ) : ( + children + )} +
{footer &&
{footer}
}
); diff --git a/nkebao/src/components/Layout/layout.module.scss b/nkebao/src/components/Layout/layout.module.scss index 796b26365..ed336f6fe 100644 --- a/nkebao/src/components/Layout/layout.module.scss +++ b/nkebao/src/components/Layout/layout.module.scss @@ -8,4 +8,21 @@ .container main { flex: 1; overflow: auto; +} + +.loadingContainer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + min-height: 300px; + background: rgba(255, 255, 255, 0.8); +} + +.loadingText { + margin-top: 16px; + color: #666; + font-size: 14px; + text-align: center; } \ No newline at end of file diff --git a/nkebao/src/components/MeauMobile/MeauMoible.tsx b/nkebao/src/components/MeauMobile/MeauMoible.tsx index 52a71d209..5f6f80869 100644 --- a/nkebao/src/components/MeauMobile/MeauMoible.tsx +++ b/nkebao/src/components/MeauMobile/MeauMoible.tsx @@ -1,81 +1,81 @@ -import React, { useState, useEffect } from "react"; -import { TabBar } from "antd-mobile"; -import { - AppOutline, - ShopbagOutline, - PieOutline, - UserOutline, -} from "antd-mobile-icons"; -import { useLocation, useNavigate } from "react-router-dom"; - -const tabs = [ - { - key: "home", - title: "首页", - icon: , - path: "/", - }, - { - key: "scene", - title: "场景获客", - icon: , - path: "/scene", - }, - { - key: "work", - title: "工作台", - icon: , - path: "/workspace", - }, - { - key: "mine", - title: "我的", - icon: , - path: "/mine", - }, -]; - -// 需要展示菜单的路由白名单(可根据实际业务调整) -const menuPaths = ["/", "/scene", "/workspace", "/mine"]; - -const MeauMobile: React.FC = () => { - const location = useLocation(); - const navigate = useNavigate(); - const [activeKey, setActiveKey] = useState("home"); - - // 根据当前路由自动设置 activeKey,支持嵌套路由 - useEffect(() => { - const found = tabs.find((tab) => - tab.path === "/" - ? location.pathname === "/" - : location.pathname.startsWith(tab.path) - ); - if (found) setActiveKey(found.key); - }, [location.pathname]); - - // 判断当前路由是否需要展示菜单 - const showMenu = menuPaths.some((path) => - path === "/" - ? location.pathname === "/" - : location.pathname.startsWith(path) - ); - if (!showMenu) return null; - - return ( - { - setActiveKey(key); - const tab = tabs.find((t) => t.key === key); - if (tab && tab.path) navigate(tab.path); - }} - > - {tabs.map((item) => ( - - ))} - - ); -}; - -export default MeauMobile; +import React, { useState, useEffect } from "react"; +import { TabBar } from "antd-mobile"; +import { + AppOutline, + ShopbagOutline, + PieOutline, + UserOutline, +} from "antd-mobile-icons"; +import { useLocation, useNavigate } from "react-router-dom"; + +const tabs = [ + { + key: "home", + title: "首页", + icon: , + path: "/", + }, + { + key: "scene", + title: "场景获客", + icon: , + path: "/scene", + }, + { + key: "work", + title: "工作台", + icon: , + path: "/workspace", + }, + { + key: "mine", + title: "我的", + icon: , + path: "/mine", + }, +]; + +// 需要展示菜单的路由白名单(可根据实际业务调整) +const menuPaths = ["/", "/scene", "/workspace", "/mine"]; + +const MeauMobile: React.FC = () => { + const location = useLocation(); + const navigate = useNavigate(); + const [activeKey, setActiveKey] = useState("home"); + + // 根据当前路由自动设置 activeKey,支持嵌套路由 + useEffect(() => { + const found = tabs.find((tab) => + tab.path === "/" + ? location.pathname === "/" + : location.pathname.startsWith(tab.path) + ); + if (found) setActiveKey(found.key); + }, [location.pathname]); + + // 判断当前路由是否需要展示菜单 + const showMenu = menuPaths.some((path) => + path === "/" + ? location.pathname === "/" + : location.pathname.startsWith(path) + ); + if (!showMenu) return null; + + return ( + { + setActiveKey(key); + const tab = tabs.find((t) => t.key === key); + if (tab && tab.path) navigate(tab.path); + }} + > + {tabs.map((item) => ( + + ))} + + ); +}; + +export default MeauMobile; diff --git a/nkebao/src/components/PlaceholderPage.tsx b/nkebao/src/components/PlaceholderPage.tsx index 1e5f87d73..4eec7f2f8 100644 --- a/nkebao/src/components/PlaceholderPage.tsx +++ b/nkebao/src/components/PlaceholderPage.tsx @@ -1,56 +1,56 @@ -import React from "react"; -import { NavBar, Button } from "antd-mobile"; -import { AddOutline } from "antd-mobile-icons"; -import Layout from "@/components/Layout/Layout"; -import MeauMobile from "@/components/MeauMobile/MeauMoible"; - -interface PlaceholderPageProps { - title: string; - showBack?: boolean; - showAddButton?: boolean; - addButtonText?: string; - showFooter?: boolean; -} - -const PlaceholderPage: React.FC = ({ - title, - showBack = true, - showAddButton = false, - addButtonText = "新建", - showFooter = true, -}) => { - return ( - window.history.back() : undefined} - left={ -
- {title} -
- } - right={ - showAddButton ? ( - - ) : undefined - } - /> - } - footer={showFooter ? : undefined} - > -
-

{title}页面

-

此页面正在开发中...

-
-
- ); -}; - -export default PlaceholderPage; +import React from "react"; +import { NavBar, Button } from "antd-mobile"; +import { PlusOutlined } from "@ant-design/icons"; +import Layout from "@/components/Layout/Layout"; +import MeauMobile from "@/components/MeauMobile/MeauMoible"; + +interface PlaceholderPageProps { + title: string; + showBack?: boolean; + showAddButton?: boolean; + addButtonText?: string; + showFooter?: boolean; +} + +const PlaceholderPage: React.FC = ({ + title, + showBack = true, + showAddButton = false, + addButtonText = "新建", + showFooter = true, +}) => { + return ( + window.history.back() : undefined} + left={ +
+ {title} +
+ } + right={ + showAddButton ? ( + + ) : undefined + } + /> + } + footer={showFooter ? : undefined} + > +
+

{title}页面

+

此页面正在开发中...

+
+
+ ); +}; + +export default PlaceholderPage; diff --git a/nkebao/src/pages/scenarios/ScenarioList.module.scss b/nkebao/src/pages/scenarios/ScenarioList.module.scss new file mode 100644 index 000000000..67292ac1d --- /dev/null +++ b/nkebao/src/pages/scenarios/ScenarioList.module.scss @@ -0,0 +1,250 @@ +.scenario-list-page { + padding: 16px; + background: #f5f5f5; + min-height: 100vh; +} + +.nav-title { + font-size: 18px; + font-weight: 600; + color: #333; +} + +.new-plan-btn { + font-size: 14px; + height: 32px; + padding: 0 12px; +} + +.loading { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 60vh; + gap: 16px; +} + +.loading-text { + color: #666; + font-size: 14px; +} + +.scenario-card { + margin-bottom: 16px; + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.scenario-info { + padding: 16px; +} + +.scenario-name { + font-size: 18px; + font-weight: 600; + color: #333; + margin-bottom: 8px; +} + +.scenario-desc { + font-size: 14px; + color: #666; + line-height: 1.5; + margin-bottom: 16px; +} + +.scenario-stats { + display: flex; + gap: 24px; +} + +.stat-item { + display: flex; + align-items: center; + gap: 6px; + font-size: 14px; + color: #666; + + svg { + font-size: 16px; + color: #1890ff; + } +} + +.search-bar { + display: flex; + gap: 12px; + margin-bottom: 16px; + align-items: center; + + .adm-input { + flex: 1; + border-radius: 8px; + } +} + +.refresh-btn { + height: 40px; + width: 40px; + padding: 0; + border-radius: 8px; +} + +.plan-list { + background: transparent; + border-radius: 12px; + overflow: hidden; +} + +.plan-item { + background: white; + margin-bottom: 12px; + border-radius: 12px; + padding: 16px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + cursor: pointer; + transition: all 0.2s ease; + + &:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + } + + &:last-child { + margin-bottom: 0; + } +} + +.plan-content { + flex: 1; +} + +.plan-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 12px; +} + +.plan-name { + font-size: 16px; + font-weight: 600; + color: #333; + flex: 1; + margin-right: 12px; +} + +.plan-meta { + display: flex; + flex-direction: column; + gap: 4px; + font-size: 12px; + color: #999; +} + +.plan-actions { + margin-top: 12px; + display: flex; + justify-content: flex-end; +} + +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 60px 20px; + text-align: center; +} + +.empty-text { + color: #999; + font-size: 14px; + margin-bottom: 20px; +} + +.create-first-btn { + height: 40px; + padding: 0 24px; + border-radius: 20px; +} + +.api-dialog { + background: white; + border-radius: 16px 16px 0 0; + padding: 20px; + height: 100%; + display: flex; + flex-direction: column; +} + +.dialog-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + padding-bottom: 16px; + border-bottom: 1px solid #f0f0f0; + + h3 { + margin: 0; + font-size: 18px; + font-weight: 600; + color: #333; + } +} + +.dialog-content { + flex: 1; + overflow-y: auto; +} + +.api-item { + margin-bottom: 20px; + + label { + display: block; + font-size: 14px; + color: #333; + margin-bottom: 8px; + font-weight: 500; + } + + .adm-input { + border-radius: 8px; + } +} + +.qr-dialog { + background: white; + border-radius: 16px; + padding: 20px; + width: 300px; + max-width: 90vw; +} + +.qr-loading { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 40px 20px; + gap: 16px; + color: #666; + font-size: 14px; +} + +.qr-image { + width: 100%; + max-width: 200px; + height: auto; + border-radius: 8px; +} + +.qr-error { + text-align: center; + color: #ff4d4f; + font-size: 14px; + padding: 40px 20px; +} \ No newline at end of file diff --git a/nkebao/src/pages/scenarios/ScenarioList.tsx b/nkebao/src/pages/scenarios/ScenarioList.tsx index f1088dc79..30e3710b8 100644 --- a/nkebao/src/pages/scenarios/ScenarioList.tsx +++ b/nkebao/src/pages/scenarios/ScenarioList.tsx @@ -1,8 +1,618 @@ -import React from "react"; -import PlaceholderPage from "@/components/PlaceholderPage"; - -const ScenarioList: React.FC = () => { - return ; -}; - -export default ScenarioList; +import React, { useEffect, useState, useCallback } from "react"; +import { useParams, useNavigate, useSearchParams } from "react-router-dom"; +import { + NavBar, + List, + Button, + Toast, + SpinLoading, + Dialog, + Input, + Popup, + Card, + Tag, + Space, + Divider, +} from "antd-mobile"; +import { + PlusOutlined, + UserOutlined, + CalendarOutlined, + CopyOutlined, + DeleteOutlined, + EditOutlined, + SettingOutlined, + SearchOutlined, + ReloadOutlined, + QrcodeOutlined, + UpOutlined, +} from "@ant-design/icons"; + +import Layout from "@/components/Layout/Layout"; +import MeauMobile from "@/components/MeauMobile/MeauMoible"; +import { + getPlanList, + getPlanDetail, + copyPlan, + deletePlan, + getWxMinAppCode, +} from "./api"; +import style from "./ScenarioList.module.scss"; + +interface Task { + id: string; + name: string; + status: number; + created_at: string; + updated_at: string; + enabled: boolean; + total_customers?: number; + today_customers?: number; +} + +interface ScenarioData { + id: string; + name: string; + image: string; + description: string; + totalPlans: number; + totalCustomers: number; + todayCustomers: number; + growth: string; +} + +interface ApiSettings { + apiKey: string; + webhookUrl: string; + taskId: string; +} + +const ScenarioList: React.FC = () => { + const { scenarioId, scenarioName } = useParams<{ + scenarioId: string; + scenarioName: string; + }>(); + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + const [scenario, setScenario] = useState(null); + const [tasks, setTasks] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(""); + const [showApiDialog, setShowApiDialog] = useState(false); + const [currentApiSettings, setCurrentApiSettings] = useState({ + apiKey: "", + webhookUrl: "", + taskId: "", + }); + const [searchTerm, setSearchTerm] = useState(""); + const [loadingTasks, setLoadingTasks] = useState(false); + const [showQrDialog, setShowQrDialog] = useState(false); + const [qrLoading, setQrLoading] = useState(false); + const [qrImg, setQrImg] = useState(""); + + // 获取渠道中文名称 + const getChannelName = (channel: string) => { + const channelMap: Record = { + douyin: "抖音直播获客", + kuaishou: "快手直播获客", + xiaohongshu: "小红书种草获客", + weibo: "微博话题获客", + haibao: "海报扫码获客", + phone: "电话号码获客", + gongzhonghao: "公众号引流获客", + weixinqun: "微信群裂变获客", + payment: "付款码获客", + api: "API接口获客", + }; + return channelMap[channel] || `${channel}获客`; + }; + + // 获取场景描述 + const getScenarioDescription = (channel: string) => { + const descriptions: Record = { + douyin: "通过抖音平台进行精准获客,利用短视频内容吸引目标用户", + xiaohongshu: "利用小红书平台进行内容营销获客,通过优质内容建立品牌形象", + gongzhonghao: "通过微信公众号进行获客,建立私域流量池", + haibao: "通过海报分享进行获客,快速传播品牌信息", + phone: "通过电话营销进行获客,直接与客户沟通", + weixinqun: "通过微信群进行获客,利用社交裂变效应", + payment: "通过付款码进行获客,便捷的支付方式", + api: "通过API接口进行获客,支持第三方系统集成", + }; + return descriptions[channel] || "通过该平台进行获客"; + }; + + useEffect(() => { + const fetchScenarioData = async () => { + if (!scenarioId) return; + + setLoading(true); + setError(""); + + try { + // 获取计划列表 + const response = await getPlanList(scenarioId, 1, 20); + + // 设置计划列表 + if (response && response.data && response.data.list) { + setTasks(response.data.list); + } else { + setTasks([]); + } + + // 构建场景数据 + const scenarioData: ScenarioData = { + id: scenarioId, + name: scenarioName || "", + image: "", + description: getScenarioDescription(scenarioId), + totalPlans: response?.data?.list?.length || 0, + totalCustomers: 0, + todayCustomers: 0, + growth: "", + }; + + setScenario(scenarioData); + } catch (error) { + console.error("获取场景数据失败:", error); + // 即使API失败也要创建基本的场景数据 + const scenarioData: ScenarioData = { + id: scenarioId, + name: getScenarioName(), + image: "", + description: getScenarioDescription(scenarioId), + totalPlans: 0, + totalCustomers: 0, + todayCustomers: 0, + growth: "", + }; + setScenario(scenarioData); + setTasks([]); + } finally { + setLoading(false); + } + }; + + fetchScenarioData(); + }, [scenarioId]); + + // 获取场景名称 + const getScenarioName = useCallback(() => { + const urlName = searchParams.get("name"); + if (urlName) { + return urlName; + } + return getChannelName(scenarioId || ""); + }, [searchParams, scenarioId]); + + // 更新场景数据中的名称 + useEffect(() => { + setScenario((prev) => + prev + ? { + ...prev, + name: (() => { + const urlName = searchParams.get("name"); + if (urlName) return urlName; + return getChannelName(scenarioId || ""); + })(), + } + : null + ); + }, [searchParams, scenarioId]); + + const handleCopyPlan = async (taskId: string) => { + const taskToCopy = tasks.find((task) => task.id === taskId); + if (!taskToCopy) return; + + try { + const response = await copyPlan(taskId); + if (response && response.code === 200) { + Toast.show({ + content: `已成功复制"${taskToCopy.name}"`, + position: "top", + }); + // 刷新列表 + handleRefresh(); + } else { + Toast.show({ + content: response?.msg || "复制失败", + position: "top", + }); + } + } catch (error) { + Toast.show({ + content: "复制失败,请重试", + position: "top", + }); + } + }; + + const handleDeletePlan = async (taskId: string) => { + const taskToDelete = tasks.find((task) => task.id === taskId); + if (!taskToDelete) return; + + const result = await Dialog.confirm({ + content: `确定要删除"${taskToDelete.name}"吗?`, + confirmText: "删除", + cancelText: "取消", + }); + + if (result) { + try { + const response = await deletePlan(taskId); + if (response && response.code === 200) { + Toast.show({ + content: "计划已删除", + position: "top", + }); + // 刷新列表 + handleRefresh(); + } else { + Toast.show({ + content: response?.msg || "删除失败", + position: "top", + }); + } + } catch (error) { + Toast.show({ + content: "删除失败,请重试", + position: "top", + }); + } + } + }; + + const handleOpenApiSettings = async (taskId: string) => { + try { + const response = await getPlanDetail(taskId); + if (response && response.code === 200 && response.data) { + const detail = response.data; + setCurrentApiSettings({ + apiKey: detail.apiKey || "", + webhookUrl: detail.webhookUrl || "", + taskId: taskId, + }); + setShowApiDialog(true); + } + } catch (error) { + Toast.show({ + content: "获取API设置失败", + position: "top", + }); + } + }; + + const handleCopyApiUrl = (url: string) => { + navigator.clipboard.writeText(url); + Toast.show({ + content: "API地址已复制到剪贴板", + position: "top", + }); + }; + + const handleCreateNewPlan = () => { + navigate(`/scenarios/new/${scenarioId}`); + }; + + const handleShowQrCode = async (taskId: string) => { + setQrLoading(true); + setShowQrDialog(true); + + try { + const response = await getWxMinAppCode(taskId); + if (response && response.code === 200 && response.data) { + setQrImg(response.data.qrCode || ""); + } else { + Toast.show({ + content: "获取二维码失败", + position: "top", + }); + } + } catch (error) { + Toast.show({ + content: "获取二维码失败", + position: "top", + }); + } finally { + setQrLoading(false); + } + }; + + const getStatusColor = (status: number) => { + switch (status) { + case 1: + return "success"; + case 0: + return "default"; + case -1: + return "danger"; + default: + return "default"; + } + }; + + const getStatusText = (status: number) => { + switch (status) { + case 1: + return "运行中"; + case 0: + return "已暂停"; + case -1: + return "已停止"; + default: + return "未知"; + } + }; + + const handleRefresh = async () => { + setLoadingTasks(true); + try { + const response = await getPlanList(scenarioId!, 1, 20); + if (response && response.data && response.data.list) { + setTasks(response.data.list); + } + } catch (error) { + Toast.show({ + content: "刷新失败", + position: "top", + }); + } finally { + setLoadingTasks(false); + } + }; + + const filteredTasks = tasks.filter((task) => + task.name.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + if (loading) { + return ( + +
场景详情
+ + } + footer={} + > +
+ +
加载场景数据中...
+
+
+ ); + } + + return ( + {scenario?.name}
} + right={ + + } + /> + } + footer={} + > +
+ {/* 场景信息卡片 */} + {scenario && ( + +
+
{scenario.name}
+
+ {scenario.description} +
+
+
+ + 计划数: {scenario.totalPlans} +
+
+ + 今日获客: {scenario.todayCustomers} +
+
+
+
+ )} + + {/* 搜索栏 */} +
+
+ + +
+ +
+ + {/* 计划列表 */} + + {filteredTasks.length === 0 ? ( +
+
+ {searchTerm ? "没有找到匹配的计划" : "暂无计划"} +
+ +
+ ) : ( + filteredTasks.map((task) => ( + navigate(`/scenarios/edit/${task.id}`)} + > +
+
+
{task.name}
+ + {getStatusText(task.status)} + +
+
+ 创建时间: {task.created_at} + {task.total_customers !== undefined && ( + 总获客: {task.total_customers} + )} + {task.today_customers !== undefined && ( + 今日获客: {task.today_customers} + )} +
+
+
+ + + + + + +
+
+ )) + )} +
+ + {/* API设置弹窗 */} + setShowApiDialog(false)} + position="bottom" + bodyStyle={{ height: "60vh" }} + > +
+
+

API设置

+ +
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+
+ + {/* 二维码弹窗 */} + setShowQrDialog(false)} + position="bottom" + > +
+
+

小程序二维码

+ +
+
+ {qrLoading ? ( +
+ +
生成二维码中...
+
+ ) : qrImg ? ( + 小程序二维码 + ) : ( +
二维码生成失败
+ )} +
+
+
+
+ + ); +}; + +export default ScenarioList; diff --git a/nkebao/src/pages/scenarios/api.ts b/nkebao/src/pages/scenarios/api.ts new file mode 100644 index 000000000..e7ae1867b --- /dev/null +++ b/nkebao/src/pages/scenarios/api.ts @@ -0,0 +1,51 @@ +import request from "@/api/request"; + +// 获取计划列表 +export const getPlanList = async (scenarioId: string, page: number = 1, limit: number = 20) => { + return request(`/api/scenarios/${scenarioId}/plans`, { page, limit }, 'GET'); +}; + +// 获取计划详情 +export const getPlanDetail = async (planId: string) => { + return request(`/api/scenarios/plans/${planId}`, undefined, 'GET'); +}; + +// 复制计划 +export const copyPlan = async (planId: string) => { + return request(`/api/scenarios/plans/${planId}/copy`, undefined, 'POST'); +}; + +// 删除计划 +export const deletePlan = async (planId: string) => { + return request(`/api/scenarios/plans/${planId}`, undefined, 'DELETE'); +}; + +// 创建计划 +export const createPlan = async (data: any) => { + return request('/api/scenarios/plans', data, 'POST'); +}; + +// 更新计划 +export const updatePlan = async (planId: string, data: any) => { + return request(`/api/scenarios/plans/${planId}`, data, 'PUT'); +}; + +// 获取小程序二维码 +export const getWxMinAppCode = async (planId: string) => { + return request(`/api/scenarios/plans/${planId}/qrcode`, undefined, 'GET'); +}; + +// 获取场景类型列表 +export const getScenarioTypes = async () => { + return request('/api/scenarios/types', undefined, 'GET'); +}; + +// 获取设备列表 +export const getDevices = async () => { + return request('/api/devices', undefined, 'GET'); +}; + +// 获取海报列表 +export const getPosters = async () => { + return request('/api/posters', undefined, 'GET'); +}; \ No newline at end of file diff --git a/nkebao/src/pages/scenarios/new/page.module.scss b/nkebao/src/pages/scenarios/new/page.module.scss new file mode 100644 index 000000000..eaff99f16 --- /dev/null +++ b/nkebao/src/pages/scenarios/new/page.module.scss @@ -0,0 +1,43 @@ +.new-plan-page { + background: #f5f5f5; + min-height: 100vh; +} + +.nav-title { + font-size: 18px; + font-weight: 600; + color: #333; +} + +.back-btn { + height: 32px; + width: 32px; + padding: 0; + border-radius: 50%; +} + +.loading { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 60vh; + gap: 16px; +} + +.loading-text { + color: #666; + font-size: 14px; +} + +.steps-container { + background: white; + padding: 20px 16px; + margin-bottom: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.step-content { + flex: 1; + padding: 0 16px; +} \ No newline at end of file diff --git a/nkebao/src/pages/scenarios/new/page.tsx b/nkebao/src/pages/scenarios/new/page.tsx index b79b30fee..f9cb25bb7 100644 --- a/nkebao/src/pages/scenarios/new/page.tsx +++ b/nkebao/src/pages/scenarios/new/page.tsx @@ -1,8 +1,293 @@ -import React from "react"; -import PlaceholderPage from "@/components/PlaceholderPage"; - -const NewPlan: React.FC = () => { - return ; -}; - -export default NewPlan; +import React, { useState, useEffect } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { NavBar, Button, Toast, SpinLoading, Steps, Popup } from "antd-mobile"; +import { LeftOutline } from "antd-mobile-icons"; +import Layout from "@/components/Layout/Layout"; +import MeauMobile from "@/components/MeauMobile/MeauMoible"; +import BasicSettings from "./steps/BasicSettings"; +import FriendRequestSettings from "./steps/FriendRequestSettings"; +import MessageSettings from "./steps/MessageSettings"; +import { + getScenarioTypes, + createPlan, + updatePlan, + getPlanDetail, +} from "../api"; +import style from "./page.module.scss"; + +// 步骤定义 +const steps = [ + { id: 1, title: "步骤一", subtitle: "基础设置" }, + { id: 2, title: "步骤二", subtitle: "好友申请设置" }, + { id: 3, title: "步骤三", subtitle: "消息设置" }, +]; + +// 类型定义 +interface FormData { + name: string; + scenario: number; + posters: any[]; + device: string[]; + remarkType: string; + greeting: string; + addInterval: number; + startTime: string; + endTime: string; + enabled: boolean; + sceneId: string | number; + remarkFormat: string; + addFriendInterval: number; +} + +const NewPlan: React.FC = () => { + const navigate = useNavigate(); + const [currentStep, setCurrentStep] = useState(1); + const [formData, setFormData] = useState({ + name: "", + scenario: 1, + posters: [], + device: [], + remarkType: "phone", + greeting: "你好,请通过", + addInterval: 1, + startTime: "09:00", + endTime: "18:00", + enabled: true, + sceneId: "", + remarkFormat: "", + addFriendInterval: 1, + }); + const [sceneList, setSceneList] = useState([]); + const [sceneLoading, setSceneLoading] = useState(true); + const { scenarioId, planId } = useParams<{ + scenarioId: string; + planId: string; + }>(); + const [isEdit, setIsEdit] = useState(false); + const [saving, setSaving] = useState(false); + + useEffect(() => { + loadData(); + }, []); + + const loadData = async () => { + setSceneLoading(true); + try { + // 获取场景类型 + const res = await getScenarioTypes(); + if (res?.data) { + setSceneList(res.data); + } + + if (planId) { + setIsEdit(true); + // 获取计划详情 + const detailRes = await getPlanDetail(planId); + if (detailRes.code === 200 && detailRes.data) { + const detail = detailRes.data; + setFormData((prev) => ({ + ...prev, + name: detail.name ?? "", + scenario: Number(detail.scenario) || 1, + posters: detail.posters ?? [], + device: detail.device ?? [], + remarkType: detail.remarkType ?? "phone", + greeting: detail.greeting ?? "", + addInterval: detail.addInterval ?? 1, + startTime: detail.startTime ?? "09:00", + endTime: detail.endTime ?? "18:00", + enabled: detail.enabled ?? true, + sceneId: Number(detail.scenario) || 1, + remarkFormat: detail.remarkFormat ?? "", + addFriendInterval: detail.addFriendInterval ?? 1, + })); + } + } else if (scenarioId) { + setFormData((prev) => ({ + ...prev, + scenario: Number(scenarioId) || 1, + })); + } + } catch (error) { + Toast.show({ + content: "加载数据失败", + position: "top", + }); + } finally { + setSceneLoading(false); + } + }; + + // 更新表单数据 + const onChange = (data: any) => { + setFormData((prev) => ({ ...prev, ...data })); + }; + + // 处理保存 + const handleSave = async () => { + if (!formData.name.trim()) { + Toast.show({ + content: "请输入计划名称", + position: "top", + }); + return; + } + + setSaving(true); + try { + let result; + if (isEdit && planId) { + // 编辑 + const editData = { + ...formData, + id: Number(planId), + planId: Number(planId), + }; + result = await updatePlan(planId, editData); + } else { + // 新建 + result = await createPlan(formData); + } + + if (result.code === 200) { + Toast.show({ + content: isEdit ? "计划已更新" : "获客计划已创建", + position: "top", + }); + const sceneItem = sceneList.find((v) => formData.scenario === v.id); + navigate( + `/scenarios/list/${formData.sceneId}/${sceneItem?.name || ""}` + ); + } else { + Toast.show({ + content: result.msg || "操作失败", + position: "top", + }); + } + } catch (error) { + Toast.show({ + content: isEdit ? "更新计划失败,请重试" : "创建计划失败,请重试", + position: "top", + }); + } finally { + setSaving(false); + } + }; + + // 下一步 + const handleNext = () => { + if (currentStep === steps.length) { + handleSave(); + } else { + setCurrentStep((prev) => prev + 1); + } + }; + + // 上一步 + const handlePrev = () => { + setCurrentStep((prev) => Math.max(prev - 1, 1)); + }; + + // 渲染当前步骤内容 + const renderStepContent = () => { + switch (currentStep) { + case 1: + return ( + + ); + case 2: + return ( + + ); + case 3: + return ( + + ); + default: + return null; + } + }; + + if (sceneLoading) { + return ( + +
+ {isEdit ? "编辑计划" : "新建计划"} +
+ + } + footer={} + > +
+ +
加载数据中...
+
+
+ ); + } + + return ( + + {isEdit ? "编辑计划" : "新建计划"} +
+ } + right={ + + } + /> + } + footer={} + > +
+ {/* 步骤指示器 */} +
+ + {steps.map((step) => ( + + ))} + +
+ + {/* 步骤内容 */} +
{renderStepContent()}
+
+
+ ); +}; + +export default NewPlan; diff --git a/nkebao/src/pages/scenarios/new/steps/BasicSettings.module.scss b/nkebao/src/pages/scenarios/new/steps/BasicSettings.module.scss new file mode 100644 index 000000000..5720527de --- /dev/null +++ b/nkebao/src/pages/scenarios/new/steps/BasicSettings.module.scss @@ -0,0 +1,63 @@ +.basic-settings { + padding: 16px 0; +} + +.form-card { + margin-bottom: 20px; + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.form-item { + margin-bottom: 20px; + + &:last-child { + margin-bottom: 0; + } + + .adm-form-item-label { + font-size: 14px; + font-weight: 500; + color: #333; + margin-bottom: 8px; + } + + .adm-input { + border-radius: 8px; + } + + .adm-selector { + border-radius: 8px; + } +} + +.time-input { + width: 120px; + border-radius: 8px; +} + +.loading { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 40vh; + gap: 16px; +} + +.loading-text { + color: #666; + font-size: 14px; +} + +.actions { + padding: 20px 0; +} + +.next-btn { + width: 100%; + height: 48px; + border-radius: 24px; + font-size: 16px; + font-weight: 500; +} \ No newline at end of file diff --git a/nkebao/src/pages/scenarios/new/steps/BasicSettings.tsx b/nkebao/src/pages/scenarios/new/steps/BasicSettings.tsx new file mode 100644 index 000000000..7b4343fde --- /dev/null +++ b/nkebao/src/pages/scenarios/new/steps/BasicSettings.tsx @@ -0,0 +1,210 @@ +import React, { useState, useEffect } from "react"; +import { + Form, + Input, + Selector, + Button, + SpinLoading, + Toast, + Card, + Space, +} from "antd-mobile"; +import { getDevices, getPosters } from "../../api"; +import style from "./BasicSettings.module.scss"; + +interface BasicSettingsProps { + formData: any; + onChange: (data: any) => void; + onNext: () => void; + sceneList: any[]; + sceneLoading: boolean; +} + +const BasicSettings: React.FC = ({ + formData, + onChange, + onNext, + sceneList, + sceneLoading, +}) => { + const [devices, setDevices] = useState([]); + const [posters, setPosters] = useState([]); + const [loading, setLoading] = useState(false); + + useEffect(() => { + loadData(); + }, []); + + const loadData = async () => { + setLoading(true); + try { + // 获取设备列表 + const devicesRes = await getDevices(); + if (devicesRes?.data) { + setDevices(devicesRes.data); + } + + // 获取海报列表 + const postersRes = await getPosters(); + if (postersRes?.data) { + setPosters(postersRes.data); + } + } catch (error) { + Toast.show({ + content: "加载数据失败", + position: "top", + }); + } finally { + setLoading(false); + } + }; + + const handleNext = () => { + if (!formData.name.trim()) { + Toast.show({ + content: "请输入计划名称", + position: "top", + }); + return; + } + + if (!formData.scenario) { + Toast.show({ + content: "请选择场景类型", + position: "top", + }); + return; + } + + if (formData.device.length === 0) { + Toast.show({ + content: "请选择设备", + position: "top", + }); + return; + } + + onNext(); + }; + + if (loading || sceneLoading) { + return ( +
+ +
加载数据中...
+
+ ); + } + + return ( +
+ +
+ {/* 计划名称 */} + + onChange({ name: value })} + clearable + /> + + + {/* 场景类型 */} + + ({ + label: scene.name, + value: scene.id, + }))} + value={[formData.scenario]} + onChange={(value) => { + const selectedScene = sceneList.find( + (scene) => scene.id === value[0] + ); + onChange({ + scenario: value[0], + sceneId: value[0], + name: selectedScene?.name || "", + }); + }} + /> + + + {/* 选择设备 */} + + ({ + label: device.name, + value: device.id, + }))} + value={formData.device} + onChange={(value) => onChange({ device: value })} + multiple + /> + + + {/* 选择海报 */} + + ({ + label: poster.name, + value: poster.id, + }))} + value={formData.posters} + onChange={(value) => onChange({ posters: value })} + multiple + /> + + + {/* 工作时间 */} + + + onChange({ startTime: value })} + className={style["time-input"]} + /> + + onChange({ endTime: value })} + className={style["time-input"]} + /> + + + + {/* 添加间隔 */} + + + onChange({ addInterval: Number(value) || 1 }) + } + min={1} + max={60} + /> + +
+
+ + {/* 操作按钮 */} +
+ +
+
+ ); +}; + +export default BasicSettings; diff --git a/nkebao/src/pages/scenarios/new/steps/FriendRequestSettings.module.scss b/nkebao/src/pages/scenarios/new/steps/FriendRequestSettings.module.scss new file mode 100644 index 000000000..67047ef14 --- /dev/null +++ b/nkebao/src/pages/scenarios/new/steps/FriendRequestSettings.module.scss @@ -0,0 +1,56 @@ +.friend-request-settings { + padding: 16px 0; +} + +.form-card { + margin-bottom: 20px; + border-radius: 12px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.form-item { + margin-bottom: 20px; + + &:last-child { + margin-bottom: 0; + } + + .adm-form-item-label { + font-size: 14px; + font-weight: 500; + color: #333; + margin-bottom: 8px; + } + + .adm-input { + border-radius: 8px; + } + + .adm-selector { + border-radius: 8px; + } + + .adm-text-area { + border-radius: 8px; + } +} + +.actions { + padding: 20px 0; +} + +.prev-btn { + flex: 1; + height: 48px; + border-radius: 24px; + font-size: 16px; + font-weight: 500; +} + +.next-btn { + flex: 1; + height: 48px; + border-radius: 24px; + font-size: 16px; + font-weight: 500; +} \ No newline at end of file diff --git a/nkebao/src/pages/scenarios/new/steps/FriendRequestSettings.tsx b/nkebao/src/pages/scenarios/new/steps/FriendRequestSettings.tsx new file mode 100644 index 000000000..fcb0bb63b --- /dev/null +++ b/nkebao/src/pages/scenarios/new/steps/FriendRequestSettings.tsx @@ -0,0 +1,113 @@ +import React from "react"; +import { + Form, + Input, + Selector, + Button, + Card, + Space, + TextArea, +} from "antd-mobile"; +import style from "./FriendRequestSettings.module.scss"; + +interface FriendRequestSettingsProps { + formData: any; + onChange: (data: any) => void; + onNext: () => void; + onPrev: () => void; +} + +const FriendRequestSettings: React.FC = ({ + formData, + onChange, + onNext, + onPrev, +}) => { + const remarkTypeOptions = [ + { label: "手机号", value: "phone" }, + { label: "微信号", value: "wechat" }, + { label: "QQ号", value: "qq" }, + { label: "自定义", value: "custom" }, + ]; + + const handleNext = () => { + if (!formData.greeting.trim()) { + // 可以添加验证逻辑 + } + onNext(); + }; + + return ( +
+ +
+ {/* 备注类型 */} + + onChange({ remarkType: value[0] })} + /> + + + {/* 备注格式 */} + {formData.remarkType === "custom" && ( + + onChange({ remarkFormat: value })} + clearable + /> + + )} + + {/* 打招呼消息 */} + +