library-picturebook-activity/.cursor/rules/frontend-architecture.mdc
aid 418aa57ea8 Day4: 超管端设计优化 + UGC绘本创作社区P0实现
一、超管端设计优化
- 文档管理SOP体系建立,docs目录重组
- 统一用户管理:跨租户全局视角,合并用户管理+公众用户
- 活动监管全模块重构:全部活动(统计卡片+阶段筛选+SuperDetail详情页)、报名数据/作品数据/评审进度(两层合一扁平列表)、成果发布(去Tab+统计+隐藏写操作)
- 菜单精简:移除评委管理/评审规则/通知管理
- Bug修复:租户编辑丢失隐藏菜单、pageSize限制、主色统一

二、UGC绘本创作社区P0
- 数据库:10张新表(user_works/user_work_pages/work_tags等)
- 子女账号独立化:Child升级为独立User,家长切换+独立登录
- 用户作品库:CRUD+发布审核,8个API
- AI创作流程:提交→生成→保存到作品库,4个API
- 作品广场:首页改造为推荐流,标签+搜索+排序
- 内容审核(超管端):作品审核+作品管理+标签管理
- 活动联动:WorkSelector作品选择器
- 布局改造:底部5Tab(发现/创作/活动/作品库/我的)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 22:20:25 +08:00

349 lines
7.1 KiB
Plaintext

