前端代码结构
SurveyKing Pro 前端采用 Umi.js 4 + React 18 + TypeScript + Ant Design 5 + Formily 2 技术栈,是一个现代化的企业级前端应用架构。
🛠️ 技术栈概览
基于 package.json
的实际依赖:
- 核心框架: Umi.js 4.0.73 (React 全家桶)
- UI 组件库: Ant Design 5.23.1 + Ant Design Pro Components 2.6.8
- 状态管理: Formily Reactive (基于 @formily/reactive)
- 表单方案: Formily 2.2.27 (阿里巴巴表单解决方案)
- 类型支持: TypeScript 4+
- 样式方案: Less + CSS-in-JS (@emotion/css、@emotion/styled)
- 图表库: BizCharts 4.1.22 + ECharts 5.4.3
- 移动端: Ant Design Mobile 5.37.0
- 工具库: ahooks 3.7.4、lodash 4.17.21、dayjs 1.11.10
- 构建工具: Umi Max (内置 Webpack)
📁 项目目录结构
client/
├── public/ # 静态资源目录
│ ├── favicon.ico # 网站图标
│ ├── logo.svg # Logo 图片
│ ├── CNAME # 域名配置
│ ├── assets/ # 公共资源
│ └── icons/ # PWA 图标集合
├── src/ # 源代码目录
│ ├── components/ # 公共组件库 (44个组件)
│ │ ├── Answer/ # 答题相关组件
│ │ ├── AudioRecord/ # 音频录制组件
│ │ ├── Charts/ # 图表组件
│ │ ├── FormulaEditor/ # 公式编辑器
│ │ ├── QuillEditor/ # Quill富文本编辑器
│ │ ├── Quill2/ # Quill 2.0版本
│ │ ├── ReactQuill/ # React Quill组件
│ │ ├── CanvasDatagrid/ # 高性能表格组件
│ │ ├── FortuneSheet/ # 在线表格(类Excel)
│ │ ├── AuthComponent/ # 权限控制组件
│ │ ├── DynamicGrid/ # 动态网格布局
│ │ ├── SliderCaptcha/ # 滑块验证码
│ │ ├── QrScanner/ # 二维码扫描
│ │ ├── ReactViewer/ # 文件预览
│ │ ├── FormilyAntd/ # Formily Antd适配器
│ │ └── ... # 其他30+个组件
│ ├── pages/ # 页面组件
│ │ ├── Home/ # 首页
│ │ ├── Survey/ # 问卷相关页面
│ │ ├── exam/ # 考试相关页面
│ │ ├── Exercise/ # 练习页面
│ │ ├── project/ # 项目管理
│ │ ├── system/ # 系统管理
│ │ ├── user/ # 用户相关页面
│ │ ├── Template/ # 模板页面
│ │ ├── repo/ # 题库页面
│ │ ├── infra/ # 基础设施页面
│ │ ├── Access/ # 访问控制页面
│ │ ├── mobile/ # 移动端页面
│ │ │ ├── Account/ # 移动端账户
│ │ │ ├── Exercise/ # 移动端练习
│ │ │ └── Home/ # 移动端首页
│ │ ├── 401.tsx # 401错误页面
│ │ ├── 404.tsx # 404错误页面
│ │ └── Loading.tsx # 加载页面
│ ├── services/ # API 服务层
│ │ ├── auth/ # 认证服务
│ │ ├── survey/ # 问卷服务
│ │ ├── system/ # 系统服务
│ │ ├── exam/ # 考试服务
│ │ ├── project/ # 项目服务
│ │ ├── home/ # 首页服务
│ │ ├── repo/ # 题库服务
│ │ ├── infra/ # 基础设施服务
│ │ ├── demo/ # 演示服务
│ │ └── types.ts # API 通用类型
│ ├── store/ # Formily响应式状态管理
│ │ ├── index.ts # Store 配置
│ │ ├── UserStore.ts # 用户状态管理
│ │ ├── ProjectStore.ts # 项目状态管理
│ │ ├── AnswerStore.ts # 答题状态管理
│ │ ├── DictStore.ts # 字典数据管理
│ │ ├── HomeStore.ts # 首页状态管理
│ │ ├── SystemStore.ts # 系统状态管理
│ │ ├── TemplateStore.ts # 模板状态管理
│ │ └── FlowStore.ts # 流程状态管理
│ ├── shared/ # 共享工具库 (30+个文件)
│ │ ├── types.ts # 全局类型定义
│ │ ├── utils.ts # 工具函数集合
│ │ ├── api.ts # API 请求工具
│ │ ├── auth.ts # 认证工具
│ │ ├── useAxios.ts # HTTP 客户端 Hook
│ │ ├── tree.ts # 树形数据处理
│ │ ├── exam.ts # 考试相关工具
│ │ ├── download.ts # 下载工具
│ │ ├── jsencrypt.ts # 加密工具
│ │ ├── useIsMobile.ts # 移动端检测
│ │ ├── useDebouncedCallback.ts # 防抖回调
│ │ ├── useThrottleCallback.ts # 节流回调
│ │ └── ... # 其他工具 Hooks
│ ├── layouts/ # 布局组件
│ │ ├── WorkspaceLayout.tsx # 工作台布局
│ │ ├── BasicLayout.tsx # 基础布局
│ │ ├── BasicPcLayout.tsx # PC端布局
│ │ ├── BasicMobileLayout.tsx # 移动端布局
│ │ ├── AppLayout.tsx # 应用布局
│ │ ├── WorkspaceLayout.less # 工作台样式
│ │ └── mobile.less # 移动端样式
│ ├── types/ # TypeScript 类型定义
│ ├── utils/ # 工具函数
│ ├── constants/ # 常量定义
│ ├── locales/ # 国际化文件
│ ├── models/ # Umi Model 数据流
│ ├── assets/ # 本地资源
│ ├── app.tsx # 应用入口组件
│ ├── app.less # 全局样式
│ ├── access.ts # 权限配置
│ ├── loading.tsx # 全局加载组件
│ └── loading.less # 加载组件样式
├── .umirc.ts # Umi 开发配置
├── .umirc.prod.ts # Umi 生产配置
├── package.json # 项目配置
├── tsconfig.json # TypeScript 配置
└── patches/ # 第三方库补丁
├── core-js+3.28.0.patch
└── quill+1.3.7.patch
🧩 核心模块详解
1. 状态管理架构 (store/)
项目使用 Formily Reactive 进行状态管理,这是阿里巴巴开源的响应式状态管理方案:
// store/index.ts - 全局 Store 配置
import { createContext } from 'react';
import { HomeStore } from './HomeStore';
import { SystemStore } from './SystemStore';
import { TemplateStore } from './TemplateStore';
import type { ProjectStore } from './ProjectStore';
import DictStore from './DictStore';
export default class AppStore {
dictStore: DictStore;
homeStore: HomeStore;
systemStore: SystemStore;
publicTemplateStore: TemplateStore;
privateTemplateStore: TemplateStore;
projectStoreObj: Record<string, ProjectStore> = {};
constructor() {
this.dictStore = new DictStore();
this.homeStore = new HomeStore();
this.systemStore = new SystemStore();
this.publicTemplateStore = new TemplateStore(1);
this.privateTemplateStore = new TemplateStore(0);
}
}
export const AppContext = createContext<AppStore>({} as AppStore);
UserStore 实现示例
// store/UserStore.ts - 用户状态管理
import { action, define, observable } from '@formily/reactive';
export class UserStore {
system: SystemInfo = {
name: '卷王问卷',
id: '1',
description: '做更好的调查问卷系统',
locale: 'zh-CN',
// ...
};
userInfo?: UserInfoVO;
isMobile: boolean = false;
loading: boolean = false;
userOverview?: UserOverview;
users: UserInfo[] = [];
constructor() {
this.makeObservable();
this.isMobile = checkIsMobile();
this.getSystemInfo();
}
// 使用 Formily 的响应式系统
makeObservable() {
define(this, {
userInfo: observable,
system: observable,
loading: observable.ref,
userOverview: observable.ref,
users: observable.shallow,
user: observable.computed,
logout: action,
listUserByIds: action,
permStyle: action,
filterItemByPerm: action,
});
}
// 权限检查
hasPermission(...permission: string[]) {
if (
this.userInfo &&
permission.every((perm) =>
(this.userInfo?.permissions || []).includes(perm)
)
) {
return true;
}
return false;
}
// 根据权限过滤项目
filterItemByPerm<T extends { perm?: string }>(items: T[]): T[] {
return items.filter(
(x) => !x.perm || this.userInfo?.permissions.includes(x.perm)
);
}
}
2. API 服务层 (services/)
按业务模块组织,每个模块包含 index.ts
和 types.ts
:
// services/types.ts - 通用类型
export type PaginationProps = {
pageNo?: number;
current?: number;
pageSize?: number;
};
export type PaginationResult<T> = {
current: number;
pageSize: number;
data: T[];
total: number;
success: boolean;
};
// services/auth/index.ts - 认证服务
export const getPermissionApi = (): Promise<UserInfoVO> => {
return request('/system/auth/get-permission-info');
};
export const socialLoginApi = (
data: SocialLoginVO
): Promise<CommonResult<string>> => {
return request('/system/auth/social-login', {
method: 'POST',
data,
});
};
// services/survey/index.ts - 问卷服务
export const getSurveyApi = (id: string): Promise<Survey> => {
return request(`/survey/${id}`);
};
export const createSurveyApi = (data: CreateSurveyRequest): Promise<Survey> => {
return request('/survey/create', {
method: 'POST',
data,
});
};
3. 组件库架构 (components/)
项目拥有 44 个自定义业务组件:
核心组件分类
// 答题相关组件
components/Answer/ # 问卷答题核心组件
components/AudioRecord/ # 音频录制组件
// 编辑器组件
components/QuillEditor/ # Quill.js 富文本编辑器封装
components/Quill2/ # Quill 2.0 版本
components/ReactQuill/ # React Quill 组件
components/FormulaEditor/ # 数学公式编辑器
// 数据展示组件
components/Charts/ # 图表组件集合
components/CanvasDatagrid/ # 高性能Canvas表格
components/FortuneSheet/ # 在线表格(类似Excel)
components/ReactViewer/ # 文件预览组件
// 交互组件
components/DynamicGrid/ # 动态网格布局
components/SliderCaptcha/ # 滑块验证码
components/QrScanner/ # 二维码扫描
components/ImageView/ # 图片查看器
// 权限相关组件
components/AuthComponent/ # 权限控制组件
components/PermissionWrapper/ # 权限包装器
components/AuthProvider/ # 认证提供者
// Formily 相关组件
components/FormilyAntd/ # Formily + Ant Design 适配
components/DecoratorField/ # 装饰器字段
components/ProFormFields/ # Pro 表单字段
// 业务组件
components/ExamQuestionPickModal/ # 考试题目选择弹窗
components/LogicConditionGroup/ # 逻辑条件组
components/Poster/ # 海报生成
4. 页面架构 (pages/)
工作台页面
// 使用 WorkspaceLayout 布局
pages/Home/ # 首页
pages/project/ # 项目管理
├── List/ # 项目列表
│ ├── Collaboration.tsx # 协作项目
│ ├── Favorites.tsx # 收藏项目
│ ├── Todo.tsx # 待办项目
│ └── Recycle.tsx # 回收站
pages/Exercise/ # 练习页面
pages/Template/ # 模板页面
pages/repo/ # 题库管理
├── Repo/ # 题库列表
├── Template/ # 题库模板
└── Book/ # 错题本
问卷管理页面
// 使用 BasicLayout 布局
pages/Survey/ # 问卷相关页面
├── index.tsx # 问卷答题页面
├── Appraise/ # 360度评价
├── Exercise/ # 练习模式
├── ExamResult/ # 考试结果
├── PublicQuery/ # 公开查询
├── Lottery/ # 抽奖页面
├── Print/ # 打印报表
└── Dict/ # 字典管理
移动端页面
// 移动端专门页面
pages/mobile/
├── Account/ # 移动端账户页面
├── Exercise/ # 移动端练习
│ └── Exercise/ # 练习详情页面
└── Home/ # 移动端 首页
5. 共享工具库 (shared/)
包含 30+个工具文件,核心文件有:
// shared/types.ts - 全局类型定义 (420行)
export type UserType = {
name: string;
userId: string;
avatar?: string;
deptId?: string;
deptName?: string;
email?: string;
profile?: string;
authorityList?: string[];
};
export type SystemInfo = {
id: string;
name: string;
description: string;
avatar?: string;
locale: 'zh-CN' | 'en-US';
mapType: 'amap' | 'tmap';
mapKey?: string;
registerInfo: {
roles?: string[];
registerEnabled: boolean;
strongPasswordEnabled?: boolean;
};
};
// shared/useAxios.ts - HTTP 客户端
export const useAxios = () => {
const request = axios.create({
baseURL: '',
timeout: 30000,
});
// 请求拦截器
request.interceptors.request.use((config) => {
const token = getToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
return request;
};
🎨 布局系统 (layouts/)
1. 工作台布局 (WorkspaceLayout.tsx)
用于主要的工作页面,提供侧边栏导航:
// layouts/WorkspaceLayout.tsx
export default function WorkspaceLayout() {
return (
<Layout className="workspace-layout">
<Sider>{/* 侧边栏菜单 */}</Sider>
<Layout>
<Header>{/* 头部导航 */}</Header>
<Content>{/* 页面内容 */}</Content>
</Layout>
</Layout>
);
}
2. 基础布局 (BasicLayout.tsx)
用于问卷管理等功能页面:
// layouts/BasicLayout.tsx
export default function BasicLayout() {
return (
<ProLayout
// ProLayout 配置
>
{children}
</ProLayout>
);
}
3. 移动端布局 (BasicMobileLayout.tsx)
专门为移动端优化的布局:
// layouts/BasicMobileLayout.tsx
export default function BasicMobileLayout() {
return <div className="mobile-layout">{/* 移动端布局 */}</div>;
}
🔧 Umi 配置
1. 主配置文件 (.umirc.ts)
export default defineConfig({
antd: {},
access: {},
hash: true,
model: {},
initialState: {},
layout: false,
locale: {
default: 'zh-CN',
baseSeparator: '-',
},
// 代理配置
proxy: {
'/': {
target: 'http://localhost:48080/',
changeOrigin: true,
},
'/captcha/': {
target: 'http://localhost:48080',
changeOrigin: true,
},
},
// 路由配置
routes: [
// 公开访问路由
{ path: '/s/:id', component: './Survey' }, // 答卷页面
{ path: '/t/:id', component: './Survey/Exercise' }, // 练习页面
{ path: '/e/:id', component: './Survey/Appraise' }, // 360评价
// 工作台布局
{
path: '/',
component: '@/layouts/WorkspaceLayout',
routes: [
{ path: '/home', component: './Home' },
{ path: '/mine', component: './project/List' },
{ path: '/exercise', component: './Exercise' },
{ path: '/repo', component: './repo/Repo' },
{ path: '/template', component: './Template' },
// 移动端页面
{
path: '/m',
routes: [
{ path: '/m/account', component: './mobile/Account' },
{
path: '/m/exercise/:repoId',
component: './mobile/Exercise/Exercise',
},
],
},
],
},
// 问卷管理布局
{
path: '/survey',
component: '@/layouts/BasicLayout',
routes: [
{ path: '/survey/dict', component: './Survey/Dict' },
// ... 更多问卷管理路由
],
},
],
});
📱 移动端支持
1. 移动端页面结构
// 移动端页面实际结构
pages/mobile/
├── index.tsx # 移动端入口 (3行)
├── Account/ # 账户管理
├── Exercise/ # 练习功能
│ └── Exercise/ # 练习详情页面
└── Home/ # 移动端首页
2. 移动端检测
// shared/useIsMobile.ts - 实际的移动端检测逻辑
export const useIsMobile = () => {
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {
const checkMobile = () => {
// ...
};
checkMobile();
window.addEventListener('resize', checkMobile);
return () => window.removeEventListener('resize', checkMobile);
}, []);
return isMobile;
};
3. 移动端布局
// layouts/BasicMobileLayout.tsx
export default function BasicMobileLayout({
children,
}: {
children: React.ReactNode;
}) {
return <div className="basic-mobile-layout">{children}</div>;
}
🔒 权限系统
1. 权限配置 (access.ts)
// access.ts - 权限配置
export default function access(initialState: InitialState) {
const { currentUser } = initialState;
return {
canAdmin: currentUser?.role === 'admin',
canProject: currentUser?.permissions?.includes('project:read'),
canSurvey: currentUser?.permissions?.includes('survey:read'),
};
}
2. 权限组件
// components/AuthComponent/ - 权限控制组件
// components/PermissionWrapper/ - 权限包装器
// 在 UserStore 中的权限检查
hasPermission(...permission: string[]) {
if (
this.userInfo &&
permission.every((perm) =>
(this.userInfo?.permissions || []).includes(perm),
)
) {
return true;
}
return false;
}
🚀 开发体验
1. 开发命令
# 开发服务器
npm run dev
# 生产构建
npm run build
# 代码格式化
npm run format
# 依赖分析
npm run analyze
2. 代码规范
项目使用 Prettier + ESLint 确保代码质量:
// .eslintrc.js
module.exports = {
extends: ['@umijs/lint/dist/config/eslint'],
};
// .prettierrc
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"proseWrap": "never",
"endOfLine": "lf"
}
总结: SurveyKing Pro 前端采用了企业级的现代化架构,特别是使用了 Formily 响应式状态管理和丰富的业务组件库,为问卷、考试、练习等复杂业务场景提供了强大的技术支撑。
下一步: 查看 后端代码结构 了解服务端架构设计。