# 关于我们模块 + 工厂重置 — 收尾实施计划 ## 摘要 本计划承接上一会话已完成的工作,完成剩余的前端改造与新增页面、编译验证、服务重启。整体目标: 1. 前台"关于我们"页面去掉"联系我们"模块,并改为从后端动态加载内容(已基本完成) 2. 后台新增"关于我们"管理模块:控制前台是否显示该页 + 全部内容**内联所见即所得**编辑 3. 在系统设置页新增"危险操作"卡片,提供恢复出厂设置入口(清库 + 清 COS) ## 当前状态分析 ### 已完成(上一会话) **后端(编译通过,0 错误):** * [FileStorageService.cs](file:///d:/TraeProject/MilliSim4/backend/MilliSim.Api/Services/Infrastructure/FileStorageService.cs) — 新增 `ListAllObjectsAsync()` 与 `DeleteObjectsAsync(IEnumerable)`,支持 COS 分页列出 + 批量删除(每批 1000),Local 模式回退到 Directory.GetFiles * [Dtos.cs](file:///d:/TraeProject/MilliSim4/backend/MilliSim.Api/Common/Dtos/Dtos.cs) — `PublicSettingsDto` 加 `ShowAbout`;新增 `AboutStatDto`/`AboutMissionCardDto`/`AboutTimelineItemDto`/`AboutContentDto`/`AboutSettingsDto`/`FactoryResetRequest`/`FactoryResetResultDto` * [SystemService.cs](file:///d:/TraeProject/MilliSim4/backend/MilliSim.Api/Services/SystemService.cs) — `GetPublicSettingsAsync` 加 `ShowAbout`;新增 `GetAboutSettingsAsync`、`UpdateAboutSettingsAsync(AboutSettingsDto)`、`FactoryResetAsync(FactoryResetRequest)` * [ISystemService.cs](file:///d:/TraeProject/MilliSim4/backend/MilliSim.Api/Services/ISystemService.cs) — 新增 3 个方法签名 * [SystemController.cs](file:///d:/TraeProject/MilliSim4/backend/MilliSim.Api/Controllers/SystemController.cs) — 新增 `GET /api/system/about`(AllowAnonymous)、`PUT /api/system/about`(Admin)、`POST /api/system/factory-reset`(Admin) * [DbSeeder.cs](file:///d:/TraeProject/MilliSim4/backend/MilliSim.Api/Data/DbSeeder.cs) — allSettings 新增 `ShowAbout=true` 与 `AboutContent=JSON(默认内容)`;工厂重置后重新种子可恢复默认数据 **前端(部分完成):** * [types/index.ts](file:///d:/TraeProject/MilliSim4/frontend/src/types/index.ts) — `PublicSettings` 加 `showAbout`;新增 `AboutStat`/`AboutMissionCard`/`AboutTimelineItem`/`AboutContent`/`AboutSettings`/`FactoryResetResult` 接口 * [api/index.ts](file:///d:/TraeProject/MilliSim4/frontend/src/api/index.ts) — systemApi 加 `getAbout()`、`updateAbout(data)`、`factoryReset(confirmText)` * [stores/system.ts](file:///d:/TraeProject/MilliSim4/frontend/src/stores/system.ts) — 加 `aboutContent` ref、`aboutLoaded` ref、`loadAbout()` 方法 * [About.vue](file:///d:/TraeProject/MilliSim4/frontend/src/views/frontend/About.vue) — 已重写,去掉"联系我们"模块,所有内容从 `systemStore.aboutContent` 加载,骨架屏 loading ### 待完成 * **B 收尾**:[router/index.ts](file:///d:/TraeProject/MilliSim4/frontend/src/router/index.ts) 与 [FrontendLayout.vue](file:///d:/TraeProject/MilliSim4/frontend/src/layouts/FrontendLayout.vue) 改造未完成 * **C**:[AboutSettings.vue](file:///d:/TraeProject/MilliSim4/frontend/src/views/admin/AboutSettings.vue) 与配套 CSS 文件尚未创建 * **D**:[Settings.vue](file:///d:/TraeProject/MilliSim4/frontend/src/views/admin/Settings.vue) 危险操作卡片未添加 * **E**:编译验证 + 重启前后端 ## 关键决策(用户已确认) 1. **数据库重置范围**:保留种子数据(DbSeeder.SeedAsync 重新补回分类/平台/标签/示例项目/轮播图/合作伙伴;admin 账户保留,密码 admin123) 2. **编辑交互方式**:内联所见即所得(非表单式),编辑样式与前台 About 页一致,每个内容可直接编辑 ## 提议变更 ### 阶段 B 收尾 — 路由 + 前台导航 #### 文件 1:[frontend/src/router/index.ts](file:///d:/TraeProject/MilliSim4/frontend/src/router/index.ts) **变更 1(L28)**:About 路由 meta 增加 `requiresAbout: true`: ```ts { path: 'about', name: 'About', component: () => import('@/views/frontend/About.vue'), meta: { title: '关于我们', requiresAbout: true } }, ``` **变更 2(L84 后,notifications 路由前)**:admin children 增加"关于我们"管理路由: ```ts { path: 'system/about', name: 'AdminAboutSettings', component: () => import('@/views/admin/AboutSettings.vue'), meta: { title: '关于我们', admin: true } }, ``` **变更 3(L103-139 beforeEach 守卫)**:守卫改为 async,并在权限校验前加 `showAbout` 检查。完整守卫: ```ts router.beforeEach(async (to, _from, next) => { document.title = `${to.meta.title || ''} - 毫秒数仿` const userStore = useUserStore() const systemStore = useSystemStore() // 确保 systemStore 已加载(用于 showAbout 判断) await systemStore.ensureLoaded() // 校验关于我们页面是否对外显示 if (to.meta.requiresAbout && !systemStore.settings.showAbout) { next('/') return } // 校验登录状态 if (to.meta.auth && !userStore.isLoggedIn) { next(to.path.startsWith('/admin') ? '/admin/login' : '/login') return } // ... 其余 staff/admin/pm/sales 校验保持不变 next() }) ``` > 注意:原守卫是同步函数,需改为 `async`,因为 `ensureLoaded()` 返回 Promise。系统已在 main.ts 启动时调 `systemStore.load()`,此处 `ensureLoaded` 通常是 no-op,不会引入明显延迟。 #### 文件 2:[frontend/src/layouts/FrontendLayout.vue](file:///d:/TraeProject/MilliSim4/frontend/src/layouts/FrontendLayout.vue) **变更 1(L192-198 menuItems)**:改为 computed,让"关于我们"项根据 `systemStore.settings.showAbout` 过滤: ```ts import { ref, computed, onMounted, onUnmounted } from 'vue' // ... const allMenuItems = [ { path: '/', label: '首页', icon: 'home' }, { path: '/projects', label: '项目中心', icon: 'grid' }, { path: '/tech', label: '技术能力', icon: 'zap' }, { path: '/about', label: '关于我们', icon: 'building' }, { path: '/contact', label: '联系咨询', icon: 'message' }, ] const menuItems = computed(() => systemStore.settings.showAbout ? allMenuItems : allMenuItems.filter(i => i.path !== '/about') ) ``` **变更 2(L132-138 footer 关于我们 列)**:footer 中"公司介绍"链接加 `v-if="systemStore.settings.showAbout"`: ```html
  • 公司介绍
  • ``` ### 阶段 C — 后台内联编辑页 AboutSettings.vue #### 文件 3:[frontend/src/views/admin/AboutSettings.vue](file:///d:/TraeProject/MilliSim4/frontend/src/views/admin/AboutSettings.vue)(新建) **目标**:内联所见即所得编辑器,编辑样式尽量与前台 About 一致,每个文本内容可直接点击编辑。 **结构**: * 顶部页头卡片(与其他 admin 页一致),含"显示开关"和"保存"按钮 * 内容区按 Hero / 公司简介 / 使命愿景 / 发展历程分块,每块都是可编辑卡片 * 列表型数据(introParagraphs / stats / missionCards / timeline)支持增删和上下移动 * 使用 `el-input`(无边框 + autosize)实现内联编辑,hover/focus 时显示虚线边框提示可编辑 **关键交互**: * `showAbout` 开关:el-switch,change 时立即调 `updateAbout` 保存(不依赖主保存按钮) * 主"保存"按钮:保存全部内容到 `AboutContent` * 列表增删:每项右侧悬浮显示"+ 添加 / 删除 / 上移 / 下移"图标按钮 * 保存后调 `systemStore.loadAbout()` 刷新 store,使前台立即生效 **核心代码骨架**: ```vue ``` #### 文件 4:[frontend/src/views/admin/AboutSettings.css](file:///d:/TraeProject/MilliSim4/frontend/src/views/admin/AboutSettings.css)(新建) **目标**:内联编辑样式 — 让 `el-input` 看起来像前台 About 页的文字,但 hover/focus 时显示虚线边框提示可编辑。 **关键样式**: ```css /* 内联输入:默认无边框、透明背景,模拟前台文本外观 */ .inline-input :deep(.el-input__wrapper), .inline-input :deep(.el-textarea__inner) { background: transparent; box-shadow: none !important; border: 1px dashed transparent; padding: 4px 8px; border-radius: 4px; transition: border-color .2s; } .inline-input :deep(.el-input__wrapper):hover, .inline-input :deep(.el-textarea__inner):hover { border-color: var(--color-primary-light); } .inline-input :deep(.el-input__wrapper).is-focus, .inline-input :deep(.el-textarea__inner):focus { border-color: var(--color-primary); background: rgba(22, 119, 255, 0.04); } /* Hero 区编辑样式(白字) */ .hero-preview { background: var(--gradient-hero); padding: 40px 24px; border-radius: 12px; text-align: center; } .hero-preview .hero-tag-input :deep(.el-input__inner), .hero-preview .hero-title-input :deep(.el-input__inner), .hero-preview .hero-desc-input :deep(.el-textarea__inner) { color: #fff; text-align: center; } .hero-title-input :deep(.el-input__inner) { font-size: 32px; font-weight: 700; } /* 列表项容器 */ .list-item { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 8px; } .list-item > .el-input, .list-item > .el-textarea { flex: 1; } .list-actions { display: flex; gap: 4px; padding-top: 4px; } .list-actions button { background: transparent; border: none; padding: 4px; cursor: pointer; color: var(--color-text-secondary); border-radius: 4px; } .list-actions button:hover:not(:disabled) { background: var(--color-bg-elevated); color: var(--color-primary); } .list-actions button:disabled { opacity: .3; cursor: not-allowed; } ``` ### 阶段 D — Settings.vue 危险操作卡片 #### 文件 5:[frontend/src/views/admin/Settings.vue](file:///d:/TraeProject/MilliSim4/frontend/src/views/admin/Settings.vue) 在"上传限制"卡片之后追加"危险操作"卡片: ```html
    危险操作
    恢复出厂设置
    清空所有业务数据(仅保留 admin 账户)并删除 COS 全部资源。系统会先自动创建数据备份,再重新写入种子数据(分类/平台/标签/示例项目/轮播图/合作伙伴)。
    恢复出厂设置

    请输入 RESET 确认:

    ``` **script 增加**: ```ts import { systemApi } from '@/api' // ... const resetDialogVisible = ref(false) const resetConfirmText = ref('') const resetting = ref(false) function openFactoryReset() { resetConfirmText.value = '' resetDialogVisible.value = true } async function confirmFactoryReset() { if (resetting.value) return resetting.value = true try { const res = await systemApi.factoryReset(resetConfirmText.value) if (res.data) { ElMessage.success(`重置成功:已清空 ${res.data.deletedTables} 张表,删除 ${res.data.deletedCosObjects} 个 COS 对象`) resetDialogVisible.value = false // 刷新 systemStore(admin 头像被清空、Logo 等可能变化) await systemStore.load() await systemStore.loadAbout() } } catch { // 错误已由拦截器处理 } finally { resetting.value = false } } ``` **关于 FactoryResetResultDto 字段**:后端 DTO 已定义 `DeletedTables`、`DeletedCosObjects`、`BackupId` 等字段(与前端 `FactoryResetResult` 接口对应)。 #### 文件 6:[frontend/src/views/admin/Settings.css](file:///d:/TraeProject/MilliSim4/frontend/src/views/admin/Settings.css)(追加样式) 追加: ```css .danger-card { border: 1px solid #E5484D; } .danger-title { color: #E5484D; } .danger-item { display: flex; justify-content: space-between; align-items: center; gap: 16px; } .danger-info { flex: 1; } .danger-name { font-weight: 600; color: var(--color-text-primary); margin-bottom: 4px; } .danger-desc { font-size: 13px; color: var(--color-text-secondary); line-height: 1.6; } ``` ### 阶段 E — 编译验证 + 重启 1. **前端 HMR 验证**:保存所有改动后,Vite dev server(5173 端口)应自动 HMR;检查控制台无 TypeScript 错误 2. **后端编译验证**: * `taskkill /F /PID ` 杀掉运行中的 MilliSim.Api.exe(如 PID 13788/22680,但需 `tasklist | findstr MilliSim` 确认当前 PID) * `cd d:\TraeProject\MilliSim4\backend\MilliSim.Api && dotnet build`(预期 0 错误,仅有 2 个预存警告) * 后台启动 `dotnet run --project d:\TraeProject\MilliSim4\backend\MilliSim.Api\MilliSim.Api.csproj`(run\_in\_background=true) 3. **功能验证清单**(不强制要求用户操作,但建议): * 访问 `/admin/system/about` → 加载默认内容 → 修改 Hero 标题 → 保存 → 访问前台 `/about` 看到新内容 * 切换"前台显示"开关为关闭 → 前台导航不再显示"关于我们" → 直接访问 `/about` 重定向到首页 * 在 Settings 页点"恢复出厂设置" → 输入 RESET → 确认 → 看到"重置成功"提示 → 数据库业务表清空,admin 仍可登录 ## 假设与决策 1. **AboutSettings.vue 列表交互**:使用数组 `splice`/`push` 直接操作(reactive 深层响应),不引入额外的拖拽库;上下移动用图标按钮 2. **图标名输入**:missionCards 的 `icon` 字段直接用文本输入框(让管理员填 `rocket`/`globe`/`heart` 等 SvgIcon 名称),不引入图标选择器,保持简单 3. **保存策略**:`showAbout` 开关 change 即时保存(独立于内容);主"保存全部内容"按钮保存 `AboutContent` JSON。这样切换开关不会丢失未保存的内容编辑(因为 updateAbout 同时发送 showAbout + content) 4. **路由守卫 async 化**:原 `beforeEach` 是同步,改为 async 会让首次路由跳转等待一次 `systemStore.load()`(main.ts 已先调过,通常是 no-op) 5. **不修改 main.ts**:保持 main.ts 已有的 `systemStore.load()` 调用,不在启动时调 `loadAbout()`(About.vue 自己 onMounted 调即可,避免首次启动多一次请求) 6. **危险操作不放在独立路由**:直接在 Settings.vue 末尾加卡片,符合"系统设置集中"的产品语义;不新建 `DangerZone.vue` 页 7. **FactoryResetResult 字段命名**:后端 DTO 已使用 PascalCase(`DeletedTables`、`DeletedCosObjects`),前端 ApiResponse 拦截器已统一转 camelCase(`deletedTables`、`deletedCosObjects`),按前端命名规范使用 ## 验证步骤 实施完成后: 1. 前端 Vite 控制台无 TS 报错,`/admin/system/about` 路由可访问 2. 后端 `dotnet build` 输出 `Build succeeded, 0 Error` 3. 访问后台"系统设置"侧边栏看到"关于我们"子菜单 4. AboutSettings.vue 加载默认内容、修改保存、开关切换均生效 5. 前台 `/about` 显示修改后的内容;关闭开关后导航隐藏、直接访问重定向到首页 6. Settings.vue 危险操作卡片显示,输入 RESET 后调用工厂重置成功 ## 任务清单(执行顺序) * [ ] B1: 修改 router/index.ts(加 requiresAbout meta + admin 路由 + 守卫 showAbout 检查) * [ ] B2: 修改 FrontendLayout.vue(menuItems 改 computed + footer 链接加 v-if) * [ ] C1: 创建 AboutSettings.vue(内联所见即所得编辑器) * [ ] C2: 创建 AboutSettings.css(内联编辑样式) * [ ] D1: Settings.vue 追加危险操作卡片 + 工厂重置弹窗 + script 逻辑 * [ ] D2: Settings.css 追加 danger-card 样式 * [ ] E1: 杀掉后端进程 + dotnet build 验证 * [ ] E2: 重启后端 + 前端 HMR 验证