@chenhui996/gg-cli 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/template/zhiguan/.editorconfig +16 -0
- package/dist/template/zhiguan/.env +1 -0
- package/dist/template/zhiguan/.env.development +4 -0
- package/dist/template/zhiguan/.env.production +4 -0
- package/dist/template/zhiguan/.env.test +4 -0
- package/dist/template/zhiguan/.prettierignore +34 -0
- package/dist/template/zhiguan/.prettierrc +14 -0
- package/dist/template/zhiguan/README.md +183 -0
- package/dist/template/zhiguan/docs/Git/345/274/200/345/217/221/350/247/204/350/214/203.md +105 -0
- package/dist/template/zhiguan/docs/React/345/274/200/345/217/221/350/247/204/350/214/203.md +81 -0
- package/dist/template/zhiguan/docs/TypeScript/345/274/200/345/217/221/350/247/204/350/214/203.md +119 -0
- package/dist/template/zhiguan/docs//345/211/215/347/253/257/346/227/245/345/277/227/344/270/216/347/233/221/346/216/247/345/237/213/347/202/271/350/247/204/350/214/203.md +136 -0
- package/dist/template/zhiguan/docs//345/215/225/345/205/203/346/265/213/350/257/225/350/247/204/350/214/203.md +131 -0
- package/dist/template/zhiguan/docs//351/241/271/347/233/256/347/233/256/345/275/225/347/273/223/346/236/204/350/247/204/350/214/203.md +93 -0
- package/dist/template/zhiguan/eslint.config.js +27 -0
- package/dist/template/zhiguan/index.html +13 -0
- package/dist/template/zhiguan/package.json +60 -0
- package/dist/template/zhiguan/public/favicon.svg +1 -0
- package/dist/template/zhiguan/public/icons.svg +24 -0
- package/dist/template/zhiguan/src/api/user.ts +21 -0
- package/dist/template/zhiguan/src/assets/Frame 20.png +0 -0
- package/dist/template/zhiguan/src/assets/react.svg +1 -0
- package/dist/template/zhiguan/src/components/Chart/index.tsx +22 -0
- package/dist/template/zhiguan/src/components/ErrorBoundary/index.tsx +82 -0
- package/dist/template/zhiguan/src/layouts/BasicLayout.tsx +174 -0
- package/dist/template/zhiguan/src/main.tsx +38 -0
- package/dist/template/zhiguan/src/pages/404.test.tsx +20 -0
- package/dist/template/zhiguan/src/pages/404.tsx +32 -0
- package/dist/template/zhiguan/src/pages/about/index.tsx +8 -0
- package/dist/template/zhiguan/src/pages/calendar/index.tsx +8 -0
- package/dist/template/zhiguan/src/pages/dashboard/index.tsx +72 -0
- package/dist/template/zhiguan/src/pages/home/index.less +59 -0
- package/dist/template/zhiguan/src/pages/home/index.tsx +217 -0
- package/dist/template/zhiguan/src/pages/settings/index.tsx +8 -0
- package/dist/template/zhiguan/src/pages/workspace/index.tsx +8 -0
- package/dist/template/zhiguan/src/router/index.tsx +81 -0
- package/dist/template/zhiguan/src/setupTests.ts +1 -0
- package/dist/template/zhiguan/src/store/useCounterStore.ts +24 -0
- package/dist/template/zhiguan/src/style.less +3 -0
- package/dist/template/zhiguan/src/utils/request/index.ts +108 -0
- package/dist/template/zhiguan/src/vite-env.d.ts +12 -0
- package/dist/template/zhiguan/tsconfig.app.json +34 -0
- package/dist/template/zhiguan/tsconfig.json +7 -0
- package/dist/template/zhiguan/tsconfig.node.json +26 -0
- package/dist/template/zhiguan/vite.config.ts +77 -0
- package/package.json +1 -1
- package/dist/template/operations-tem/package-lock.json +0 -6413
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# 前端日志与监控埋点规范
|
|
2
|
+
|
|
3
|
+
传统的 `console.log` 只能输出在用户的浏览器控制台,一旦代码发布到生产环境,前端就变成了“黑盒”。为了解决“生产环境瞎子”问题,提升前端应用的可观测性(Observability),特制定本前端日志与监控埋点规范。
|
|
4
|
+
|
|
5
|
+
前端日志不写入本地文件,而是通过 HTTP/Beacon 上报至监控中心(如 Sentry, 阿里云 ARMS, 或自建日志服务)。
|
|
6
|
+
|
|
7
|
+
## 1. 前端日志的分类
|
|
8
|
+
|
|
9
|
+
前端日志主要分为三大类,每类的采集方式和关注点均不同:
|
|
10
|
+
|
|
11
|
+
1. **异常日志 (Error Logs)**:对应后端的 ERROR 级别。记录 JS 报错、接口请求失败、静态资源加载失败。
|
|
12
|
+
2. **行为埋点日志 (Behavior Logs)**:对应后端的 INFO/TRACE 级别。记录用户的核心操作路径,用于业务分析和故障复现(用户点击流)。
|
|
13
|
+
3. **性能日志 (Performance Logs)**:记录页面白屏时间、首屏渲染时间、接口耗时等 Web Vitals 指标。
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 2. 核心字段规范 (Context)
|
|
18
|
+
|
|
19
|
+
无论上报哪种日志,前端必须在日志的 Context(上下文)中携带以下基础信息,以便和后端链路打通,并精确定位用户环境:
|
|
20
|
+
|
|
21
|
+
| 字段名 | 类型 | 说明 | 获取方式示例 |
|
|
22
|
+
| --- | --- | --- | --- |
|
|
23
|
+
| `traceId` | String | **全链路追踪 ID**,前后端必须一致 | 从后端的 API 响应 Header 或 HTML Meta 中获取 |
|
|
24
|
+
| `userId` | String | 当前登录操作员 ID | 从 Redux/Zustand Store 或 Token 解析 |
|
|
25
|
+
| `env` | String | 当前环境标识 (dev/test/prod) | `import.meta.env.MODE` |
|
|
26
|
+
| `app_name` | String | 前端应用名称 | 如:`operations-admin-web` |
|
|
27
|
+
| `page_url` | String | 发生事件/报错的当前页面 URL | `window.location.href` |
|
|
28
|
+
| `user_agent` | String | 用户的浏览器、操作系统信息 | `navigator.userAgent` |
|
|
29
|
+
| `network` | String | 用户的网络环境 (4G/WiFi等) | `navigator.connection.effectiveType` (可选) |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 3. 异常日志规范 (Error Logging)
|
|
34
|
+
|
|
35
|
+
### 3.1 自动捕获
|
|
36
|
+
在项目入口文件(如 `main.tsx`)中,必须全局挂载异常捕获钩子,将错误上报至监控平台:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// 捕获常规 JS 运行时错误
|
|
40
|
+
window.addEventListener('error', (event) => {
|
|
41
|
+
logService.error('JS_RUNTIME_ERROR', {
|
|
42
|
+
message: event.message,
|
|
43
|
+
filename: event.filename,
|
|
44
|
+
lineno: event.lineno,
|
|
45
|
+
colno: event.colno,
|
|
46
|
+
stack: event.error?.stack,
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// 捕获 Promise 未处理的 rejection (如 async/await 报错未 catch)
|
|
51
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
52
|
+
logService.error('PROMISE_UNHANDLED_REJECTION', {
|
|
53
|
+
reason: event.reason,
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 3.2 React 组件树异常捕获
|
|
59
|
+
必须在根路由或核心组件外层包裹 `ErrorBoundary`(错误边界组件),防止单一组件报错导致整个页面白屏崩溃,并在钩子中上报。
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
import React, { ErrorInfo } from 'react';
|
|
63
|
+
|
|
64
|
+
class GlobalErrorBoundary extends React.Component<any, { hasError: boolean }> {
|
|
65
|
+
state = { hasError: false };
|
|
66
|
+
|
|
67
|
+
static getDerivedStateFromError(error: Error) {
|
|
68
|
+
return { hasError: true };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
|
72
|
+
// 关键:在这里将 React 渲染报错上报
|
|
73
|
+
logService.error('REACT_RENDER_ERROR', {
|
|
74
|
+
error: error.toString(),
|
|
75
|
+
componentStack: errorInfo.componentStack,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
render() {
|
|
80
|
+
if (this.state.hasError) {
|
|
81
|
+
return <h1>抱歉,页面加载出错,请刷新重试。</h1>;
|
|
82
|
+
}
|
|
83
|
+
return this.props.children;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 3.3 HTTP 接口异常上报
|
|
89
|
+
在 Axios 的全局响应拦截器 (`response interceptor`) 中,如果 HTTP 状态码非 200,或业务状态码报错,必须上报。
|
|
90
|
+
**关键规范**:接口报错上报时,必须携带请求的 URL、参数、响应体以及**最关键的 `traceId`**。
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 4. 行为埋点规范 (Behavior Logging)
|
|
95
|
+
|
|
96
|
+
业务埋点不仅为了产品分析转化率,更是为了在用户报障时,能还原出**“用户点了什么导致了报错” (Breadcrumbs)**。
|
|
97
|
+
|
|
98
|
+
### 4.1 埋点触发时机
|
|
99
|
+
- **PV 埋点 (Page View)**:路由切换完成时上报。
|
|
100
|
+
- **点击埋点 (Click Event)**:用户点击核心按钮(如:提交订单、确认支付、删除数据)时上报。
|
|
101
|
+
- **曝光埋点 (Impression)**:核心区块(如:广告位、重要弹窗)进入可视区域时上报。
|
|
102
|
+
|
|
103
|
+
### 4.2 埋点命名与数据规范
|
|
104
|
+
- **事件 ID (EventId)**:统一采用 `动作_对象` 格式的大写蛇形命名。如 `CLICK_SUBMIT_ORDER_BTN`, `VIEW_LOGIN_PAGE`。
|
|
105
|
+
- **严禁携带大字段**:不要把整个接口返回的 List 或 Base64 图片塞入埋点上报中。
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// ✅ 推荐的做法:只记录关键标识
|
|
109
|
+
logService.track('CLICK_DELETE_USER', {
|
|
110
|
+
targetUserId: '8848',
|
|
111
|
+
sourcePage: '/users/list'
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## 5. 数据脱敏规范 (Data Masking)
|
|
118
|
+
|
|
119
|
+
**与后端规范完全一致,前端在拼接日志和埋点 Context 时,严禁将明文敏感数据上报至公网监控服务中!**
|
|
120
|
+
|
|
121
|
+
如果用户的输入或接口返回包含了以下字段,在调用 `logService.send()` 之前必须进行掩码处理:
|
|
122
|
+
- 手机号 (`phone`, `mobile`) -> 保留前3后4,中间替换为 `****`
|
|
123
|
+
- 身份证号 (`idCard`) -> 保留前6后4,中间替换为 `********`
|
|
124
|
+
- 密码 (`password`, `pwd`) -> 直接替换为常量 `[REDACTED]` 或根本不上报
|
|
125
|
+
- 银行卡号 (`bankCard`) -> 仅保留后4位
|
|
126
|
+
|
|
127
|
+
*(前端脱敏通常采用在封装的底层日志 SDK 拦截器中,遍历对象 Key,通过正则统一替换)*
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 6. 日志发送策略
|
|
132
|
+
|
|
133
|
+
由于前端日志通过公网发送,不能影响用户的正常业务网络请求。
|
|
134
|
+
|
|
135
|
+
- **优先使用 `navigator.sendBeacon`**:这是一种浏览器原生提供的异步发送数据的方法,即使页面被卸载或关闭,数据也能可靠发出,且不会阻塞主线程。
|
|
136
|
+
- **节流与批量上报**:对于高频触发的事件(如滚动、性能指标收集),必须在本地收集放入队列,定时(如每 5 秒)或定量(如满 10 条)批量合并发送一次 HTTP 请求。
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# 前端单元测试开发规范
|
|
2
|
+
|
|
3
|
+
单元测试是保障前端代码质量、支持安全重构的重要手段。本文档规定了团队内前端项目的测试工具选型、目录结构以及测试代码的编写规范。
|
|
4
|
+
|
|
5
|
+
## 1. 测试工具选型
|
|
6
|
+
|
|
7
|
+
在基于 Vite 的前端项目中,我们**全面弃用 Jest,转而使用 Vitest 作为核心测试框架**。
|
|
8
|
+
|
|
9
|
+
### 为什么选择 Vitest?
|
|
10
|
+
1. **与 Vite 完美融合**:Vitest 直接复用项目的 `vite.config.ts`(包括 alias、插件等),实现了零配置开箱即用,避免了在 Jest 中配置复杂的 Babel/TypeScript 转换器。
|
|
11
|
+
2. **极速运行**:得益于 Vite 的原生 ESM 支持和底层的 esbuild/oxc,Vitest 的启动和运行速度远超 Jest。
|
|
12
|
+
3. **API 兼容**:Vitest 提供了与 Jest 几乎 100% 兼容的 API(如 `describe`, `it`, `expect`),现有的 Jest 经验可以无缝迁移。
|
|
13
|
+
|
|
14
|
+
### 核心测试栈:
|
|
15
|
+
- **测试框架**: [Vitest](https://vitest.dev/) (替代 Jest)
|
|
16
|
+
- **组件测试库**: [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) (用于渲染和交互测试)
|
|
17
|
+
- **环境模拟**: `jsdom` 或 `happy-dom` (模拟浏览器 DOM 环境)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 2. 测试文件命名与目录规范
|
|
22
|
+
|
|
23
|
+
### 2.1 命名规范
|
|
24
|
+
测试文件必须以 `.test.ts` 或 `.test.tsx` 结尾(如果包含 JSX 代码必须用 `.tsx`)。
|
|
25
|
+
|
|
26
|
+
### 2.2 目录存放方式
|
|
27
|
+
推荐**就近存放**原则。测试文件应与其被测试的代码文件放在同一目录下,方便查找和维护。
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
src/
|
|
31
|
+
├── utils/
|
|
32
|
+
│ ├── math.ts
|
|
33
|
+
│ └── math.test.ts # 工具函数的测试文件
|
|
34
|
+
├── components/
|
|
35
|
+
│ └── Button/
|
|
36
|
+
│ ├── index.tsx
|
|
37
|
+
│ ├── index.less
|
|
38
|
+
│ └── index.test.tsx # UI 组件的测试文件
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 3. 编写规范
|
|
44
|
+
|
|
45
|
+
### 3.1 测试结构 (Arrange - Act - Assert)
|
|
46
|
+
所有的测试用例都必须遵循著名的 **AAA 模式**:
|
|
47
|
+
- **Arrange (准备)**:初始化测试数据、渲染组件、设置 Mock 函数。
|
|
48
|
+
- **Act (执行)**:触发用户交互(点击、输入)或调用函数。
|
|
49
|
+
- **Assert (断言)**:验证期望的结果(如:函数返回值、DOM 元素的变化)。
|
|
50
|
+
|
|
51
|
+
### 3.2 纯函数测试规范
|
|
52
|
+
纯函数(如工具类、数据转换逻辑)是最容易且最应该被测试的代码。
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
// utils/math.test.ts
|
|
56
|
+
import { describe, it, expect } from 'vitest';
|
|
57
|
+
import { add } from './math';
|
|
58
|
+
|
|
59
|
+
describe('math 工具函数', () => {
|
|
60
|
+
it('应当正确计算两个正数的和', () => {
|
|
61
|
+
// Arrange
|
|
62
|
+
const a = 1;
|
|
63
|
+
const b = 2;
|
|
64
|
+
|
|
65
|
+
// Act
|
|
66
|
+
const result = add(a, b);
|
|
67
|
+
|
|
68
|
+
// Assert
|
|
69
|
+
expect(result).toBe(3);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 3.3 React 组件测试规范 (React Testing Library)
|
|
75
|
+
组件测试的原则是:**测试组件的行为,而不是其内部实现**。像真实用户一样去交互和断言,不要去测试组件的内部 state。
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
// components/Button/index.test.tsx
|
|
79
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
80
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
81
|
+
import { Button } from './index';
|
|
82
|
+
|
|
83
|
+
describe('Button 组件', () => {
|
|
84
|
+
it('正常渲染标签文字', () => {
|
|
85
|
+
// Arrange
|
|
86
|
+
render(<Button label="提交" onClick={() => {}} />);
|
|
87
|
+
|
|
88
|
+
// Act & Assert
|
|
89
|
+
// 像用户一样通过可见文本寻找元素
|
|
90
|
+
expect(screen.getByText('提交')).toBeInTheDocument();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('点击时应触发 onClick 回调', () => {
|
|
94
|
+
// Arrange
|
|
95
|
+
// 使用 vi.fn() 创建一个 Mock 函数记录调用
|
|
96
|
+
const handleClick = vi.fn();
|
|
97
|
+
render(<Button label="提交" onClick={handleClick} />);
|
|
98
|
+
|
|
99
|
+
// Act
|
|
100
|
+
fireEvent.click(screen.getByText('提交'));
|
|
101
|
+
|
|
102
|
+
// Assert
|
|
103
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 4. Mock 规范
|
|
111
|
+
|
|
112
|
+
在前端测试中,经常需要 Mock 网络请求或复杂的第三方依赖。
|
|
113
|
+
|
|
114
|
+
- **Mock 函数**:使用 `vi.fn()` 替代 `jest.fn()`。
|
|
115
|
+
- **Mock 模块**:使用 `vi.mock('module-name')` 替代 `jest.mock()`。
|
|
116
|
+
- **Mock 定时器**:使用 `vi.useFakeTimers()`。
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 5. 测试覆盖率 (Coverage) 要求
|
|
121
|
+
|
|
122
|
+
虽然不强求 100% 的覆盖率(会带来过高的维护成本),但应遵守以下底线:
|
|
123
|
+
|
|
124
|
+
1. **核心业务逻辑 (utils/store/hooks)**:包含大量计算、格式化、状态流转的纯函数,要求语句覆盖率达到 **80%** 以上。
|
|
125
|
+
2. **公共 UI 组件 (components)**:要求进行基本的渲染测试和交互测试。
|
|
126
|
+
3. **页面级组件 (pages)**:不强制要求编写详细的单元测试,推荐使用 E2E 测试工具(如 Cypress/Playwright)进行核心链路覆盖。
|
|
127
|
+
|
|
128
|
+
运行覆盖率命令:
|
|
129
|
+
```bash
|
|
130
|
+
npm run test:coverage
|
|
131
|
+
```
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# 项目目录结构规范
|
|
2
|
+
|
|
3
|
+
本文档定义了前端工程的标准目录结构,旨在保持项目架构的清晰一致,降低新成员的理解成本,并为大型应用的扩展奠定基础。
|
|
4
|
+
|
|
5
|
+
## 1. 根目录结构
|
|
6
|
+
|
|
7
|
+
项目根目录存放工程化配置、环境变量文件以及源代码目录。
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
├── .env.development # 本地开发环境变量配置
|
|
11
|
+
├── .env.test # 测试环境变量配置
|
|
12
|
+
├── .env.production # 生产环境变量配置
|
|
13
|
+
├── .eslintrc.cjs # ESLint 逻辑检查配置
|
|
14
|
+
├── .prettierrc # Prettier 代码格式化配置
|
|
15
|
+
├── .editorconfig # 跨编辑器基础配置规范
|
|
16
|
+
├── package.json # 项目依赖及 npm scripts
|
|
17
|
+
├── tsconfig.json # TypeScript 基础配置
|
|
18
|
+
├── tsconfig.app.json # TypeScript 针对 src 目录的配置
|
|
19
|
+
├── tsconfig.node.json # TypeScript 针对 vite.config.ts 等 Node 环境的配置
|
|
20
|
+
├── vite.config.ts # Vite 构建工具配置
|
|
21
|
+
├── index.html # 应用挂载的 HTML 模板
|
|
22
|
+
└── src/ # 源代码主目录
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 2. `src/` 源代码主目录结构
|
|
26
|
+
|
|
27
|
+
所有业务代码、静态资源和配置文件都应放置在 `src` 目录下,并严格按照功能模块进行划分。
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
src/
|
|
31
|
+
├── api/ # API 接口请求定义
|
|
32
|
+
├── assets/ # 静态资源文件
|
|
33
|
+
├── components/ # 业务无关的全局公共组件
|
|
34
|
+
├── hooks/ # 全局自定义 React Hooks
|
|
35
|
+
├── layouts/ # 页面布局组件
|
|
36
|
+
├── pages/ # 页面级组件 (路由视图)
|
|
37
|
+
├── router/ # 路由配置表
|
|
38
|
+
├── store/ # 全局状态管理 (Zustand)
|
|
39
|
+
├── types/ # 全局 TypeScript 类型定义
|
|
40
|
+
├── utils/ # 全局公共工具函数
|
|
41
|
+
├── App.tsx # 根组件,承载路由等全局 Context
|
|
42
|
+
├── main.tsx # 应用入口文件,负责渲染 React 树
|
|
43
|
+
├── index.css # 全局样式文件
|
|
44
|
+
└── vite-env.d.ts # Vite 环境变量类型声明
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 3. 详细目录说明与规范
|
|
50
|
+
|
|
51
|
+
### 3.1 `api/` (接口定义层)
|
|
52
|
+
- 存放所有的接口请求函数。
|
|
53
|
+
- 按照业务模块或后端微服务划分文件,例如 `user.ts`、`order.ts`。
|
|
54
|
+
- **规范**:接口函数的命名应见名知意(如 `getUserList`, `createOrder`),并且必须定义入参和返回值的 TypeScript 类型。
|
|
55
|
+
|
|
56
|
+
### 3.2 `assets/` (静态资源)
|
|
57
|
+
- 存放不需要经过 Webpack/Vite 编译的静态资源,如图片 (`images/`)、SVG 图标 (`icons/`)、字体文件等。
|
|
58
|
+
- **规范**:较大的静态资源建议上传至 CDN,小图片或图标可以直接放在此处。
|
|
59
|
+
|
|
60
|
+
### 3.3 `components/` (公共组件库)
|
|
61
|
+
- 存放**跨页面复用**的、**业务无关**的 UI 组件或业务组件(如封装的通用图表组件 `Chart/`、通用的文件上传组件)。
|
|
62
|
+
- **规范**:每个组件应拥有自己的文件夹,内部包含 `index.tsx` (组件逻辑) 和 `index.less` (组件样式)。
|
|
63
|
+
|
|
64
|
+
### 3.4 `hooks/` (自定义 Hooks)
|
|
65
|
+
- 存放全局复用的自定义 React Hooks,例如 `useDebounce.ts`、`useWindowSize.ts`。
|
|
66
|
+
- **规范**:文件命名必须以 `use` 开头,采用 **camelCase (小驼峰)**。
|
|
67
|
+
|
|
68
|
+
### 3.5 `layouts/` (布局组件)
|
|
69
|
+
- 存放页面的整体布局组件,例如包含侧边栏、顶栏和底部版权信息的 `BasicLayout.tsx`,或者登录页专用的 `BlankLayout.tsx`。
|
|
70
|
+
- 布局组件通常被用作路由配置中的父级路由组件 (`<Outlet />`)。
|
|
71
|
+
|
|
72
|
+
### 3.6 `pages/` (页面级视图)
|
|
73
|
+
- 存放与路由直接对应的页面组件。
|
|
74
|
+
- 按照功能模块划分文件夹,如 `home/`、`dashboard/`、`about/`。
|
|
75
|
+
- **规范**:
|
|
76
|
+
- 如果一个页面较复杂,可以在其文件夹下建立私有的 `components/` 目录存放仅该页面使用的子组件。
|
|
77
|
+
- 入口文件统一命名为 `index.tsx`,配合 `index.less` 使用。
|
|
78
|
+
|
|
79
|
+
### 3.7 `router/` (路由配置)
|
|
80
|
+
- 存放应用的路由配置表。
|
|
81
|
+
- **规范**:基于 React Router v7 的 Data API 模式配置路由树。按业务模块拆分路由配置,避免单一文件过长。
|
|
82
|
+
|
|
83
|
+
### 3.8 `store/` (全局状态管理)
|
|
84
|
+
- 存放基于 Zustand 的全局状态。
|
|
85
|
+
- **规范**:按业务领域拆分 Store 文件,如 `useUserStore.ts`、`useAppStore.ts`。不要将本应属于组件内部的局部状态放入全局 Store。
|
|
86
|
+
|
|
87
|
+
### 3.9 `types/` (全局类型声明)
|
|
88
|
+
- 存放全局公用的 TypeScript 接口 (`interface`) 和类型别名 (`type`)。
|
|
89
|
+
- **规范**:例如定义后端的通用分页返回结构、全局共享的枚举值等。命名约定为 `*.d.ts` 或 `*.ts`。
|
|
90
|
+
|
|
91
|
+
### 3.10 `utils/` (工具函数)
|
|
92
|
+
- 存放纯 JavaScript/TypeScript 的工具函数,如日期格式化 (`date.ts`)、正则校验 (`regex.ts`)、本地存储封装 (`storage.ts`)。
|
|
93
|
+
- **重点**:`request/` 目录用于存放 Axios 的二次封装,包括拦截器配置、错误统一处理等。
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import js from "@eslint/js";
|
|
2
|
+
import globals from "globals";
|
|
3
|
+
import reactHooks from "eslint-plugin-react-hooks";
|
|
4
|
+
import reactRefresh from "eslint-plugin-react-refresh";
|
|
5
|
+
import tseslint from "typescript-eslint";
|
|
6
|
+
import { defineConfig, globalIgnores } from "eslint/config";
|
|
7
|
+
|
|
8
|
+
export default defineConfig([
|
|
9
|
+
globalIgnores(["dist"]),
|
|
10
|
+
{
|
|
11
|
+
files: ["**/*.{ts,tsx}"],
|
|
12
|
+
extends: [
|
|
13
|
+
js.configs.recommended,
|
|
14
|
+
tseslint.configs.recommended,
|
|
15
|
+
reactHooks.configs.flat.recommended,
|
|
16
|
+
reactRefresh.configs.vite,
|
|
17
|
+
],
|
|
18
|
+
languageOptions: {
|
|
19
|
+
ecmaVersion: 2020,
|
|
20
|
+
globals: globals.browser,
|
|
21
|
+
},
|
|
22
|
+
rules: {
|
|
23
|
+
"@typescript-eslint/no-explicit-any": "off",
|
|
24
|
+
"react-refresh/only-export-components": "off",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
]);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>react-temp1</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zhiguan-tem",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite --mode development",
|
|
8
|
+
"build:test": "tsc -b && vite build --mode test",
|
|
9
|
+
"build:prod": "tsc -b && vite build --mode production",
|
|
10
|
+
"build": "npm run build:prod",
|
|
11
|
+
"test": "vitest",
|
|
12
|
+
"test:ui": "vitest --ui",
|
|
13
|
+
"test:coverage": "vitest run --coverage",
|
|
14
|
+
"analyze": "cross-env ANALYZE=true npm run build:prod",
|
|
15
|
+
"lint": "eslint .",
|
|
16
|
+
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,less,css,json,md}\"",
|
|
17
|
+
"preview": "vite preview"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@ant-design/icons": "^6.1.0",
|
|
21
|
+
"antd": "^6.3.3",
|
|
22
|
+
"axios": "^1.13.6",
|
|
23
|
+
"echarts": "^6.0.0",
|
|
24
|
+
"echarts-for-react": "^3.0.6",
|
|
25
|
+
"react": "^19.2.4",
|
|
26
|
+
"react-dom": "^19.2.4",
|
|
27
|
+
"react-router-dom": "^7.13.1",
|
|
28
|
+
"tslib": "^2.8.1",
|
|
29
|
+
"zustand": "^5.0.12"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@babel/core": "^7.29.0",
|
|
33
|
+
"@eslint/js": "^9.39.4",
|
|
34
|
+
"@rolldown/plugin-babel": "^0.2.0",
|
|
35
|
+
"@testing-library/dom": "^10.4.1",
|
|
36
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
37
|
+
"@testing-library/react": "^16.3.2",
|
|
38
|
+
"@types/babel__core": "^7.20.5",
|
|
39
|
+
"@types/node": "^24.12.0",
|
|
40
|
+
"@types/react": "^19.2.14",
|
|
41
|
+
"@types/react-dom": "^19.2.3",
|
|
42
|
+
"@vitejs/plugin-react": "^6.0.0",
|
|
43
|
+
"@vitest/coverage-v8": "^4.1.4",
|
|
44
|
+
"babel-plugin-react-compiler": "^1.0.0",
|
|
45
|
+
"cross-env": "^10.1.0",
|
|
46
|
+
"eslint": "^9.39.4",
|
|
47
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
48
|
+
"eslint-plugin-react-refresh": "^0.5.2",
|
|
49
|
+
"globals": "^17.4.0",
|
|
50
|
+
"jsdom": "^29.0.2",
|
|
51
|
+
"less": "^4.6.4",
|
|
52
|
+
"prettier": "^3.8.1",
|
|
53
|
+
"rollup-plugin-visualizer": "^7.0.1",
|
|
54
|
+
"terser": "^5.46.1",
|
|
55
|
+
"typescript": "~5.9.3",
|
|
56
|
+
"typescript-eslint": "^8.56.1",
|
|
57
|
+
"vite": "^8.0.0",
|
|
58
|
+
"vitest": "^4.1.4"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="46" fill="none" viewBox="0 0 48 46"><path fill="#863bff" d="M25.946 44.938c-.664.845-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.287c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.497 0-3.578-1.842-3.578H1.237c-.92 0-1.456-1.04-.92-1.788L10.013.474c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.579 1.842 3.579h11.377c.943 0 1.473 1.088.89 1.83L25.947 44.94z" style="fill:#863bff;fill:color(display-p3 .5252 .23 1);fill-opacity:1"/><mask id="a" width="48" height="46" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M25.842 44.938c-.664.844-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.183c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.498 0-3.579-1.842-3.579H1.133c-.92 0-1.456-1.04-.92-1.787L9.91.473c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.578 1.842 3.578h11.377c.943 0 1.473 1.088.89 1.832L25.843 44.94z" style="fill:#000;fill-opacity:1"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#ede6ff" rx="5.508" ry="14.704" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -4.47 31.516)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#ede6ff" rx="10.399" ry="29.851" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -39.328 7.883)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#7e14ff" rx="5.508" ry="30.487" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -25.913 -14.639)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -32.644 -3.334)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -34.34 30.47)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#ede6ff" rx="14.072" ry="22.078" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="rotate(93.35 24.506 48.493)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx=".387" cy="8.972" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(39.51 .387 8.972)"/></g><g filter="url(#k)"><ellipse cx="47.523" cy="-6.092" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 47.523 -6.092)"/></g><g filter="url(#l)"><ellipse cx="41.412" cy="6.333" fill="#47bfff" rx="5.971" ry="9.665" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 41.412 6.333)"/></g><g filter="url(#m)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#n)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#o)"><ellipse cx="35.651" cy="29.907" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 35.651 29.907)"/></g><g filter="url(#p)"><ellipse cx="38.418" cy="32.4" fill="#47bfff" rx="5.971" ry="15.297" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 38.418 32.4)"/></g></g><defs><filter id="b" width="60.045" height="41.654" x="-19.77" y="16.149" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-54.613" y="-7.533" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="d" width="79.355" height="29.4" x="-49.64" y="2.03" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="e" width="79.579" height="29.4" x="-45.045" y="20.029" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="f" width="79.579" height="29.4" x="-43.513" y="21.178" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="g" width="74.749" height="58.852" x="15.756" y="-17.901" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="h" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="i" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="j" width="56.045" height="63.649" x="-27.636" y="-22.853" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="k" width="54.814" height="64.646" x="20.116" y="-38.415" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="l" width="33.541" height="35.313" x="24.641" y="-11.323" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="m" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="n" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="o" width="54.814" height="64.646" x="8.244" y="-2.416" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="p" width="39.409" height="43.623" x="18.713" y="10.588" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter></defs></svg>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<symbol id="bluesky-icon" viewBox="0 0 16 17">
|
|
3
|
+
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
|
|
4
|
+
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
|
|
5
|
+
</symbol>
|
|
6
|
+
<symbol id="discord-icon" viewBox="0 0 20 19">
|
|
7
|
+
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
|
|
8
|
+
</symbol>
|
|
9
|
+
<symbol id="documentation-icon" viewBox="0 0 21 20">
|
|
10
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
|
|
11
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
|
|
12
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
|
|
13
|
+
</symbol>
|
|
14
|
+
<symbol id="github-icon" viewBox="0 0 19 19">
|
|
15
|
+
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
|
|
16
|
+
</symbol>
|
|
17
|
+
<symbol id="social-icon" viewBox="0 0 20 20">
|
|
18
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
|
|
19
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
|
|
20
|
+
</symbol>
|
|
21
|
+
<symbol id="x-icon" viewBox="0 0 19 19">
|
|
22
|
+
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
|
|
23
|
+
</symbol>
|
|
24
|
+
</svg>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { request } from '../utils/request/index.js';
|
|
2
|
+
|
|
3
|
+
// 定义接口返回的数据类型
|
|
4
|
+
export interface UserInfo {
|
|
5
|
+
id: number;
|
|
6
|
+
name: string;
|
|
7
|
+
email: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// 示例 API 服务
|
|
11
|
+
export const userService = {
|
|
12
|
+
// 获取用户信息
|
|
13
|
+
getUserInfo: (userId: number) => {
|
|
14
|
+
return request.get<UserInfo>(`/users/${userId}`);
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
// 更新用户信息
|
|
18
|
+
updateUserInfo: (userId: number, data: Partial<UserInfo>) => {
|
|
19
|
+
return request.put<UserInfo>(`/users/${userId}`, data);
|
|
20
|
+
},
|
|
21
|
+
};
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import ReactECharts from 'echarts-for-react';
|
|
2
|
+
import type { EChartsOption } from 'echarts';
|
|
3
|
+
|
|
4
|
+
interface ChartProps {
|
|
5
|
+
option: EChartsOption;
|
|
6
|
+
style?: React.CSSProperties;
|
|
7
|
+
className?: string;
|
|
8
|
+
loading?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function Chart({ option, style, className, loading = false }: ChartProps) {
|
|
12
|
+
return (
|
|
13
|
+
<ReactECharts
|
|
14
|
+
option={option}
|
|
15
|
+
style={{ height: '100%', width: '100%', ...style }}
|
|
16
|
+
className={className}
|
|
17
|
+
showLoading={loading}
|
|
18
|
+
notMerge={true}
|
|
19
|
+
lazyUpdate={true}
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
}
|