---
description: Vue 3 前端架构规范和组件开发
globs:
- "frontend/**/*.vue"
- "frontend/**/*.ts"
alwaysApply: false
---
# 前端架构规范
## 组件结构
### 目录组织
- 页面组件放在 `views/` 目录下,按模块组织
- 公共组件放在 `components/` 目录下
- 组件命名使用 PascalCase
### 组件语法
使用 Vue 3 Composition API 的 `<script setup lang="ts">` 语法:
```vue
<script setup lang="ts">
import { ref, computed, onMounted } from "vue";
import { message } from "ant-design-vue";
import { getUsers, type User } from "@/api/users";
const loading = ref(false);
const users = ref<User[]>([]);
const activeUsers = computed(() =>
users.value.filter((u) => u.validState === 1)
);
onMounted(async () => {
await fetchUsers();
});
const fetchUsers = async () => {
try {
loading.value = true;
users.value = await getUsers();
} catch (error) {
message.error("获取用户列表失败");
} finally {
loading.value = false;
}
};
</script>
<template>
<div class="p-4">
<a-spin :spinning="loading">
<a-table :dataSource="activeUsers" />
</a-spin>
</div>
</template>
```
## API 调用规范
### 目录结构
所有 API 调用放在 `api/` 目录下,按模块组织:
```
api/
├── users.ts
├── roles.ts
├── contests.ts
└── auth.ts
```
### API 函数命名
- `getXxx` - 获取数据
- `createXxx` - 创建数据
- `updateXxx` - 更新数据
- `deleteXxx` - 删除数据
### 示例
```typescript
// api/users.ts
import request from "@/utils/request";
export interface User {
id: number;
username: string;
nickname: string;
email?: string;
validState: number;
}
export interface CreateUserDto {
username: string;
password: string;
nickname: string;
email?: string;
roleIds?: number[];
}
export const getUsers = (params?: { skip?: number; take?: number }) => {
return request.get<User[]>("/users", { params });
};
export const createUser = (data: CreateUserDto) => {
return request.post<User>("/users", data);
};
export const updateUser = (id: number, data: Partial<CreateUserDto>) => {
return request.put<User>(`/users/${id}`, data);
};
export const deleteUser = (id: number) => {
return request.delete(`/users/${id}`);
};
```
## 状态管理 (Pinia)
### Store 规范
- Store 文件放在 `stores/` 目录下
- 使用 `defineStore()` 定义 store
- Store 命名使用 camelCase + Store 后缀
### 示例
```typescript
// stores/auth.ts
import { defineStore } from "pinia";
import { ref, computed } from "vue";
import { login, getUserInfo, type LoginDto, type User } from "@/api/auth";
export const useAuthStore = defineStore("auth", () => {
const token = ref<string | null>(localStorage.getItem("token"));
const user = ref<User | null>(null);
const menus = ref<any[]>([]);
const isAuthenticated = computed(() => !!token.value && !!user.value);
const loginAction = async (loginDto: LoginDto) => {
const {
accessToken,
user: userInfo,
menus: userMenus,
} = await login(loginDto);
token.value = accessToken;
user.value = userInfo;
menus.value = userMenus;
localStorage.setItem("token", accessToken);
};
const logout = () => {
token.value = null;
user.value = null;
menus.value = [];
localStorage.removeItem("token");
};
return {
token,
user,
menus,
isAuthenticated,
loginAction,
logout,
};
});
```
## 路由管理
### 路由规范
- 路由配置在 `router/index.ts`
- 支持动态路由(基于菜单权限)
- 路由路径包含租户编码:`/:tenantCode/xxx`
- 路由 meta 包含权限信息
### 示例
```typescript
{
path: '/:tenantCode/users',
name: 'Users',
component: () => import('@/views/users/Index.vue'),
meta: {
requiresAuth: true,
permissions: ['user:read'],
roles: ['admin'],
},
}
```
## 表单验证
### 使用 VeeValidate + Zod
```vue
<script setup lang="ts">
import { useForm } from "vee-validate";
import { toTypedSchema } from "@vee-validate/zod";
import * as z from "zod";
const schema = z.object({
username: z.string().min(3, "用户名至少3个字符"),
password: z.string().min(6, "密码至少6个字符"),
email: z.string().email("邮箱格式不正确").optional(),
});
const { defineField, handleSubmit, errors } = useForm({
validationSchema: toTypedSchema(schema),
});
const [username] = defineField("username");
const [password] = defineField("password");
const [email] = defineField("email");
const onSubmit = handleSubmit(async (values) => {
try {
await createUser(values);
message.success("创建成功");
} catch (error) {
message.error("创建失败");
}
});
</script>
<template>
<a-form @submit.prevent="onSubmit">
<a-form-item
:help="errors.username"
:validateStatus="errors.username ? 'error' : ''"
>
<a-input v-model:value="username" placeholder="用户名" />
</a-form-item>
<a-form-item
:help="errors.password"
:validateStatus="errors.password ? 'error' : ''"
>
<a-input-password v-model:value="password" placeholder="密码" />
</a-form-item>
<a-button type="primary" html-type="submit">提交</a-button>
</a-form>
</template>
```
## UI 组件规范
### Ant Design Vue
- 使用 Ant Design Vue 组件库
- 遵循 Ant Design 设计规范
### 样式
- 使用 Tailwind CSS 工具类
- 复杂样式使用 SCSS
- 响应式设计,移动端优先
### 状态管理
组件必须有 loading 和 error 状态:
```vue
<template>
<div class="p-4">
<a-spin :spinning="loading">
<a-alert
v-if="error"
type="error"
:message="error"
closable
@close="error = null"
/>
<div v-else>
<!-- 内容 -->
</div>
</a-spin>
</div>
</template>
```
## TypeScript 类型定义
### 类型文件组织
- TypeScript 类型定义放在 `types/` 目录下
- 接口类型使用 `interface`
- 数据模型使用 `type`
- 导出类型供其他模块使用
### 示例
```typescript
// types/user.ts
export interface User {
id: number;
username: string;
nickname: string;
email?: string;
tenantId: number;
validState: number;
createTime: string;
modifyTime: string;
}
export type CreateUserParams = Omit<
User,
"id" | "tenantId" | "createTime" | "modifyTime"
>;
export type UserRole = {
userId: number;
roleId: number;
role: Role;
};
```
## 性能优化
### 路由懒加载
```typescript
const routes = [
{
path: "/users",
component: () => import("@/views/users/Index.vue"),
},
];
```
### 组件按需加载
```typescript
import { defineAsyncComponent } from "vue";
const AsyncComponent = defineAsyncComponent(
() => import("@/components/HeavyComponent.vue")
);
```
### 避免不必要的重新渲染
使用 `computed`、`watchEffect` 和 `memo` 优化性能。