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 0000000..5c1d2c9 Binary files /dev/null and b/src/static/base/details.png differ diff --git a/src/static/base/push.png b/src/static/base/push.png new file mode 100644 index 0000000..693ffcd Binary files /dev/null and b/src/static/base/push.png differ diff --git a/src/static/base/qr-code.png b/src/static/base/qr-code.png new file mode 100644 index 0000000..d46440f Binary files /dev/null and b/src/static/base/qr-code.png differ