From a592e66f78e2256cb95e5ecea42e573c5d0f9748 Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Tue, 15 Sep 2026 15:55:00 +0800 Subject: [PATCH 01/39] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E3=80=8C=E6=95=B0=E6=8D=AE=E5=90=8C=E6=AD=A5=E3=80=8D=E4=B8=8E?= =?UTF-8?q?=E3=80=8C=E5=AD=A6=E7=94=9F=E5=90=8D=E5=86=8C=E3=80=8D=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 数据同步页 /gxmu/dataSync: - 分段手动同步(部门/教师/班级/学生)+ 全部同步 - 同步为异步:提交后立即返回批次号,每 2 秒轮询 /gxmu/sync/status 展示分段进度 (待执行/进行中/成功/失败 + 成功/失败/停用计数 + 错误摘要) - 页面挂载时先查一次状态,可接管定时任务或别处触发的同步 - 同步记录列表,支持按类型筛选 学生名册页 /gxmu/studentRoster: - 按学院(取组织树 parentId=24)/班级/学号/姓名/培养层次/状态/认领状态筛选 - 手机号与身份证由后端脱敏后返回 - 导出用 gxmu:student:export 单独授权,前端生成 CSV - 支持手动认领与解除认领 对应 api 模块 src/api/gxmu/sync 与 src/api/gxmu/student。 --- src/api/gxmu/student/index.ts | 77 +++++ src/api/gxmu/student/model/index.ts | 60 ++++ src/api/gxmu/sync/index.ts | 66 +++++ src/api/gxmu/sync/model/index.ts | 79 ++++++ src/views/gxmu/dataSync/index.vue | 370 +++++++++++++++++++++++++ src/views/gxmu/studentRoster/index.vue | 362 ++++++++++++++++++++++++ 6 files changed, 1014 insertions(+) create mode 100644 src/api/gxmu/student/index.ts create mode 100644 src/api/gxmu/student/model/index.ts create mode 100644 src/api/gxmu/sync/index.ts create mode 100644 src/api/gxmu/sync/model/index.ts create mode 100644 src/views/gxmu/dataSync/index.vue create mode 100644 src/views/gxmu/studentRoster/index.vue diff --git a/src/api/gxmu/student/index.ts b/src/api/gxmu/student/index.ts new file mode 100644 index 0000000..b685f66 --- /dev/null +++ b/src/api/gxmu/student/index.ts @@ -0,0 +1,77 @@ +import request from '@/utils/request'; +import type { ApiResult, PageResult } from '@/api'; +import type { StudentRoster, StudentRosterParam } from './model'; + +/** 学生名册分页查询 */ +export async function pageStudentRoster(params: StudentRosterParam) { + const res = await request.get>>( + '/gxmu/student/page', + { params } + ); + if (res.data.code === 0 && res.data.data) { + return res.data.data; + } + return Promise.reject(new Error(res.data.message)); +} + +/** 学生名册详情 */ +export async function getStudentRoster(id: number) { + const res = await request.get>( + '/gxmu/student/' + id + ); + if (res.data.code === 0 && res.data.data) { + return res.data.data; + } + return Promise.reject(new Error(res.data.message)); +} + +/** 导出名册(返回已脱敏列表,由前端生成文件) */ +export async function exportStudentRoster(params: StudentRosterParam) { + const res = await request.post>( + '/gxmu/student/export', + null, + { params } + ); + if (res.data.code === 0 && res.data.data) { + return res.data.data; + } + return Promise.reject(new Error(res.data.message)); +} + +/** 自助认领(学号 + 姓名) */ +export async function claimStudent(data: { xh: string; xm: string }) { + const res = await request.post>( + '/gxmu/student/claim', + data + ); + if (res.data.code === 0) { + return res.data.message; + } + return Promise.reject(new Error(res.data.message)); +} + +/** 手动认领/改绑 */ +export async function bindStudent( + id: number, + data?: { userId?: number; phone?: string } +) { + const res = await request.put>( + `/gxmu/student/${id}/claim`, + data || {} + ); + if (res.data.code === 0) { + return res.data.message; + } + return Promise.reject(new Error(res.data.message)); +} + +/** 解除认领 */ +export async function unbindStudent(id: number) { + const res = await request.put>( + `/gxmu/student/${id}/unclaim` + ); + if (res.data.code === 0) { + return res.data.message; + } + return Promise.reject(new Error(res.data.message)); +} diff --git a/src/api/gxmu/student/model/index.ts b/src/api/gxmu/student/model/index.ts new file mode 100644 index 0000000..f2505f5 --- /dev/null +++ b/src/api/gxmu/student/model/index.ts @@ -0,0 +1,60 @@ +/** + * 学生名册(开放平台同步) + */ + +/** 学生名册记录 */ +export interface StudentRoster { + id: number; + /** 学号 */ + xh: string; + /** 姓名 */ + xm?: string; + /** 性别码 */ + xbm?: string; + /** 性别 */ + sex?: string; + /** 民族码 */ + mzm?: string; + /** 民族 */ + nation?: string; + /** 政治面貌码 */ + zzmmm?: string; + /** 政治面貌 */ + politics?: string; + /** 身份证件号(后端已脱敏) */ + sfzjh?: string; + /** 手机号(后端已脱敏) */ + sjh?: string; + /** 平台班级码 */ + bjm?: string; + /** 平台班号 */ + bh?: string; + classId?: number; + className?: string; + collegeId?: number; + collegeName?: string; + dwh?: string; + zyh?: string; + zymc?: string; + /** 培养层次 */ + pyccmc?: string; + /** 状态:1在校 0毕业 */ + status?: number; + /** 已认领的账号ID */ + userId?: number; + claimTime?: string; + syncTime?: string; +} + +/** 名册查询参数 */ +export interface StudentRosterParam { + page?: number; + limit?: number; + collegeId?: number; + classId?: number; + xh?: string; + xm?: string; + pyccmc?: string; + status?: number; + claimed?: boolean; +} diff --git a/src/api/gxmu/sync/index.ts b/src/api/gxmu/sync/index.ts new file mode 100644 index 0000000..bcb6b4b --- /dev/null +++ b/src/api/gxmu/sync/index.ts @@ -0,0 +1,66 @@ +import request from '@/utils/request'; +import type { ApiResult, PageResult } from '@/api'; +import type { + SyncBatchStatus, + SyncRecord, + SyncRecordParam +} from './model'; + +/** 提交类接口统一返回批次号 */ +async function submit(url: string) { + const res = await request.post>(url); + if (res.data.code === 0 && res.data.data) { + return res.data.data; + } + return Promise.reject(new Error(res.data.message)); +} + +/** 提交全部同步,返回批次号 */ +export function syncAll() { + return submit('/gxmu/sync/all'); +} + +/** 提交部门同步,返回批次号 */ +export function syncDept() { + return submit('/gxmu/sync/dept'); +} + +/** 提交教师同步,返回批次号 */ +export function syncTeacher() { + return submit('/gxmu/sync/teacher'); +} + +/** 提交班级同步,返回批次号 */ +export function syncClass() { + return submit('/gxmu/sync/class'); +} + +/** 提交学生同步,返回批次号 */ +export function syncStudent() { + return submit('/gxmu/sync/student'); +} + +/** + * 查询当前(或最近一次)批次进度。 + * + * 同步是异步的:学生全量约 9.8 万条、耗时 75~90 秒,提交后需轮询本接口看进度。 + */ +export async function syncStatus() { + const res = await request.get>('/gxmu/sync/status'); + if (res.data.code === 0 && res.data.data) { + return res.data.data; + } + return Promise.reject(new Error(res.data.message)); +} + +/** 同步记录分页查询 */ +export async function pageSyncRecord(params: SyncRecordParam) { + const res = await request.get>>( + '/gxmu/sync/record/page', + { params } + ); + if (res.data.code === 0 && res.data.data) { + return res.data.data; + } + return Promise.reject(new Error(res.data.message)); +} diff --git a/src/api/gxmu/sync/model/index.ts b/src/api/gxmu/sync/model/index.ts new file mode 100644 index 0000000..84b0470 --- /dev/null +++ b/src/api/gxmu/sync/model/index.ts @@ -0,0 +1,79 @@ +/** + * 开放平台数据同步 + */ + +/** 单段同步结果(同步返回,异步改动后仅用于记录展示) */ +export interface SyncResult { + /** 同步类型:dept/teacher/class/student */ + syncType: string; + /** 成功条数 */ + successCount: number; + /** 失败条数 */ + failCount: number; + /** 跳过/停用条数 */ + skipCount: number; + /** 是否成功 */ + success: boolean; + /** 错误信息 */ + errorMsg?: string; +} + +/** 某一段的进度 */ +export interface SyncSectionStatus { + /** 同步类型:dept/teacher/class/student */ + syncType: string; + /** pending 待执行 / running 进行中 / success 成功 / fail 失败 */ + state: 'pending' | 'running' | 'success' | 'fail'; + successCount?: number; + failCount?: number; + skipCount?: number; + startTime?: string; + endTime?: string; + errorMsg?: string; +} + +/** 批次进度 */ +export interface SyncBatchStatus { + /** 是否有同步任务在执行 */ + running: boolean; + batchId?: string; + triggerType?: string; + triggerUser?: string; + startTime?: string; + /** 正在执行的段 */ + currentSection?: string; + /** 本次批次的各段状态 */ + sections?: SyncSectionStatus[]; +} + +/** 同步记录 */ +export interface SyncRecord { + id: number; + /** 批次号 */ + batchId?: string; + /** 同步类型 */ + syncType: string; + /** 触发方式:schedule/manual */ + triggerType: string; + /** 触发人 */ + triggerUser?: string; + /** 开始时间 */ + startTime?: string; + /** 结束时间 */ + endTime?: string; + successCount: number; + failCount: number; + skipCount: number; + /** 状态:0成功 1失败 2进行中 */ + status: number; + /** 错误摘要 */ + errorMsg?: string; +} + +/** 同步记录查询参数 */ +export interface SyncRecordParam { + page?: number; + limit?: number; + syncType?: string; + batchId?: string; +} diff --git a/src/views/gxmu/dataSync/index.vue b/src/views/gxmu/dataSync/index.vue new file mode 100644 index 0000000..38545bc --- /dev/null +++ b/src/views/gxmu/dataSync/index.vue @@ -0,0 +1,370 @@ + + + + + diff --git a/src/views/gxmu/studentRoster/index.vue b/src/views/gxmu/studentRoster/index.vue new file mode 100644 index 0000000..c950496 --- /dev/null +++ b/src/views/gxmu/studentRoster/index.vue @@ -0,0 +1,362 @@ + + + + + From b2b7d4208ea4375cbf7f096bb86a7db438e3edcd Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Tue, 15 Sep 2026 17:21:38 +0800 Subject: [PATCH 02/39] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E3=80=8C=E6=95=99=E5=B8=88=E5=90=8D=E5=86=8C=E3=80=8D=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 纯只读页面,形态与学生名册一致(#19)。 - src/api/gxmu/teacher:分页查询与导出接口 - src/views/gxmu/teacherRoster:学院/教工号/姓名/状态筛选 + 列表 + CSV 导出 - 导出按钮按 gxmu:teacher:export 权限渲染,与查询权限分开 - 手机号按 spec §5.2 不进任何列表接口,页面与导出均无该列 --- src/api/gxmu/teacher/index.ts | 28 +++ src/api/gxmu/teacher/model/index.ts | 43 +++++ src/views/gxmu/teacherRoster/index.vue | 243 +++++++++++++++++++++++++ 3 files changed, 314 insertions(+) create mode 100644 src/api/gxmu/teacher/index.ts create mode 100644 src/api/gxmu/teacher/model/index.ts create mode 100644 src/views/gxmu/teacherRoster/index.vue diff --git a/src/api/gxmu/teacher/index.ts b/src/api/gxmu/teacher/index.ts new file mode 100644 index 0000000..54bb1f5 --- /dev/null +++ b/src/api/gxmu/teacher/index.ts @@ -0,0 +1,28 @@ +import request from '@/utils/request'; +import type { ApiResult, PageResult } from '@/api'; +import type { TeacherRoster, TeacherRosterParam } from './model'; + +/** 教师名册分页查询 */ +export async function pageTeacherRoster(params: TeacherRosterParam) { + const res = await request.get>>( + '/gxmu/teacher/page', + { params } + ); + if (res.data.code === 0 && res.data.data) { + return res.data.data; + } + return Promise.reject(new Error(res.data.message)); +} + +/** 导出教师名册(返回列表,由前端生成文件;响应中不含手机号) */ +export async function exportTeacherRoster(params: TeacherRosterParam) { + const res = await request.post>( + '/gxmu/teacher/export', + null, + { params } + ); + if (res.data.code === 0 && res.data.data) { + return res.data.data; + } + return Promise.reject(new Error(res.data.message)); +} diff --git a/src/api/gxmu/teacher/model/index.ts b/src/api/gxmu/teacher/model/index.ts new file mode 100644 index 0000000..5f820b9 --- /dev/null +++ b/src/api/gxmu/teacher/model/index.ts @@ -0,0 +1,43 @@ +/** + * 教师名册(开放平台同步) + */ + +/** 教师名册记录 */ +export interface TeacherRoster { + id: number; + /** 教工号(开放平台外部键) */ + jgh: string; + /** 姓名 */ + xm?: string; + /** 性别码 */ + xbm?: string; + /** 性别 */ + sex?: string; + /** 单位号(平台原始值) */ + dwh?: string; + /** 解析出的学院ID */ + collegeId?: number; + /** 学院名称 */ + collegeName?: string; + /** 科室教研室编号 */ + ksjybh?: string; + /** 状态:1启用 0停用 */ + status?: number; + /** 最近同步时间 */ + syncTime?: string; +} + +/** + * 教师名册查询参数 + * + * 说明:教师手机号由后端落盘但不出任何列表接口(spec §5.2),故此处没有该字段, + * 页面也不展示。 + */ +export interface TeacherRosterParam { + page?: number; + limit?: number; + collegeId?: number; + jgh?: string; + xm?: string; + status?: number; +} diff --git a/src/views/gxmu/teacherRoster/index.vue b/src/views/gxmu/teacherRoster/index.vue new file mode 100644 index 0000000..1b752f7 --- /dev/null +++ b/src/views/gxmu/teacherRoster/index.vue @@ -0,0 +1,243 @@ + + + + + From add24cfde8c7c6c442862333f52f6d7e4a380afe Mon Sep 17 00:00:00 2001 From: "weicw1996@qq.com" Date: Fri, 18 Sep 2026 23:30:54 +0800 Subject: [PATCH 03/39] =?UTF-8?q?=E5=90=8E=E5=8F=B0=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=BB=9F=E4=B8=80=E8=BA=AB=E4=BB=BD=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 与学生端共用后端 /sso/login 接口,学号 + 统一身份认证密码, 返回结构与账号登录一致,拿到 access_token 后流程无差别。 --- src/api/passport/login/index.ts | 18 +++++++++ src/views/passport/login/index.vue | 59 +++++++++++++++++++++++++----- 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/src/api/passport/login/index.ts b/src/api/passport/login/index.ts index 3403079..b847141 100644 --- a/src/api/passport/login/index.ts +++ b/src/api/passport/login/index.ts @@ -20,6 +20,24 @@ export async function login(data: LoginParam) { return Promise.reject(new Error(res.data.message)); } +/** + * 学校统一身份认证登录(学号 + 统一身份认证密码) + * + * 与小程序端共用同一个后端接口,返回结构完全一致, + * 因此拿到 access_token 后前端流程与账号登录没有区别。 + */ +export async function loginByGxmuSso(data: LoginParam) { + const res = await request.post>('/sso/login', { + username: data.username, + password: data.password + }); + if (res.data.code === 0) { + setToken(res.data.data?.access_token, data.remember); + return res.data.message; + } + return Promise.reject(new Error(res.data.message)); +} + /** * 获取验证码 */ diff --git a/src/views/passport/login/index.vue b/src/views/passport/login/index.vue index 198c714..1ecad0c 100644 --- a/src/views/passport/login/index.vue +++ b/src/views/passport/login/index.vue @@ -26,6 +26,13 @@ > 账号登录 + - + +
+
+ 写作参数 + 活动信息可选填,填了会一并提交 + + 已填 {{ writingParamCount }} 项 + + +
+
+
+ 活动类型 + + +
+
+ + + + +
+
+ 下方输入框里的内容会作为「亮点内容」提交;活动主题留空时由上游自己拟标题。 +
+
+
参考素材
-
- 参考素材 - - - 最多 {{ IMAGE_FILE_LIMIT }} 个,支持图片 / 视频 / 音频 / 文档 - -
    -
  • - {{ item.name }} - -
  • -
- -