diff --git a/README.md b/README.md new file mode 100644 index 00000000..74fce66c --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# CTMS 项目快速上手 + +## 账号 +- 管理员:`admin / admin123`(默认存在,用于创建项目、成员、FAQ 等) +- 示例 PM:`pm_faq / pm123456`(可作为项目 PM 登录) +- 示例 CRA:`cra_faq / cra123456`(只读成员) + +> 若找不到示例用户,可用管理员在 `/api/v1/users` 创建,或在 UI 的登录页使用管理员登录后到“项目列表”添加成员。 + +## 访问方式 +- 前端:`http://localhost`(Nginx 反代到 Vite 开发容器) +- 后端 API:同域 `/api/v1/*`(已在前端代理) + +## 常用流程 +1. 用 `admin / admin123` 登录前端。 +2. 在“项目列表”进入一个项目或新建项目后进入。 +3. Dashboard 查看进度、待办;左侧导航进入“里程碑”“任务”等页面。 +4. 退出登录或切换项目可通过顶部右侧下拉。 + +## 注意 +- 令牌与当前项目上下文保存在浏览器 LocalStorage 中,清除后需重新登录/选择项目。 +- 若后端重启,确认数据库容器仍健康,前端会弹出错误提示。 diff --git a/frontend/src/api/milestones.ts b/frontend/src/api/milestones.ts new file mode 100644 index 00000000..4a03f8ed --- /dev/null +++ b/frontend/src/api/milestones.ts @@ -0,0 +1,11 @@ +import { apiGet, apiPost, apiPatch } from "./axios"; +import type { ApiListResponse } from "../types/api"; + +export const fetchMilestones = (studyId: string) => + apiGet>(`/api/v1/studies/${studyId}/milestones/`); + +export const createMilestone = (studyId: string, payload: Record) => + apiPost(`/api/v1/studies/${studyId}/milestones/`, payload); + +export const updateMilestone = (studyId: string, milestoneId: string, payload: Record) => + apiPatch(`/api/v1/studies/${studyId}/milestones/${milestoneId}`, payload); diff --git a/frontend/src/api/tasks.ts b/frontend/src/api/tasks.ts new file mode 100644 index 00000000..ef493618 --- /dev/null +++ b/frontend/src/api/tasks.ts @@ -0,0 +1,11 @@ +import { apiGet, apiPost, apiPatch } from "./axios"; +import type { ApiListResponse } from "../types/api"; + +export const fetchTasks = (studyId: string, params?: Record) => + apiGet>(`/api/v1/studies/${studyId}/tasks/`, { params }); + +export const createTask = (studyId: string, payload: Record) => + apiPost(`/api/v1/studies/${studyId}/tasks/`, payload); + +export const updateTask = (studyId: string, taskId: string, payload: Record) => + apiPatch(`/api/v1/studies/${studyId}/tasks/${taskId}`, payload); diff --git a/frontend/src/components/Layout.vue b/frontend/src/components/Layout.vue index 67bfc64e..bb9a73de 100644 --- a/frontend/src/components/Layout.vue +++ b/frontend/src/components/Layout.vue @@ -17,6 +17,8 @@ 首页 项目首页 + 里程碑 + 任务 diff --git a/frontend/src/components/MilestoneForm.vue b/frontend/src/components/MilestoneForm.vue new file mode 100644 index 00000000..c868176c --- /dev/null +++ b/frontend/src/components/MilestoneForm.vue @@ -0,0 +1,129 @@ + + + diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index dbb5d3ae..96e4f5f8 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -7,6 +7,7 @@ import Login from "../views/Login.vue"; import StudyList from "../views/StudyList.vue"; import StudyHome from "../views/StudyHome.vue"; import Tasks from "../views/Tasks.vue"; +import Milestones from "../views/Milestones.vue"; import Subjects from "../views/Subjects.vue"; import Aes from "../views/Aes.vue"; import DataQueries from "../views/DataQueries.vue"; @@ -44,6 +45,12 @@ const routes: RouteRecordRaw[] = [ component: Tasks, meta: { title: "任务", requiresStudy: true }, }, + { + path: "study/milestones", + name: "StudyMilestones", + component: Milestones, + meta: { title: "里程碑", requiresStudy: true }, + }, { path: "study/subjects", name: "StudySubjects", diff --git a/frontend/src/views/Milestones.vue b/frontend/src/views/Milestones.vue new file mode 100644 index 00000000..f68eeff9 --- /dev/null +++ b/frontend/src/views/Milestones.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/frontend/src/views/StudyHome.vue b/frontend/src/views/StudyHome.vue index 455bd9fd..abc37349 100644 --- a/frontend/src/views/StudyHome.vue +++ b/frontend/src/views/StudyHome.vue @@ -52,6 +52,7 @@