This commit is contained in:
ywyonui 2025-07-25 17:59:46 +08:00
commit d114fe8c8f
5 changed files with 180 additions and 96 deletions

View File

@ -180,3 +180,10 @@ export const jzXsQjListApi = async (params: any) => {
export const jzXsQjActivitiHistoryApi = async (params: any) => {
return await get("/activiti/history/historicFlow", params);
};
/**
*
*/
export const getUserLatestInfoApi = async () => {
return await get("/open/login/getLatestInfo");
};

View File

@ -75,14 +75,60 @@
</template>
<script setup lang="ts">
import { ref } from "vue";
import { ref, computed, onMounted, watch } from "vue";
import { onShow } from "@dcloudio/uni-app";
import XsPicker from "@/pages/base/components/XsPicker/index.vue"
import { cmsArticlePageApi } from "@/api/base/server";
import { cmsArticlePageApi, getUserLatestInfoApi } from "@/api/base/server";
import { useUserStore } from "@/store/modules/user";
import { useDataStore } from "@/store/modules/data";
const { getCurXs } = useUserStore();
const { setData, getAppCode } = useDataStore();
//
const { getLastRefreshTime, getRefreshInterval, setLastRefreshTime, updateStudentInfo, updateStudentList } = useUserStore();
const REFRESH_INTERVAL = 10 * 60 * 1000; // 10
//
const checkAndRefreshStudentInfo = async () => {
const lastRefreshTime = getLastRefreshTime;
const currentTime = Date.now();
if (!lastRefreshTime || (currentTime - lastRefreshTime) > REFRESH_INTERVAL) {
await refreshStudentInfo();
setLastRefreshTime(currentTime);
}
};
//
const refreshStudentInfo = async () => {
try {
const response = await getUserLatestInfoApi();
if (response && response.result) {
//
if (response.result.xsList && response.result.xsList.length > 0) {
updateStudentList(response.result.xsList);
//
const currentXsId = curXs.value?.id;
if (currentXsId) {
const currentStudent = response.result.xsList.find((xs: any) => xs.id === currentXsId);
if (currentStudent) {
updateStudentInfo(currentStudent);
} else {
//
updateStudentInfo(response.result.xsList[0]);
}
} else {
//
updateStudentInfo(response.result.xsList[0]);
}
}
}
} catch (error) {
//
}
};
//
const menuItems = ref([
{
@ -161,7 +207,6 @@ const goToGlxs = () => {
//
function handleMenuClick(item: any) {
console.log("点击菜单:", item.title);
if (item.path) {
uni.navigateTo({
url: item.path,
@ -183,19 +228,46 @@ function goToDetail(notice: any) {
}
const getArticleList = async () => {
const params = Object.assign({}, pageParams.value, { njmcId: curXs.value.njmcId });
if (curXs.value && curXs.value.njmcId) {
const params = Object.assign({}, pageParams.value, { njmcId: curXs.value.njmcId });
cmsArticlePageApi(params).then(res => {
announcements.value = res.rows;
})
.catch((error) => {
//
console.error("调用检查通知公告接口失败:", error);
});
});
}
};
onMounted(async () => {
getArticleList();
//
if (curXs.value && curXs.value.njmcId) {
getArticleList();
} else {
// store
setTimeout(() => {
if (curXs.value && curXs.value.njmcId) {
getArticleList();
}
}, 100);
}
//
await checkAndRefreshStudentInfo();
});
//
onShow(async () => {
await checkAndRefreshStudentInfo();
});
//
watch(curXs, (newXs, oldXs) => {
if (newXs && newXs.njmcId) {
getArticleList();
}
}, { immediate: false });
</script>
<style lang="scss" scoped>

View File

@ -8,13 +8,13 @@
<view class="info-card main-content-card">
<view v-if="noticeDetail.jlfm" class="cover-image-container">
<image
:src="imagUrl(noticeDetail.jlfm)"
mode="aspectFill"
class="cover-image"
:src="imagUrl(noticeDetail.jlfm)"
mode="aspectFill"
class="cover-image"
></image>
</view>
<text class="notice-title">{{ noticeDetail.jlmc }}</text>
<!-- 发布人和结束时间 -->
<view class="notice-meta">
<text class="meta-item">发布人: {{ noticeDetail.jsxm }}</text>
@ -24,11 +24,11 @@
<view class="notice-content">
<view v-if="!descExpanded">
<text class="desc-preview">{{ descPreview }}</text>
<u-button type="text" size="mini" @click="descExpanded = true">更多</u-button>
<u-button type="text" size="mini" class="more-btn" @click="descExpanded = true">更多</u-button>
</view>
<view v-else>
<rich-text :nodes="noticeDetail.jlms" class="desc-rich-text"></rich-text>
<u-button type="text" size="mini" @click="descExpanded = false">收起</u-button>
<u-button type="text" size="mini" class="more-btn" @click="descExpanded = false">收起</u-button>
</view>
</view>
</view>
@ -37,8 +37,8 @@
<text class="attachment-title">附件</text>
<view class="attachment-list">
<view
class="attachment-item"
@click="previewAttachment(noticeDetail.jlfj)"
class="attachment-item"
@click="previewAttachment(noticeDetail.jlfj)"
>
<uni-icons type="paperplane" size="16" color="#409eff"></uni-icons>
<text class="attachment-name">{{ getFileName(noticeDetail.jlfj) }}</text>
@ -50,13 +50,10 @@
<text class="feedback-title">接龙完成情况 ({{ receivedCount }}/{{ totalStudents }})</text>
<view class="name-tags">
<view
v-for="stu in studentList"
:key="stu.id || stu.xsId"
class="name-tag"
:class="{
received: stu.jlwc_status === 'A',
currentStudent: isCurrentStudent(stu)
}"
v-for="stu in studentList"
:key="stu.id || stu.xsId"
class="name-tag"
:class="{ received: stu.jlwc_status === 'A' }"
>
<text>{{ stu.xsxm || stu.name }}</text>
<view v-if="stu.jlwc_status === 'A'" class="checkmark-icon">
@ -68,29 +65,24 @@
</view>
<view v-else class="empty-state">通知详情未找到</view>
</view>
<!-- 添加签名组件 -->
<BasicSign ref="signCompRef" :title="signTitle" @confirm="onSignConfirm"/>
<!-- 添加签名组件 - 只在需要时显示 -->
<BasicSign v-if="showSignature" ref="signCompRef" :title="signTitle"></BasicSign>
<template #bottom>
<view class="flex-row items-center pb-10 pt-5">
<u-button
text="点击接龙"
class="mr-15 mr-7"
type="primary"
@click="onRelayClick"
/>
<view class="bottom-actions">
<button class="action-btn publish-btn" @click="onRelayClick">接龙</button>
</view>
</template>
</BasicLayout>
</template>
<script lang="ts" setup>
import {ref, computed, watch} from "vue";
import {onLoad} from "@dcloudio/uni-app";
import {getByJlIdApi, jlzxFindByJlParamsApi, relayFinishApi} from "@/api/base/server";
import {imagUrl} from "@/utils";
import {BASE_IMAGE_URL} from "@/config";
import {useUserStore} from "@/store/modules/user";
import {navigateBack, showLoading, hideLoading} from "@/utils/uniapp";
import { ref, computed, watch, nextTick } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import { getByJlIdApi, jlzxFindByJlParamsApi, relayFinishApi } from "@/api/base/server";
import { imagUrl } from "@/utils";
import { BASE_IMAGE_URL } from "@/config";
import { useUserStore } from "@/store/modules/user";
import { showLoading, hideLoading } from "@/utils/uniapp";
const noticeId = ref<string>("");
const noticeDetail = ref<any>(null);
@ -102,6 +94,7 @@ const descExpanded = ref(false);
const signCompRef = ref<any>(null);
const signTitle = ref<string>("签名");
const sign_file = ref<string>("");
const showSignature = ref<boolean>(false);
//
const userStore = useUserStore();
@ -123,19 +116,14 @@ const getFileName = (filePath: string) => {
// Computed properties for feedback status
const receivedCount = computed(() => {
return studentList.value.filter((s) => s.jlwc_status === '1').length;
return studentList.value.filter((s) => s.jlwc_status === 'A').length;
});
const totalStudents = computed(() => studentList.value.length);
//
const isCurrentStudent = (student: any) => {
return currentStudent.value && student.xsId === currentStudent.value.id;
};
//
//
async function onRelayClick() {
try {
const res = await getByJlIdApi({jlId: noticeId.value});
const res = await getByJlIdApi({ jlId: noticeId.value });
const detail = Array.isArray(res) ? res[0] : res;
//
const curStu = studentList.value.find(stu => {
@ -149,38 +137,24 @@ async function onRelayClick() {
}
if (detail && detail.mdqz == 1) {
//
showSignature.value = true;
sign_file.value = '';
if (signCompRef.value) {
onSignConfirm();
}
//
await nextTick();
const data = await signCompRef.value.getSyncSignature();
sign_file.value = data.base64;
showSignature.value = false;
submitRelay();
} else {
submitRelay();
}
} catch (e) {
showSignature.value = false;
uni.showToast({title: '获取详情失败', icon: 'none'});
}
}
//
async function onSignConfirm() {
try {
if (!sign_file.value) {
if (signCompRef.value && signCompRef.value.getSyncSignature) {
const data = await signCompRef.value.getSyncSignature();
sign_file.value = data.base64;
}
}
if (!sign_file.value) {
uni.showToast({title: '请完成签名', icon: 'none'});
return;
}
submitRelay();
} catch (e) {
uni.showToast({title: '签名流程异常', icon: 'none'});
}
}
//
//
async function submitRelay() {
const params: any = {
jl_id: noticeId.value,
@ -196,7 +170,6 @@ async function submitRelay() {
if (res && res.resultCode === 1 ) {
uni.showToast({title: '接龙成功', icon: 'success'});
await refreshStudentList();
} else {
uni.showToast({title: res?.msg || '接龙失败', icon: 'none'});
}
@ -206,7 +179,7 @@ async function submitRelay() {
}
}
//
//
async function refreshStudentList() {
try {
const stuRes = await jlzxFindByJlParamsApi({ jlId: noticeId.value });
@ -222,40 +195,40 @@ onLoad(async (options) => {
isLoading.value = true;
// 1.
try {
const detailRes = await getByJlIdApi({jlId: noticeId.value});
const detailRes = await getByJlIdApi({ jlId: noticeId.value });
noticeDetail.value = Array.isArray(detailRes) ? detailRes[0] : {};
} catch (e) {
uni.showToast({title: "加载接龙详情失败", icon: "none"});
uni.showToast({ title: "加载接龙详情失败", icon: "none" });
}
// 2.
try {
const stuRes = await jlzxFindByJlParamsApi({jlId: noticeId.value});
const stuRes = await jlzxFindByJlParamsApi({ jlId: noticeId.value });
studentList.value = stuRes?.rows || stuRes?.result || [];
} catch (e) {
uni.showToast({title: "加载学生状态失败", icon: "none"});
uni.showToast({ title: "加载学生状态失败", icon: "none" });
}
isLoading.value = false;
uni.setNavigationBarTitle({title: "点击接龙"});
uni.setNavigationBarTitle({ title: "接龙情况" });
} else {
uni.showToast({title: "加载失败缺少通知ID", icon: "none"});
uni.showToast({ title: "加载失败缺少通知ID", icon: "none" });
}
});
//
const previewAttachment = (filePath: string) => {
if (!filePath) {
uni.showToast({title: "附件链接无效", icon: "none"});
uni.showToast({ title: "附件链接无效", icon: "none" });
return;
}
// URL
const baseUrl = BASE_IMAGE_URL; // 使URL
const fullUrl = filePath.startsWith('http') ? filePath : baseUrl + filePath;
//
const fileExtension = filePath.split('.').pop()?.toLowerCase();
const isImage = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(fileExtension || '');
//
if (isImage) {
uni.previewImage({
@ -265,7 +238,7 @@ const previewAttachment = (filePath: string) => {
return;
}
uni.showLoading({title: "正在准备附件..."});
uni.showLoading({ title: "正在准备附件..." });
uni.downloadFile({
url: fullUrl,
@ -280,33 +253,37 @@ const previewAttachment = (filePath: string) => {
//
},
fail: function (err) {
uni.showToast({title: "无法打开该文件类型", icon: "none"});
uni.showToast({ title: "无法打开该文件类型", icon: "none" });
uni.hideLoading(); // Ensure loading is hidden on failure too
},
});
} else {
uni.hideLoading();
uni.showToast({title: "下载附件失败", icon: "none"});
uni.showToast({ title: "下载附件失败", icon: "none" });
}
},
fail: (err) => {
uni.hideLoading();
uni.showToast({title: "下载附件失败", icon: "none"});
uni.showToast({ title: "下载附件失败", icon: "none" });
},
});
};
watch(noticeDetail, (val) => {
//
}, {immediate: true, deep: true});
}, { immediate: true, deep: true });
// studentListjlwc_status
//
watch(studentList, (list) => {
list.forEach(stu => {
//
if (stu.jlwcStatus && !stu.jlwc_status) {
stu.jlwc_status = stu.jlwcStatus;
}
// '1''A'
if (stu.jlwc_status === '1') {
stu.jlwc_status = 'A';
}
});
}, { immediate: true, deep: true });
</script>
@ -578,14 +555,12 @@ watch(studentList, (list) => {
box-sizing: border-box;
// 44
flex-basis: calc((100% - 30px) / 4); // 43
min-width: 60px; // 4
height: 30px; // Slightly taller height
line-height: 20px; // Adjust line-height for vertical centering
border: 1px solid #f4f4f5;
//
white-space: normal;
word-break: keep-all; //
overflow: hidden;
word-break: break-all; // Break long names if they don't fit
}
.name-tag {
@ -635,6 +610,13 @@ watch(studentList, (list) => {
}
}
/* 更多按钮样式 */
.more-btn {
font-size: 16px !important;
color: #409eff !important;
font-weight: 500;
}
/* 列表项样式的卡片 */
.list-item-card {
padding: 5px 15px; // padding

View File

@ -206,7 +206,6 @@ const openDicPicker = (xs: any) => {
};
const dicChanged = (dicArr: any) => {
console.log(dicArr);
const dic = dicOptions.value[0][dicArr[0]];
curXs.value.jzxsgxId = dic.dictionaryValue;
curXs.value.jzxsgxmc = dic.dictionaryCode;
@ -224,11 +223,9 @@ async function afterRead(event: any, index: number) {
const result = res.result;
if (result && result.length > 0 && result[0].filePath) {
students.value[index].xstx = result[0].filePath;
console.log(`Student ${index} avatar uploaded:`, result[0].filePath);
showToast({ title: "上传成功" });
} else {
showToast({ title: "上传失败,请重试", icon: "none" });
console.error("Upload result format error:", result);
}
} catch (error) {
showToast({ title: "上传出错", icon: "none" });
@ -313,7 +310,7 @@ async function submit() {
showToast({ title: res.message || "提交失败", icon: "none" });
}
} catch (error) {
console.log(error);
//
}
}

View File

@ -12,9 +12,11 @@ interface UserState {
userdata: any;
curXs: any;
token: string;
auth: string[],
ws: any,
wsCallback: any
auth: string[];
lastRefreshTime: number; // 上次刷新时间
refreshInterval: number; // 刷新间隔(毫秒)
ws: any;
wsCallback: any;
}
export const useUserStore = defineStore({
@ -28,6 +30,8 @@ export const useUserStore = defineStore({
token: '',
//用户注册信息
auth: [],
lastRefreshTime: 0, // 上次刷新时间
refreshInterval: 7 * 24 * 60 * 60 * 1000, // 刷新间隔(毫秒)
ws: null,
wsCallback: defWsCallback
}),
@ -43,6 +47,12 @@ export const useUserStore = defineStore({
},
getAuth(): string[] {
return this.auth;
},
getLastRefreshTime(): number {
return this.lastRefreshTime;
},
getRefreshInterval(): number {
return this.refreshInterval;
}
},
actions: {
@ -58,6 +68,22 @@ export const useUserStore = defineStore({
setAuth(data: string[]) {
this.auth = data;
},
setLastRefreshTime(time: number) {
this.lastRefreshTime = time;
},
setRefreshInterval(interval: number) {
this.refreshInterval = interval;
},
// 更新学生信息
updateStudentInfo(studentInfo: any) {
this.setCurXs(studentInfo);
},
// 更新学生列表
updateStudentList(studentList: any[]) {
if (this.userdata) {
this.userdata.xsList = studentList;
}
},
setWsCallback(callback: any) {
this.wsCallback = callback;
},