From bc28a17e01721755dcd3d69aede632b784deae4a Mon Sep 17 00:00:00 2001 From: hb Date: Wed, 23 Jul 2025 22:32:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AD=BE=E5=88=B0=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 + src/api/base/server.ts | 81 ++ src/pages.json | 44 + src/pages/base/service/index.vue | 7 + src/pages/view/notice/publish.vue | 112 ++- src/pages/view/routine/qd/confirm.vue | 630 ++++++++++++++ src/pages/view/routine/qd/detail.vue | 438 ++++++++++ src/pages/view/routine/qd/index.vue | 558 ++++++++++++ src/pages/view/routine/qd/publish.vue | 515 +++++++++++ src/pages/view/routine/qd/push-list.vue | 304 +++++++ src/pages/view/routine/qd/qr-code.vue | 443 ++++++++++ src/pages/view/routine/qd/selectTeachers.vue | 844 +++++++++++++++++++ src/static/base/details.png | Bin 0 -> 9951 bytes src/static/base/push.png | Bin 0 -> 7741 bytes src/static/base/qr-code.png | Bin 0 -> 5373 bytes 15 files changed, 3944 insertions(+), 34 deletions(-) create mode 100644 src/pages/view/routine/qd/confirm.vue create mode 100644 src/pages/view/routine/qd/detail.vue create mode 100644 src/pages/view/routine/qd/index.vue create mode 100644 src/pages/view/routine/qd/publish.vue create mode 100644 src/pages/view/routine/qd/push-list.vue create mode 100644 src/pages/view/routine/qd/qr-code.vue create mode 100644 src/pages/view/routine/qd/selectTeachers.vue create mode 100644 src/static/base/details.png create mode 100644 src/static/base/push.png create mode 100644 src/static/base/qr-code.png diff --git a/package.json b/package.json index 85393ce..b7b4126 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "lodash": "4.17.21", "pinia": "2.0.23", "pinia-plugin-persist-uni": "1.2.0", + "qrcode": "^1.5.3", "uview-plus": "3.1.20", "vconsole": "3.15.1", "vue": "3.2.45", @@ -72,6 +73,7 @@ "@types/html5plus": "1.0.2", "@types/lodash": "4.14.191", "@types/node": "18.11.15", + "@types/qrcode": "^1.5.5", "@types/uni-app": "1.4.4", "sass": "1.56.0", "sass-loader": "10.1.1", diff --git a/src/api/base/server.ts b/src/api/base/server.ts index 75fe4b0..1cc16a9 100644 --- a/src/api/base/server.ts +++ b/src/api/base/server.ts @@ -64,6 +64,26 @@ export const jsdfindJsByPhoneApi = async (params: any) => { return await get("/api/js/findJsByPhone", params); }; +// 根据年级/班级ID查询教师 +export const jsFindByNjIdOrBjIdApi = async (params: { njOrBjId: string }) => { + return await get(`/api/js/findByNjIdOrBjId?njOrBjId=${params.njOrBjId}`); +}; + +// 根据科目ID查询教师 +export const jsFindByKmIdApi = async (params: { kmId: string }) => { + return await get(`/api/js/findByKmId?kmId=${params.kmId}`); +}; + +// 根据班主任年级ID查询教师 +export const jsFindByBzrNjIdApi = async (params: { njId: string }) => { + return await get(`/api/js/findByBzrNjId?njId=${params.njId}`); +}; + +// 根据职务ID查询教师 +export const jsFindByZwIdApi = async (params: { zwId: string; zwType: string }) => { + return await get(`/api/js/findByZwId?zwId=${params.zwId}&zwType=${params.zwType}`); +}; + // 选课列表 export const jsdXkListApi = async (params: any) => { return await get("/mobile/js/xk/list", params); @@ -208,3 +228,64 @@ export const drpkkbApi = async (params: any) => { export const getRzRjApi = async (params: any) => { return await get("/mobile/js/getRzRj", params); }; + +// 签到发布相关API +// 获取签到发布列表 +export const qdzxFindPageApi = async (params: any) => { + return await get("/api/qdzx/findPage", params); +}; + +// 保存签到发布(新增/编辑) +export const qdzxSaveApi = async (params: any) => { + return await post("/api/qdzx/save", params); +}; + +// 根据签到ID查询签到执行情况(教师列表) +export const qdzxFindByQdParamsApi = async (params: { qdId: string }) => { + return await get("/api/qdzx/findByQdParams", params); +}; + +export const qdzxFindByQdAndJsApi = async (params: { qdId: string; jsId: string }) => { + return await get("/api/qdzx/findByQdAndJs", params); +}; + +// 保存签到消息推送 +export const xxtsSaveByQdzxParamsApi = async (params: { qdId: string }) => { + return await post("/api/xxts/saveByQdzxParams", params); +}; + +// 签到主表相关API +// 获取签到列表 +export const qdFindPageApi = async (params: any) => { + return await get("/api/qd/findPage", params); +}; + +// 保存签到(新增/编辑) +export const qdSaveApi = async (params: any) => { + return await post("/api/qd/save", params); +}; + +// 根据ID获取签到详情 +export const qdFindByIdApi = async (params: { id: string }) => { + return await get(`/api/qd/findById?id=${params.id}`); +}; + +// 签到执行相关API +export const qdzxSignInApi = async (params: any) => { + return await post("/api/qdzx/signIn", params); +}; + +// 生成签到二维码 +export const generateQRCodeApi = async (params: { qdId: string }) => { + return await post("/api/qdzx/generateQRCode", params); +}; + +// 年级相关API +export const findAllNj = async () => { + return await get("/api/nj/findAllNj"); +}; + +// 职务相关API +export const zwFindAllApi = async () => { + return await get("/api/zw/findAll"); +}; diff --git a/src/pages.json b/src/pages.json index bbc55e7..0a7fa50 100644 --- a/src/pages.json +++ b/src/pages.json @@ -493,6 +493,50 @@ "navigationBarTitleText": "学生请假审批", "enablePullDownRefresh": false } + }, + { + "path": "pages/view/routine/qd/index", + "style": { + "navigationBarTitleText": "签到发布" + } + }, + { + "path": "pages/view/routine/qd/publish", + "style": { + "navigationBarTitleText": "新增签到" + } + }, + { + "path": "pages/view/routine/qd/push-list", + "style": { + "navigationBarTitleText": "推送清单" + } + }, + { + "path": "pages/view/routine/qd/detail", + "style": { + "navigationBarTitleText": "签到详情" + } + }, + { + "path": "pages/view/routine/qd/selectTeachers", + "style": { + "navigationBarTitleText": "选择教师" + } + }, + { + "path": "pages/view/routine/qd/qr-code", + "style": { + "navigationBarTitleText": "签到二维码", + "navigationStyle": "custom" + } + }, + { + "path": "pages/view/routine/qd/confirm", + "style": { + "navigationBarTitleText": "确认签到", + "navigationStyle": "custom" + } } ], "globalStyle": { diff --git a/src/pages/base/service/index.vue b/src/pages/base/service/index.vue index d726a4a..4b0bf35 100644 --- a/src/pages/base/service/index.vue +++ b/src/pages/base/service/index.vue @@ -232,6 +232,13 @@ const sections = reactive([ show: true, path: "/pages/view/notice/index", }, + { + id: "r10", + icon: "draftfill", + text: "签到发布", + show: true, + path: "/pages/view/routine/qd/index", + }, ], }, { diff --git a/src/pages/view/notice/publish.vue b/src/pages/view/notice/publish.vue index 071c68e..cc78f6e 100644 --- a/src/pages/view/notice/publish.vue +++ b/src/pages/view/notice/publish.vue @@ -295,7 +295,7 @@ const signatureStatusText = computed(() => { }); // 树形数据 -const treeData = ref([]); +const treeData = ref([]); const treeRef = ref(); // 学生显示相关 @@ -310,23 +310,19 @@ const displayNames = computed(() => { // 添加 ref 来引用封面上传组件 const coverUploadRef = ref(); -// 统一接口返回类型声明 -interface ApiResponse { - resultCode: number; - result?: T; - resultMsg?: string; - data?: any; - [key: string]: any; -} - // 加载树形数据 const loadTreeData = async () => { try { - const res = await findAllNjBjTree() as ApiResponse; - if (res && res.resultCode === 1 && res.result) { + const res = await findAllNjBjTree(); + if (res.resultCode === 1 && res.result) { + // 添加调试信息,查看原始数据 + console.log("=== 后端返回的原始树形数据 ==="); + console.log("res.result:", res.result); + console.log("================================"); + // 递归转换数据格式以适配 BasicTree 组件,同时保留 njmcId 字段 - const convertTreeData = (items: Array<{ key: string; title: string; njmcId?: string; children?: any[] }>): any[] => { - return items.map((item) => ({ + const convertTreeData = (items: any[]): any[] => { + return items.map((item: any) => ({ key: item.key, title: item.title, njmcId: item.njmcId, // 保留 njmcId 字段 @@ -335,6 +331,11 @@ const loadTreeData = async () => { }; treeData.value = convertTreeData(res.result); + + // 添加调试信息,查看转换后的数据 + console.log("=== 转换后的树形数据 ==="); + console.log("treeData.value:", treeData.value); + console.log("=========================="); } } catch (error) { uni.showToast({ title: "加载班级数据失败", icon: "error" }); @@ -408,7 +409,9 @@ const resetForm = () => { const loadJlData = async (jlId: string) => { try { uni.showLoading({ title: "加载数据中..." }); - const response = await jlFindByIdApi({ id: jlId }) as ApiResponse; + + // 调用获取接龙详情的接口 + const response = await jlFindByIdApi({ id: jlId }); if (response && response.resultCode === 1 && response.result) { const jlData = response.result; @@ -426,10 +429,10 @@ const loadJlData = async (jlId: string) => { // 处理附件 if (jlData.jlfj) { const attachmentUrls = jlData.jlfj.split(","); - formData.attachments = attachmentUrls.map((url: string) => ({ + formData.attachments = attachmentUrls.map(url => ({ name: url.split("/").pop() || "附件", type: "file", - url: url as string + url: url })); } @@ -596,7 +599,7 @@ const getAttachmentIcon = (type: string): string => { const previewAttachment = (attachment: Attachment) => { // 如果是图片类型,可以预览 if (attachment.type === "image") { - const fullUrl = imagUrl(attachment.url as string); + const fullUrl = imagUrl(attachment.url); uni.previewImage({ urls: [fullUrl], current: fullUrl, @@ -617,11 +620,11 @@ const showClassTree = () => { }; // 树形选择确认 -const onTreeConfirm = async (selectedItems: Array) => { +const onTreeConfirm = async (selectedItems: any[]) => { if (selectedItems.length > 0) { // 处理多选情况 - const classNames = selectedItems.map((item: any) => item.title); + const classNames = selectedItems.map((item) => item.title); formData.targetClass = classNames.join(", "); formData.targetNames = []; formData.targetStudentIds = []; @@ -638,7 +641,7 @@ const onTreeConfirm = async (selectedItems: Array) => { const bjIds: string[] = []; const gradeNames: string[] = []; // 用于存储年级名称,用于错误提示 - selectedItems.forEach((item: any) => { + selectedItems.forEach((item) => { // 如果选择的是班级(有parents表示是班级) if (item.parents && item.parents.length > 0) { const parent = item.parents[0]; // 年级信息 @@ -649,13 +652,23 @@ const onTreeConfirm = async (selectedItems: Array) => { // 通过年级key从treeData中查找对应的njmcId let njmcId: string | undefined; - for (const grade of treeData.value || []) { + for (const grade of treeData.value) { if (grade.key === njId) { njmcId = grade.njmcId; break; } } + // 添加调试信息 + console.log("=== 年级信息调试 ==="); + console.log("item:", item); + console.log("parent:", parent); + console.log("njId:", njId); + console.log("njmcId (从treeData查找):", njmcId); + console.log("bjId:", bjId); + console.log("gradeName:", gradeName); + console.log("===================="); + // 确保年级信息完整 if (njId && bjId && gradeName) { njIds.push(njId); @@ -689,6 +702,14 @@ const onTreeConfirm = async (selectedItems: Array) => { const uniqueNjIds = [...new Set(njIds)]; const uniqueNjmcIds = [...new Set(njmcIds)]; + // 添加调试信息 + console.log("=== 最终设置调试 ==="); + console.log("njIds:", njIds); + console.log("njmcIds:", njmcIds); + console.log("uniqueNjIds:", uniqueNjIds); + console.log("uniqueNjmcIds:", uniqueNjmcIds); + console.log("===================="); + // 保存选择的年级ID、年级名称ID和班级ID到formData中 formData.targetNjIds = uniqueNjIds; formData.targetNjmcIds = uniqueNjmcIds; @@ -700,7 +721,7 @@ const onTreeConfirm = async (selectedItems: Array) => { bjId: bjIds.join(","), }; - const response = await mobilejlstudentListApi(params) as ApiResponse; + const response = await mobilejlstudentListApi(params); if (response && response.resultCode === 1 && response.result) { // 提取学生姓名,尝试多个可能的字段名 @@ -730,6 +751,7 @@ const onTreeConfirm = async (selectedItems: Array) => { icon: "success", }); } catch (error) { + console.error("获取学生列表失败:", error); uni.hideLoading(); uni.showToast({ title: "获取学生列表失败", icon: "error" }); @@ -844,6 +866,14 @@ const buildJlDto = (status: string) => { const user = userData.value; const js = jsData.value; + // 添加调试信息 + console.log("=== buildJlDto 调试信息 ==="); + console.log("formData.targetNjIds:", formData.targetNjIds); + console.log("formData.targetNjmcIds:", formData.targetNjmcIds); + console.log("formData.targetBjIds:", formData.targetBjIds); + console.log("targetNjmcIds.join(','):", formData.targetNjmcIds.join(",")); + console.log("=========================="); + return { id: formData.id || "", // 接龙ID,新增时为空 jlmc: formData.title.trim(), // 接龙名称 @@ -878,13 +908,13 @@ const saveDraft = async () => { const jlDto = buildJlDto("B"); // B表示暂存 // 调用后端接口 - const response = await jlSaveApi(jlDto) as ApiResponse; + const response = await jlSaveApi(jlDto); uni.hideLoading(); if (response && response.resultCode === 1) { // 获取接龙ID - const jlId = response.result || (response as any).data || jlDto.id; + const jlId = response.result || response.data || jlDto.id; // 更新表单中的ID if (jlId) { @@ -903,12 +933,15 @@ const saveDraft = async () => { }, 2000); } else { uni.showToast({ - title: (response as any)?.resultMsg || "保存草稿失败", + title: response?.resultMsg || "保存草稿失败", icon: "error" }); } } catch (error) { uni.hideLoading(); + console.error("保存草稿失败:", error); + + // 处理数据验证错误 if (error instanceof Error) { uni.showToast({ title: error.message, @@ -941,7 +974,7 @@ const publishNotice = async () => { // 如果有ID,检查接龙是否已存在且已发布 if (formData.id) { try { - const response = await jlFindByIdApi({ id: formData.id }) as ApiResponse; + const response = await jlFindByIdApi({ id: formData.id }); if (response && response.resultCode === 1 && response.result) { const jlData = response.result; if (jlData.jlStatus === 'A') { @@ -949,7 +982,9 @@ const publishNotice = async () => { return; } } - } catch (error) {} + } catch (error) { + console.error("检查接龙状态失败:", error); + } } try { @@ -959,21 +994,27 @@ const publishNotice = async () => { // 准备发布数据,构建 JlDto 对象 const jlDto = buildJlDto("A"); // A表示已发布 + // 添加调试信息 + console.log("=== 发布接龙调试信息 ==="); + console.log("jlDto:", jlDto); + console.log("=========================="); + // 调用后端接口,设置超时处理 const response = await Promise.race([ - jlSaveApi(jlDto) as Promise>, + jlSaveApi(jlDto), new Promise((_, reject) => setTimeout(() => reject(new Error("请求超时")), 300000) // 5分钟超时 ) - ]) as ApiResponse; + ]); uni.hideLoading(); if (response && response.resultCode === 1) { // 获取接龙ID,优先使用返回的ID,如果没有则使用请求中的ID - const jlId = response.result || (response as any).data || jlDto.id; + const jlId = response.result || response.data || jlDto.id; if (!jlId) { + console.error("发布成功但未获取到接龙ID"); uni.showToast({ title: "发布成功,但获取接龙ID失败", icon: "error" @@ -1001,7 +1042,7 @@ const publishNotice = async () => { }, 2000); } else { // 处理业务错误 - const errorMsg = (response as any)?.resultMsg || (response as any)?.message || "发布失败"; + const errorMsg = response?.resultMsg || response?.message || "发布失败"; uni.showToast({ title: errorMsg, icon: "none", @@ -1010,6 +1051,9 @@ const publishNotice = async () => { } } catch (error) { uni.hideLoading(); + console.error("发布接龙失败:", error); + + // 处理不同类型的错误 if (error instanceof Error) { if (error.message === "请求超时") { uni.showToast({ @@ -1025,7 +1069,7 @@ const publishNotice = async () => { } } else if (error && typeof error === 'object' && 'errMsg' in error) { // uni-app 错误对象 - if ((error as any).errMsg && (error as any).errMsg.includes('timeout')) { + if (error.errMsg && error.errMsg.includes('timeout')) { uni.showToast({ title: "请求超时,请检查网络连接或稍后重试", icon: "none", @@ -1033,7 +1077,7 @@ const publishNotice = async () => { }); } else { uni.showToast({ - title: (error as any).errMsg || "发布失败,请重试", + title: error.errMsg || "发布失败,请重试", icon: "error" }); } diff --git a/src/pages/view/routine/qd/confirm.vue b/src/pages/view/routine/qd/confirm.vue new file mode 100644 index 0000000..9379db6 --- /dev/null +++ b/src/pages/view/routine/qd/confirm.vue @@ -0,0 +1,630 @@ + + + + + \ No newline at end of file diff --git a/src/pages/view/routine/qd/detail.vue b/src/pages/view/routine/qd/detail.vue new file mode 100644 index 0000000..89fbac1 --- /dev/null +++ b/src/pages/view/routine/qd/detail.vue @@ -0,0 +1,438 @@ + + + + + \ No newline at end of file diff --git a/src/pages/view/routine/qd/index.vue b/src/pages/view/routine/qd/index.vue new file mode 100644 index 0000000..0e9c19a --- /dev/null +++ b/src/pages/view/routine/qd/index.vue @@ -0,0 +1,558 @@ + + + + + + \ No newline at end of file diff --git a/src/pages/view/routine/qd/publish.vue b/src/pages/view/routine/qd/publish.vue new file mode 100644 index 0000000..cbd69b8 --- /dev/null +++ b/src/pages/view/routine/qd/publish.vue @@ -0,0 +1,515 @@ + + + + + \ No newline at end of file diff --git a/src/pages/view/routine/qd/push-list.vue b/src/pages/view/routine/qd/push-list.vue new file mode 100644 index 0000000..3211a8f --- /dev/null +++ b/src/pages/view/routine/qd/push-list.vue @@ -0,0 +1,304 @@ + + + + + \ No newline at end of file diff --git a/src/pages/view/routine/qd/qr-code.vue b/src/pages/view/routine/qd/qr-code.vue new file mode 100644 index 0000000..eda6df9 --- /dev/null +++ b/src/pages/view/routine/qd/qr-code.vue @@ -0,0 +1,443 @@ + + + + + \ No newline at end of file diff --git a/src/pages/view/routine/qd/selectTeachers.vue b/src/pages/view/routine/qd/selectTeachers.vue new file mode 100644 index 0000000..04c1257 --- /dev/null +++ b/src/pages/view/routine/qd/selectTeachers.vue @@ -0,0 +1,844 @@ + + + + + \ No newline at end of file diff --git a/src/static/base/details.png b/src/static/base/details.png new file mode 100644 index 0000000000000000000000000000000000000000..5c1d2c977885bd50bfedfc420169c2ae6ccadb86 GIT binary patch literal 9951 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 literal 0 HcmV?d00001 diff --git a/src/static/base/push.png b/src/static/base/push.png new file mode 100644 index 0000000000000000000000000000000000000000..693ffcdc48921fd09547e552ea86b2aceb056469 GIT binary patch literal 7741 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 Xe>qi&RNG?v!2l&WRoTiHra}J)Fcg*K literal 0 HcmV?d00001 diff --git a/src/static/base/qr-code.png b/src/static/base/qr-code.png new file mode 100644 index 0000000000000000000000000000000000000000..d46440f3e3ffa4f1f27f94f1b0e47638f2a7f894 GIT binary patch literal 5373 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