新苗调整

This commit is contained in:
hebo 2026-06-23 11:16:12 +08:00
parent 8bdde206b0
commit 2b1dad9e29
11 changed files with 675 additions and 303 deletions

View File

@ -55,12 +55,21 @@ export const dqpkApi = async () => {
}; };
/** /**
* * yfzc_pkkb/
*/ */
export const drpkkbApi = async (params: any) => { export const drpkkbApi = async (params: any) => {
return await get("/mobile/jz/pkkb/drpkkb", params); return await get("/mobile/jz/pkkb/drpkkb", params);
}; };
/** 某周每日排课课表(家长端:仅 yfzc_pkkb不叠加调换课/代课) */
export const drpkkbWeekApi = async (params: {
bjId: string;
xqId: string;
zc: number;
}) => {
return await get("/mobile/jz/pkkb/drpkkbWeek", params);
};
/** /**
* *
*/ */
@ -125,6 +134,13 @@ export const jzXsQjActivitiHistoryApi = async (params: any) => {
return await get("/activiti/history/historicFlow", params); return await get("/activiti/history/historicFlow", params);
}; };
/**
* findPage
*/
export const xqFindPageForSelectApi = async () => {
return await get("/api/xq/findPage", { page: 1, rows: 9999, status: "A" });
};
/** /**
* ID查询作品执行数据 * ID查询作品执行数据
*/ */
@ -134,6 +150,7 @@ export const zpzxFindByKcParamsApi = async (params: {
njmcId?: string; njmcId?: string;
bjId?: string; bjId?: string;
xsId?: string; xsId?: string;
xqId?: string;
}) => { }) => {
return await get("/api/zpzx/findByKcParams", params); return await get("/api/zpzx/findByKcParams", params);
}; };

View File

@ -65,12 +65,10 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, computed, onMounted, nextTick } from "vue"; import { ref, computed, onMounted, nextTick } from "vue";
import { dqpkApi, drpkkbApi } from "@/api/base/server"; import { dqpkApi, drpkkbWeekApi } from "@/api/base/server";
import { useUserStore } from "@/store/modules/user"; import { useUserStore } from "@/store/modules/user";
import { useCommonStore } from "@/store/modules/common";
const { getCurXs } = useUserStore(); const { getCurXs } = useUserStore();
const { getCacheList }= useCommonStore();
import dayjs from "dayjs"; import dayjs from "dayjs";
import "dayjs/locale/zh-cn"; import "dayjs/locale/zh-cn";
import weekOfYear from "dayjs/plugin/weekOfYear"; import weekOfYear from "dayjs/plugin/weekOfYear";
@ -80,122 +78,197 @@ dayjs.locale("zh-cn");
dayjs.extend(weekOfYear); dayjs.extend(weekOfYear);
dayjs.extend(isoWeek); dayjs.extend(isoWeek);
let dqZc = 0; let xqId = "";
let xqId = '';
//
const dnDjz = ref(dayjs().isoWeek()); const dnDjz = ref(dayjs().isoWeek());
//
const curZc = ref<any>({}); const curZc = ref<any>({});
// const curRqIndex = ref(0);
const curRqIndex = ref(0) const zcList = ref<any[]>([]);
//
const zcList = ref<any>([])
//
const rqList = computed(() => { const rqList = computed(() => {
if (!curZc.value || !curZc.value.drList || !curZc.value.drList.length) { if (!curZc.value?.drList?.length) {
return []; return [];
} }
return curZc.value.drList.filter((dr: any) => { return filterDrList(curZc.value.drList);
return (!dr.holiday && dr.zj <= 5) || (dr.holiday && dr.holiday.type != 2);
}); });
}); const sjList = ref<any[]>([]);
// /** 当前周整周课表缓存key 为 YYYY-MM-DD */
const sjList = ref<any>([]) const weekDaySjMap = ref<Record<string, any[]>>({});
const weekCacheZcDjz = ref<number | null>(null);
const isLoading = ref(false); const isLoading = ref(false);
//
const showWeekPicker = ref(false); const showWeekPicker = ref(false);
const weekPopup = ref<any>(null); const weekPopup = ref<any>(null);
const targetScrollTop = ref(0);
const targetScrollTop = ref(0); // scroll-top function filterDrList(drList: any[]) {
if (!drList?.length) {
return [];
}
return drList.filter((dr: any) => {
return (!dr.holiday && dr.zj <= 5) || (dr.holiday && dr.holiday.type != 2);
});
}
function dayRqToYmd(day: any): string {
if (!day) {
return "";
}
if (day.rq != null && day.rq !== "") {
return dayjs(day.rq).format("YYYY-MM-DD");
}
const rqNum = day.rqNum;
if (rqNum != null && rqNum !== "") {
const s = String(rqNum);
if (s.length === 8) {
return `${s.slice(0, 4)}-${s.slice(4, 6)}-${s.slice(6, 8)}`;
}
}
return "";
}
function isDateInWeekRawDrList(zc: any, ymd: string): boolean {
if (!ymd || !zc?.drList?.length) {
return false;
}
return (zc.drList as any[]).some((dr) => dayRqToYmd(dr) === ymd);
}
function findZcIndexByCalendarDate(list: any[], ymd: string): number {
for (let i = 0; i < list.length; i++) {
if (isDateInWeekRawDrList(list[i], ymd)) {
return i;
}
}
return -1;
}
function findRqListIndexForCalendarToday(drs: any[]): number {
const today = dayjs().format("YYYY-MM-DD");
return drs.findIndex((d) => dayRqToYmd(d) === today);
}
/**
* 定位当前周 + 今天
* 1. 按校历日期找含今天的周含周末不用过滤后的列表
* 2. 否则按 dnDjzISO 匹配当前周标签
* 3. 不用缓存里可能过期的 result.zc
* 4. 若今天在本周校历内但不在可展示日周末/放假 下一周第一个可展示日
*/
function resolveInitialWeekAndDayIndex(list: any[]): { zcIdx: number; dayIdx: number } {
if (!list.length) {
return { zcIdx: 0, dayIdx: 0 };
}
const today = dayjs().format("YYYY-MM-DD");
let zcIdx = findZcIndexByCalendarDate(list, today);
if (zcIdx < 0) {
const isoWeekNum = dayjs().isoWeek();
zcIdx = list.findIndex((z: any) => z.dnDjz === isoWeekNum);
}
if (zcIdx < 0) {
zcIdx = 0;
}
let drs = filterDrList(list[zcIdx]?.drList || []);
let dayIdx = findRqListIndexForCalendarToday(drs);
if (dayIdx < 0 && isDateInWeekRawDrList(list[zcIdx], today)) {
const nextZcIdx = zcIdx + 1;
if (nextZcIdx < list.length) {
zcIdx = nextZcIdx;
drs = filterDrList(list[zcIdx]?.drList || []);
}
dayIdx = 0;
} else if (dayIdx < 0) {
dayIdx = 0;
}
return { zcIdx, dayIdx };
}
async function loadWeekSchedule(zc: any) {
weekDaySjMap.value = {};
weekCacheZcDjz.value = null;
sjList.value = [];
const bjId = getCurXs?.bjId;
if (!bjId || !xqId || !zc?.djz) {
return;
}
isLoading.value = true;
try {
const res: any = await drpkkbWeekApi({
bjId: String(bjId),
xqId,
zc: zc.djz,
});
weekCacheZcDjz.value = zc.djz;
if (res?.resultCode === 1 && Array.isArray(res.result)) {
for (const day of res.result) {
const key = day.rq ? dayjs(day.rq).format("YYYY-MM-DD") : "";
if (key) {
weekDaySjMap.value[key] = Array.isArray(day.sjList) ? day.sjList : [];
}
}
}
} catch (error) {
console.error("加载周课表失败:", error);
uni.showToast({ title: "加载课表失败", icon: "none" });
} finally {
isLoading.value = false;
}
}
function applyDaySchedule(index: number) {
curRqIndex.value = index;
if (!rqList.value.length) {
sjList.value = [];
return;
}
const safeIdx = Math.max(0, Math.min(index, rqList.value.length - 1));
curRqIndex.value = safeIdx;
const rqKey = dayRqToYmd(rqList.value[safeIdx]);
sjList.value = weekDaySjMap.value[rqKey] || [];
}
//
const openWeekPicker = async () => { const openWeekPicker = async () => {
showWeekPicker.value = true; showWeekPicker.value = true;
//
await nextTick(); await nextTick();
if (weekPopup.value) { if (weekPopup.value) {
weekPopup.value.open("bottom"); weekPopup.value.open("bottom");
} else {
console.error("Week popup ref is not available.");
}
}
//
const closeWeekPicker = () => {
if (weekPopup.value) {
weekPopup.value.close();
} }
}; };
// const closeWeekPicker = () => {
weekPopup.value?.close();
};
const popupChange = (e: { show: boolean }) => { const popupChange = (e: { show: boolean }) => {
if (!e.show) { if (!e.show) {
showWeekPicker.value = false; showWeekPicker.value = false;
} }
}; };
// const selectWeek = async (zc: any) => {
const selectWeek = (zc: any) => { if (curZc.value?.djz === zc?.djz) {
if (curZc.value && zc && zc.djz === curZc.value.djz) {
//
closeWeekPicker(); closeWeekPicker();
return; return;
} }
//
Object.assign(curZc.value, zc); Object.assign(curZc.value, zc);
//
closeWeekPicker(); closeWeekPicker();
await loadWeekSchedule(zc);
// const drs = filterDrList(zc.drList || []);
selectDay(0); const todayIdx = findRqListIndexForCalendarToday(drs);
applyDaySchedule(todayIdx >= 0 ? todayIdx : 0);
}; };
//
const selectDay = (index: number) => { const selectDay = (index: number) => {
curRqIndex.value = index; if (weekCacheZcDjz.value !== curZc.value?.djz) {
if (!rqList.value.length) { void loadWeekSchedule(curZc.value).then(() => applyDaySchedule(index));
return; return;
} }
const params = { applyDaySchedule(index);
jsId: "",
bjId: getCurXs.bjId,
xqId: xqId,
zc: curZc.value.djz,
rq: rqList.value[index].rq, //
zj: rqList.value[index].zj,
txDate: null
};
if (params.zj > 5) {
const holiday = rqList.value[index].holiday || {};
if (holiday && holiday.type === 3 && holiday.txDate) {
params.txDate = holiday.txDate;
const txZc = rqList.value[index].txZc;
if (txZc) {
params.zc = txZc;
}
}
}
drpkkbApi(params).then(res => {
// result
if (res && res.resultCode === 1) {
sjList.value = res.result;
} else {
//
console.warn("检查报名状态接口返回错误:", res);
}
})
.catch((error) => {
//
console.error("调用检查报名状态接口失败:", error);
});
}; };
//
const getCourseColorClass = (subject: string | undefined): string => { const getCourseColorClass = (subject: string | undefined): string => {
if (!subject) return ""; if (!subject) return "";
if (subject.includes("语文")) return "color-lang"; if (subject.includes("语文")) return "color-lang";
@ -205,33 +278,23 @@ const getCourseColorClass = (subject: string | undefined): string => {
}; };
onMounted(async () => { onMounted(async () => {
// try {
const res = await getCacheList(dqpkApi, "dqPkSz"); // getCacheList result.zc
const result = res.result; const res: any = await dqpkApi();
dqZc = res.result.zc; const result = res?.result ?? res;
xqId = res.result.xq.id; if (!result?.xq?.id) {
zcList.value = result.zcList; return;
}
xqId = result.xq.id;
zcList.value = result.zcList || [];
sjList.value = []; sjList.value = [];
// const { zcIdx, dayIdx } = resolveInitialWeekAndDayIndex(zcList.value);
const today = dayjs(); Object.assign(curZc.value, zcList.value[zcIdx] || {});
const currentWeekday = today.day(); // 0-60 await loadWeekSchedule(curZc.value);
const currentWeekdayIndex = currentWeekday === 0 ? 6 : currentWeekday - 1; // 1-71 applyDaySchedule(dayIdx);
} catch (error) {
// console.error("初始化课表失败:", error);
let zc = zcList.value.find((item:any) => item.djz === dqZc);
if (!zc) {
zc = zcList.value[0];
}
//
selectWeek(zc);
//
if (rqList.value && rqList.value.length > 0) {
//
const targetIndex = Math.min(currentWeekdayIndex, rqList.value.length - 1);
selectDay(targetIndex);
} }
}); });
</script> </script>

View File

@ -10,7 +10,7 @@
<!-- 顶部蓝色背景与教师端一致 --> <!-- 顶部蓝色背景与教师端一致 -->
<view class="blue-header"> <view class="blue-header">
<view class="header-content"> <view class="header-content">
<text class="exam-title">{{ kscc.ksmc || '考试场次' }}</text> <text class="exam-title">{{ kscc.ksmc || '学情场次' }}</text>
<view class="student-info-section"> <view class="student-info-section">
<text class="student-name">{{ curXs.xm || curXs.name || '学生姓名' }}</text> <text class="student-name">{{ curXs.xm || curXs.name || '学生姓名' }}</text>
<text class="student-class" v-if="curXs.njmc || curXs.bjmc"> <text class="student-class" v-if="curXs.njmc || curXs.bjmc">
@ -45,11 +45,11 @@
:class="{ active: activeTab === 'scores' }" :class="{ active: activeTab === 'scores' }"
@click="switchTab('scores')" @click="switchTab('scores')"
> >
<text>学科成绩</text> <text>学科等级</text>
</view> </view>
</view> </view>
<!-- 学科成绩视图与教师端布局一致分数按 sfXsFs 显示 --> <!-- 学科等级视图与教师端布局一致分数按 sfXsFs 显示 -->
<view class="score-view" v-if="activeTab === 'scores'"> <view class="score-view" v-if="activeTab === 'scores'">
<view v-if="ksccKscjList.length > 0" class="score-content"> <view v-if="ksccKscjList.length > 0" class="score-content">
<view <view
@ -152,7 +152,7 @@ onLoad((options?: Record<string, string>) => {
} }
kscc.value = { kscc.value = {
id: kid, id: kid,
ksmc: name || "考试场次", ksmc: name || "学情场次",
}; };
if (q.njmcId != null && String(q.njmcId).trim()) { if (q.njmcId != null && String(q.njmcId).trim()) {
try { try {
@ -198,16 +198,16 @@ const radarData = {
// //
const sysKmList = ref<any>([]); const sysKmList = ref<any>([]);
// //
const ksccKmList = ref<any>([]); const ksccKmList = ref<any>([]);
// //
const totalKmFs = ref<number>(0); const totalKmFs = ref<number>(0);
// //
const kmDjList = ref<any>([]); const kmDjList = ref<any>([]);
// //
const djList = ref<any>([]); const djList = ref<any>([]);
// //
const ksccKscjList = ref<any>([]); const ksccKscjList = ref<any>([]);
// //
@ -361,7 +361,7 @@ function resolveOverallKsDj(sumScaled: number, examFullMarksScaled: number): any
return pickBestDjMatch(ksDjRows, scoreForMatch) || {}; return pickBestDjMatch(ksDjRows, scoreForMatch) || {};
} }
// kspj/detail // kspj/detail
const initKsdj = () => { const initKsdj = () => {
djList.value.forEach((ksdj: any) => { djList.value.forEach((ksdj: any) => {
ksdj.zdf = floatToInt(ksdj.zdf, fsScale); ksdj.zdf = floatToInt(ksdj.zdf, fsScale);
@ -417,7 +417,7 @@ onMounted(async () => {
} }
const ksccId = kscc.value?.id; const ksccId = kscc.value?.id;
if (!ksccId) { if (!ksccId) {
uni.showToast({ title: "请先选择考试场次", icon: "none" }); uni.showToast({ title: "请先选择学情场次", icon: "none" });
return; return;
} }
const res = await xsKscjApi({ const res = await xsKscjApi({
@ -640,7 +640,7 @@ onMounted(async () => {
} }
} }
/* 学科成绩视图(与教师端一致) */ /* 学科等级视图(与教师端一致) */
.score-view { .score-view {
margin-top: 15px; margin-top: 15px;
display: flex; display: flex;

View File

@ -1,9 +1,9 @@
<template> <template>
<view class="page"> <view class="page">
<view class="head-tip">请选择考试场次</view> <view class="head-tip">请选择学情场次</view>
<scroll-view scroll-y class="scroll"> <scroll-view scroll-y class="scroll">
<view v-if="loading" class="state">加载中...</view> <view v-if="loading" class="state">加载中...</view>
<view v-else-if="!treeData.length" class="state">暂无考试场次数据</view> <view v-else-if="!treeData.length" class="state">暂无学情场次数据</view>
<view v-else class="sections"> <view v-else class="sections">
<view v-for="xq in treeData" :key="xqKey(xq)" class="section"> <view v-for="xq in treeData" :key="xqKey(xq)" class="section">
<text class="section-title">{{ xq.title || xq.label || "学期" }}</text> <text class="section-title">{{ xq.title || xq.label || "学期" }}</text>
@ -16,7 +16,7 @@
<text class="kscc-name">{{ c.title || c.ksmc || "—" }}</text> <text class="kscc-name">{{ c.title || c.ksmc || "—" }}</text>
<text class="arrow"></text> <text class="arrow"></text>
</view> </view>
<view v-if="!(xq.children || []).length" class="kscc-empty">该学期下暂无场次</view> <view v-if="!(xq.children || []).length" class="kscc-empty">该学期下暂无学情场次</view>
</view> </view>
</view> </view>
</scroll-view> </scroll-view>
@ -68,7 +68,7 @@ function goDetail(xq: any, c: any) {
const xqTitle = String(xq?.title ?? xq?.label ?? "").trim(); const xqTitle = String(xq?.title ?? xq?.label ?? "").trim();
const ksTitle = String(c?.title ?? c?.ksmc ?? "").trim(); const ksTitle = String(c?.title ?? c?.ksmc ?? "").trim();
const idEnc = encodeURIComponent(String(ksccId)); const idEnc = encodeURIComponent(String(ksccId));
const ksEnc = encodeURIComponent(ksTitle || "考试场次"); const ksEnc = encodeURIComponent(ksTitle || "学情场次");
uni.navigateTo({ uni.navigateTo({
url: `/pages/base/grades/detail?ksccId=${idEnc}&ksmc=${ksEnc}`, url: `/pages/base/grades/detail?ksccId=${idEnc}&ksmc=${ksEnc}`,
}); });

View File

@ -67,15 +67,18 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted, watch, reactive } from "vue"; import { ref, computed, onMounted, watch, reactive } from "vue";
import { onShow } from "@dcloudio/uni-app";
import XsPicker from "@/pages/base/components/XsPicker/index.vue" import XsPicker from "@/pages/base/components/XsPicker/index.vue"
import { getNoticeListApi } from "@/api/base/notice"; import { getNoticeListApi } from "@/api/base/notice";
import { getMobileMenuApi } from "@/api/system/menu"; import { getMobileMenuApi } from "@/api/system/menu";
import { getPermissionChangeTimeApi } from "@/api/system/config";
import { useUserStore } from "@/store/modules/user"; import { useUserStore } from "@/store/modules/user";
import { useDataStore } from "@/store/modules/data"; import { useDataStore } from "@/store/modules/data";
import { useMenuStore } from "@/store/modules/menu"; import { useMenuStore } from "@/store/modules/menu";
import { type MobileMenuTreeNode } from "@/api/system/menu"; import { type MobileMenuTreeNode } from "@/api/system/menu";
import { PageUtils } from "@/utils/pageUtil"; import { PageUtils } from "@/utils/pageUtil";
import { useDebounce } from "@/utils/debounce"; import { useDebounce } from "@/utils/debounce";
import { resolveJzdUserId } from "@/utils/menuCache";
const userStore = useUserStore(); const userStore = useUserStore();
const { getCurXs } = userStore; const { getCurXs } = userStore;
@ -218,23 +221,38 @@ const getArticleList = async () => {
} }
}; };
onMounted(async () => { async function loadHomeMenu() {
setGlobal({ from: 'home' }); const userId = resolveJzdUserId(userStore.getUser);
if (!userId || !userStore.getToken) {
menuItems.value = [];
return;
}
if (menuStore.isMenuCacheForUser(userId)) {
menuItems.value = flattenMenuToItems(menuStore.getMobileMenu);
return;
}
// launchPage menuStore
const cachedMenu = menuStore.getMobileMenu;
if (cachedMenu?.length > 0) {
menuItems.value = flattenMenuToItems(cachedMenu);
} else if (userStore.getToken) {
// launchPage token
try { try {
const timeRes = await getPermissionChangeTimeApi();
const serverChangeTime = String((timeRes as any)?.result ?? "");
if (serverChangeTime) userStore.setChangeTime(serverChangeTime);
const r = await getMobileMenuApi(); const r = await getMobileMenuApi();
if (r?.result && Array.isArray(r.result) && r.result.length > 0) { if (r?.result && Array.isArray(r.result) && r.result.length > 0) {
menuStore.setMobileMenu(r.result); menuStore.setMobileMenu(r.result, userId);
menuItems.value = flattenMenuToItems(r.result); menuItems.value = flattenMenuToItems(r.result);
} else {
menuItems.value = [];
} }
} catch (_e) {} } catch (_e) {
menuItems.value = [];
} }
}
onMounted(async () => {
setGlobal({ from: 'home' });
await loadHomeMenu();
// //
if (curXs.value && curXs.value.njmcId) { if (curXs.value && curXs.value.njmcId) {
@ -250,6 +268,13 @@ onMounted(async () => {
await checkSubscribeStatus(); await checkSubscribeStatus();
}); });
onShow(() => {
const userId = resolveJzdUserId(userStore.getUser);
if (!menuStore.isMenuCacheForUser(userId)) {
void loadHomeMenu();
}
});
// //
const checkSubscribeStatus = async () => { const checkSubscribeStatus = async () => {
const userInfo = userStore.getUser; const userInfo = userStore.getUser;

View File

@ -16,7 +16,7 @@
<view class="banner-title-wrapper"> <view class="banner-title-wrapper">
<text class="banner-icon">{{ activeTab === 0 ? '📝' : '📚' }}</text> <text class="banner-icon">{{ activeTab === 0 ? '📝' : '📚' }}</text>
<view class="banner-text"> <view class="banner-text">
<text class="banner-title">{{ activeTab === 0 ? '作品任务' : '作品资源' }}</text> <text class="banner-title">{{ activeTab === 0 ? '作品任务' : '课程资源' }}</text>
<text class="banner-subtitle">{{ <text class="banner-subtitle">{{
activeTab === 0 ? '独立项目选择 · 学习成长' : '课程资料 · 在线查看' activeTab === 0 ? '独立项目选择 · 学习成长' : '课程资料 · 在线查看'
}}</text> }}</text>
@ -29,18 +29,45 @@
</view> </view>
</view> </view>
<view class="header-panel">
<view class="filter-bar">
<view class="filter-item">
<uni-data-select
v-model="searchForm.xnmc"
:localdata="xnmcList"
placeholder="选择学年"
@change="handleXnChange"
class="filter-select"
/>
</view>
<view class="filter-item">
<uni-data-select
v-model="searchForm.xqId"
:localdata="xqList"
placeholder="选择学期"
@change="handleXqChange"
class="filter-select"
/>
</view>
</view>
<view class="tab-bar"> <view class="tab-bar">
<view class="tab-item" :class="{ active: activeTab === 0 }" @click="activeTab = 0"> <view class="tab-item" :class="{ active: activeTab === 0 }" @click="activeTab = 0">
<text class="tab-text">作品任务</text> <text class="tab-text">作品任务</text>
</view> </view>
<view class="tab-item" :class="{ active: activeTab === 1 }" @click="activeTab = 1"> <view class="tab-item" :class="{ active: activeTab === 1 }" @click="activeTab = 1">
<text class="tab-text">作品资源</text> <text class="tab-text">课程资源</text>
</view>
</view> </view>
</view> </view>
<!-- 任务列表 --> <!-- 任务列表 -->
<scroll-view scroll-y class="list-scroll-view"> <scroll-view scroll-y class="list-scroll-view" :enable-back-to-top="true">
<view v-if="isLoading && taskList.length === 0" class="loading-indicator"> <view v-if="!xqIdsForApi" class="empty-state empty-state--filter">
<view class="empty-icon">📅</view>
<text class="empty-text">请先选择学年并选择具体学期或全部学期</text>
</view>
<view v-else-if="isLoading && taskList.length === 0" class="loading-indicator">
<view class="loading-spinner"></view> <view class="loading-spinner"></view>
<text class="loading-text">加载中...</text> <text class="loading-text">加载中...</text>
</view> </view>
@ -97,7 +124,7 @@
<view v-else class="empty-state"> <view v-else class="empty-state">
<view class="empty-icon">📭</view> <view class="empty-icon">📭</view>
<text class="empty-text">{{ activeTab === 0 ? '暂无作品任务' : '暂无作品资源' }}</text> <text class="empty-text">{{ activeTab === 0 ? '暂无作品任务' : '暂无课程资源' }}</text>
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
@ -105,12 +132,41 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed, onUnmounted } from "vue"; import { ref, reactive, computed, onMounted, onUnmounted } from "vue";
import { onShow, onLoad } from "@dcloudio/uni-app"; import { onShow, onLoad } from "@dcloudio/uni-app";
import { useUserStore } from "@/store/modules/user"; import { useUserStore } from "@/store/modules/user";
import { useCommonStore } from "@/store/modules/common";
import { zpzxFindByKcParamsApi } from "@/api/base/server"; import { zpzxFindByKcParamsApi } from "@/api/base/server";
import { getPersistedDqXqAndZc } from "@/utils/xszpPersistXq";
const userStore = useUserStore(); const userStore = useUserStore();
const commonStore = useCommonStore();
const searchForm = reactive({
xnmc: "",
xqId: "",
});
const xnmcList = ref<Array<{ value: string; text: string }>>([]);
const allXqList = ref<Array<{ id: string; xqmc: string; xnmc: string }>>([]);
const xqList = ref<Array<{ value: string; text: string }>>([]);
const currentXqId = ref("");
const xqListReady = ref(false);
/** 传给接口的学期 id单选为单个 id选「全部学期」为当前学年下所有学期 id 逗号拼接 */
const xqIdsForApi = computed(() => {
if (searchForm.xqId) {
return searchForm.xqId;
}
const year = searchForm.xnmc;
if (!year) {
return "";
}
return allXqList.value
.filter((it) => it.xnmc === year)
.map((it) => String(it.id))
.filter(Boolean)
.join(",");
});
interface TaskItem { interface TaskItem {
id: string; id: string;
@ -190,18 +246,101 @@ onLoad(async (options) => {
// openId onShow isLoginReady false onLoad // openId onShow isLoginReady false onLoad
// onShow onLoad // onShow onLoad
if (options && options.openId) { if (options && options.openId && xqListReady.value) {
loadTaskList();
}
});
onMounted(async () => {
await loadXqList();
xqListReady.value = true;
if (isLoginReady.value) {
loadTaskList(); loadTaskList();
} }
}); });
onShow(() => { onShow(() => {
// //
if (isLoginReady.value) { if (isLoginReady.value && xqListReady.value) {
loadTaskList(); loadTaskList();
} }
}); });
function handleXnChange() {
refreshXqListByYear();
loadTaskList();
}
function handleXqChange() {
loadTaskList();
}
function refreshXqListByYear() {
const year = searchForm.xnmc;
const list = allXqList.value
.filter((item) => item.xnmc === year)
.map((item) => ({ value: item.id, text: item.xqmc }));
xqList.value = [{ value: "", text: "全部学期" }, ...list];
const curId = currentXqId.value;
if (curId && list.some((x) => String(x.value) === String(curId))) {
searchForm.xqId = curId;
} else {
searchForm.xqId = "";
}
}
const loadXqList = async () => {
try {
const response: any = await commonStore.getXqListForSelect();
const data =
response?.rows ||
response?.result ||
response?.data ||
(Array.isArray(response) ? response : []);
const dq = await getPersistedDqXqAndZc(commonStore);
const dqXqId = dq?.xqId != null ? String(dq.xqId) : "";
const cur =
data.find((item: any) => String(item?.id) === dqXqId) ||
data.find((item: any) => String(item?.dqxq) === "是");
currentXqId.value = cur?.id != null ? String(cur.id) : "";
const curXnmc = cur?.xnmc != null ? String(cur.xnmc).trim() : "";
allXqList.value = data.map((item: any) => ({
id: item.id,
xqmc: item.xqmc || item.name,
xnmc: item.xnmc || "",
}));
const yearSet = new Set<string>();
allXqList.value.forEach((item) => {
if (item.xnmc) yearSet.add(item.xnmc);
});
xnmcList.value = Array.from(yearSet).map((y) => ({ value: y, text: y }));
if (xnmcList.value.length > 0) {
if (curXnmc && xnmcList.value.some((x) => x.value === curXnmc)) {
searchForm.xnmc = curXnmc;
} else {
searchForm.xnmc = xnmcList.value[0].value;
}
refreshXqListByYear();
} else {
xqList.value = allXqList.value.map((item) => ({
value: item.id,
text: item.xqmc,
}));
if (currentXqId.value) searchForm.xqId = currentXqId.value;
else if (xqList.value.length)
searchForm.xqId = String(xqList.value[0].value);
}
} catch (e) {
console.error(e);
xnmcList.value = [];
allXqList.value = [];
xqList.value = [];
currentXqId.value = "";
}
};
// //
uni.$on('refreshTaskList', () => { uni.$on('refreshTaskList', () => {
console.log('收到刷新任务列表事件'); console.log('收到刷新任务列表事件');
@ -215,7 +354,11 @@ onUnmounted(() => {
const loadTaskList = async () => { const loadTaskList = async () => {
// //
if (!isLoginReady.value) { if (!isLoginReady.value || !xqListReady.value) {
return;
}
if (!xqIdsForApi.value) {
taskList.value = [];
return; return;
} }
@ -239,6 +382,9 @@ const loadTaskList = async () => {
if (kcId.value) { if (kcId.value) {
params.kcId = kcId.value; params.kcId = kcId.value;
} }
if (xqIdsForApi.value) {
params.xqId = xqIdsForApi.value;
}
const response = await zpzxFindByKcParamsApi(params); const response = await zpzxFindByKcParamsApi(params);
@ -320,7 +466,7 @@ const viewTaskDetail = (task: TaskItem) => {
}); });
}; };
/** 作品资源(资料查看)详情 */ /** 课程资源(资料查看)详情 */
const viewResourceDetail = (task: TaskItem) => { const viewResourceDetail = (task: TaskItem) => {
const params = new URLSearchParams(); const params = new URLSearchParams();
params.append('zpId', task.zpId); params.append('zpId', task.zpId);
@ -384,14 +530,16 @@ const formatTime = (timeStr?: string) => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
background: linear-gradient(180deg, #f0f5ff 0%, #f5f7fa 100%); min-height: 100vh;
background: #eef2f8;
box-sizing: border-box;
} }
//
.page-banner { .page-banner {
background: linear-gradient(135deg, #4e73df 0%, #2e59d9 100%); flex-shrink: 0;
padding: 20px 16px; background: linear-gradient(145deg, #3d5fc4 0%, #2e59d9 55%, #2547b8 100%);
box-shadow: 0 4px 12px rgba(78, 115, 223, 0.3); padding: 18px 16px 16px;
box-shadow: 0 4px 12px rgba(46, 89, 217, 0.2);
.banner-content { .banner-content {
display: flex; display: flex;
@ -402,89 +550,137 @@ const formatTime = (timeStr?: string) => {
.banner-title-wrapper { .banner-title-wrapper {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: 10px;
flex: 1; flex: 1;
min-width: 0;
} }
.banner-icon { .banner-icon {
font-size: 32px; font-size: 28px;
line-height: 1; line-height: 1;
flex-shrink: 0;
} }
.banner-text { .banner-text {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 4px; gap: 4px;
min-width: 0;
} }
.banner-title { .banner-title {
font-size: 24px; font-size: 20px;
font-weight: bold; font-weight: 700;
color: #ffffff; color: #ffffff;
line-height: 1.2; line-height: 1.25;
} }
.banner-subtitle { .banner-subtitle {
font-size: 13px; font-size: 12px;
color: rgba(255, 255, 255, 0.9); color: rgba(255, 255, 255, 0.88);
letter-spacing: 1px; line-height: 1.4;
} }
.task-count { .task-count {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
background: rgba(255, 255, 255, 0.2); flex-shrink: 0;
backdrop-filter: blur(10px); background: rgba(255, 255, 255, 0.18);
padding: 10px 16px; border: 1px solid rgba(255, 255, 255, 0.25);
border-radius: 12px; padding: 8px 14px;
min-width: 60px; border-radius: 14px;
min-width: 56px;
} }
.count-number { .count-number {
font-size: 24px; font-size: 22px;
font-weight: bold; font-weight: 700;
color: #ffffff; color: #ffffff;
line-height: 1; line-height: 1;
} }
.count-label { .count-label {
font-size: 11px; font-size: 11px;
color: rgba(255, 255, 255, 0.9); color: rgba(255, 255, 255, 0.92);
margin-top: 4px; margin-top: 4px;
} }
} }
.header-panel {
flex-shrink: 0;
width: 100%;
margin: 0;
padding: 12px 12px 10px;
background: #ffffff;
border-radius: 0;
box-shadow: none;
border-bottom: 1px solid #e9ecef;
box-sizing: border-box;
}
.filter-bar {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #eef1f6;
}
.filter-item {
flex: 1;
min-width: 0;
}
.filter-select {
width: 100%;
}
.filter-item :deep(.uni-stat__select) {
flex: 1;
min-width: 0;
}
.filter-item :deep(.uni-select) {
border: 1px solid #e2e8f0 !important;
border-radius: 10px !important;
background: #f8fafc !important;
min-height: 36px;
}
.filter-item :deep(.uni-select__input-text) {
font-size: 13px !important;
color: #334155 !important;
}
.tab-bar { .tab-bar {
display: flex; display: flex;
background: #ffffff; background: #f1f5f9;
margin: 0 16px; border-radius: 10px;
margin-top: -10px; padding: 3px;
margin-bottom: 12px; gap: 3px;
border-radius: 12px;
padding: 4px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
position: relative;
z-index: 2;
} }
.tab-item { .tab-item {
flex: 1; flex: 1;
text-align: center; text-align: center;
padding: 10px 0; padding: 9px 0;
border-radius: 10px; border-radius: 9px;
transition: background 0.2s ease, color 0.2s ease; transition: background 0.2s ease, box-shadow 0.2s ease;
.tab-text { .tab-text {
font-size: 15px; font-size: 14px;
color: #666; color: #64748b;
font-weight: 500;
} }
&.active { &.active {
background: linear-gradient(135deg, #4e73df 0%, #2e59d9 100%); background: #ffffff;
box-shadow: 0 2px 8px rgba(46, 89, 217, 0.15);
.tab-text { .tab-text {
color: #ffffff; color: #2e59d9;
font-weight: 600; font-weight: 600;
} }
} }
@ -492,7 +688,10 @@ const formatTime = (timeStr?: string) => {
.list-scroll-view { .list-scroll-view {
flex: 1; flex: 1;
padding: 0; min-height: 0;
width: 100%;
padding: 8px 12px 16px;
box-sizing: border-box;
} }
.loading-indicator { .loading-indicator {
@ -500,21 +699,21 @@ const formatTime = (timeStr?: string) => {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 40px 0; padding: 48px 0;
.loading-spinner { .loading-spinner {
width: 40px; width: 36px;
height: 40px; height: 36px;
border: 3px solid #f3f3f3; border: 3px solid #e2e8f0;
border-top: 3px solid #409eff; border-top: 3px solid #4e73df;
border-radius: 50%; border-radius: 50%;
animation: spin 1s linear infinite; animation: spin 1s linear infinite;
margin-bottom: 10px; margin-bottom: 10px;
} }
.loading-text { .loading-text {
color: #999; color: #94a3b8;
font-size: 14px; font-size: 13px;
} }
} }
@ -524,26 +723,23 @@ const formatTime = (timeStr?: string) => {
} }
.task-card { .task-card {
background: linear-gradient(135deg, #ffffff 0%, #fafbfc 100%); background: #ffffff;
border-radius: 16px; border-radius: 14px;
margin: 0 0 16px 0; margin: 0 0 12px;
padding: 16px; padding: 14px 16px;
box-shadow: border: 1px solid #e8edf5;
0 2px 16px rgba(0, 0, 0, 0.08), box-shadow: 0 4px 14px rgba(15, 23, 42, 0.06);
0 0 0 1px rgba(78, 115, 223, 0.1); transition: transform 0.2s ease, box-shadow 0.2s ease;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative; position: relative;
overflow: visible; overflow: hidden;
&--resource { &--resource {
cursor: pointer; cursor: pointer;
} }
&:active { &:active {
transform: translateY(2px) scale(0.98); transform: scale(0.99);
box-shadow: box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
0 1px 8px rgba(0, 0, 0, 0.12),
0 0 0 1px rgba(78, 115, 223, 0.15);
} }
} }
@ -565,9 +761,9 @@ const formatTime = (timeStr?: string) => {
} }
.task-title { .task-title {
font-size: 16px; font-size: 15px;
font-weight: 600; font-weight: 600;
color: #333; color: #1e293b;
flex: 1; flex: 1;
} }
@ -634,39 +830,37 @@ const formatTime = (timeStr?: string) => {
.card-footer { .card-footer {
padding-top: 12px; padding-top: 12px;
border-top: 1px solid #f0f0f0; border-top: 1px solid #f1f5f9;
display: flex; display: flex;
gap: 12px; gap: 10px;
justify-content: space-between; justify-content: space-between;
.action-btn { .action-btn {
flex: 1; flex: 1;
height: 36px; height: 34px;
line-height: 36px; line-height: 34px;
border-radius: 8px; border-radius: 10px;
font-size: 14px; font-size: 13px;
font-weight: 500; font-weight: 500;
border: none; border: none;
transition: all 0.3s; transition: opacity 0.2s;
&.challenge-btn { &.challenge-btn {
background: linear-gradient(135deg, #4e73df 0%, #2e59d9 100%); background: linear-gradient(135deg, #4e73df 0%, #2e59d9 100%);
color: #ffffff; color: #ffffff;
&:active { &:active {
background: linear-gradient(135deg, #2e59d9 0%, #1e3fa8 100%); opacity: 0.88;
transform: scale(0.98);
} }
} }
&.detail-btn { &.detail-btn {
background: #f5f7fa; background: #f1f5f9;
color: #4e73df; color: #2e59d9;
border: 1px solid #e0e6ed; border: 1px solid #e2e8f0;
&:active { &:active {
background: #e8ecf1; opacity: 0.88;
transform: scale(0.98);
} }
} }
} }
@ -677,18 +871,29 @@ const formatTime = (timeStr?: string) => {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 80px 20px; padding: 56px 20px;
background: #ffffff;
border-radius: 14px;
border: 1px dashed #dbe3ef;
&--filter {
padding: 40px 20px;
background: #f8fafc;
border-color: #e2e8f0;
}
.empty-icon { .empty-icon {
font-size: 64px; font-size: 48px;
margin-bottom: 16px; margin-bottom: 12px;
opacity: 0.6; opacity: 0.75;
} }
.empty-text { .empty-text {
font-size: 16px; font-size: 14px;
color: #4a5568; color: #64748b;
font-weight: 600; font-weight: 500;
text-align: center;
line-height: 1.5;
} }
} }

View File

@ -20,6 +20,7 @@ import { checkOpenId } from "@/api/system/login";
import { getMobileMenuApi } from "@/api/system/menu"; import { getMobileMenuApi } from "@/api/system/menu";
import { getPermissionChangeTimeApi } from "@/api/system/config"; import { getPermissionChangeTimeApi } from "@/api/system/config";
import { PageUtils } from "@/utils/pageUtil"; import { PageUtils } from "@/utils/pageUtil";
import { resolveJzdUserId } from "@/utils/menuCache";
const dataStore = useDataStore(); const dataStore = useDataStore();
const { setGlobal } = dataStore; const { setGlobal } = dataStore;
@ -33,6 +34,37 @@ const toLogin = () => {
}); });
}; };
async function syncMobileMenu(userId: string, savedChangeTime: string) {
if (!userId) return;
let needFetchMenu = true;
let serverChangeTime = "";
try {
const timeRes = await getPermissionChangeTimeApi();
serverChangeTime = String((timeRes as any)?.result ?? "");
needFetchMenu = menuStore.shouldRefreshMenu(
userId,
serverChangeTime,
savedChangeTime
);
if (serverChangeTime) userStore.setChangeTime(serverChangeTime);
} catch (_e) {
needFetchMenu = true;
}
if (!needFetchMenu && menuStore.isMenuCacheForUser(userId)) {
return;
}
try {
const r = await getMobileMenuApi();
if (r?.result && Array.isArray(r.result) && r.result.length > 0) {
menuStore.setMobileMenu(r.result, userId);
}
} catch (_e) {}
}
// //
const initGlobalData = (data: any) => { const initGlobalData = (data: any) => {
let gData = { ...(data || {}) }; let gData = { ...(data || {}) };
@ -71,36 +103,11 @@ const initGlobalData = (data: any) => {
onLoad(async (data: any) => { onLoad(async (data: any) => {
const gData = initGlobalData(data); const gData = initGlobalData(data);
// // + userId
if (gData.fromLogin === "1") { if (gData.fromLogin === "1") {
let needFetchMenu = true; const userId = resolveJzdUserId(userStore.getUser);
try { const savedChangeTime = userStore.getChangeTime || "";
const timeRes = await getPermissionChangeTimeApi(); await syncMobileMenu(userId, savedChangeTime);
const serverChangeTime = String((timeRes as any)?.result ?? "");
const localChangeTime = userStore.getChangeTime || "";
const localMenu = menuStore.getMobileMenu || [];
needFetchMenu =
serverChangeTime !== localChangeTime || !localMenu?.length;
console.log("[launchPage] fromLogin", {
timeResRaw: JSON.stringify(timeRes),
serverChangeTime,
localChangeTime,
localMenuLength: localMenu?.length ?? 0,
needFetchMenu,
});
if (serverChangeTime) userStore.setChangeTime(serverChangeTime);
} catch (_e) {
needFetchMenu = true;
console.warn("[launchPage] fromLogin getPermissionChangeTime 异常", _e);
}
if (needFetchMenu) {
try {
const r = await getMobileMenuApi();
if (r?.result && Array.isArray(r.result) && r.result.length > 0) {
menuStore.setMobileMenu(r.result);
}
} catch (_e) {}
}
PageUtils.toHome(gData.lxId, gData.action); PageUtils.toHome(gData.lxId, gData.action);
return; return;
} }
@ -110,40 +117,37 @@ onLoad(async (data: any) => {
const res = await checkOpenId({ openId: gData.openId, appCode: "JZ" }); const res = await checkOpenId({ openId: gData.openId, appCode: "JZ" });
if (res.resultCode == 1 && res.result) { if (res.resultCode == 1 && res.result) {
// afterLoginAction afterLoginAction logout changeTime menuStore const loginUserId = resolveJzdUserId(res.result);
const savedChangeTime = userStore.getChangeTime || ""; const savedChangeTime = userStore.getChangeTime || "";
const savedMenu = menuStore.getMobileMenu || []; // H5 token
const canRestoreCache = menuStore.isMenuCacheForUser(loginUserId);
const savedMenu = canRestoreCache ? [...menuStore.getMobileMenu] : [];
userStore.afterLoginAction(res.result); userStore.afterLoginAction(res.result);
//
let needFetchMenu = true; let needFetchMenu = true;
try { try {
const timeRes = await getPermissionChangeTimeApi(); const timeRes = await getPermissionChangeTimeApi();
const serverChangeTime = String((timeRes as any)?.result ?? ""); const serverChangeTime = String((timeRes as any)?.result ?? "");
needFetchMenu = needFetchMenu = menuStore.shouldRefreshMenu(
serverChangeTime !== savedChangeTime || !savedMenu?.length; loginUserId,
console.log("[launchPage] openId", {
timeResRaw: JSON.stringify(timeRes),
serverChangeTime, serverChangeTime,
savedChangeTime, savedChangeTime
savedMenuLength: savedMenu?.length ?? 0, );
needFetchMenu,
});
if (serverChangeTime) userStore.setChangeTime(serverChangeTime); if (serverChangeTime) userStore.setChangeTime(serverChangeTime);
} catch (_e) { } catch (_e) {
needFetchMenu = true; needFetchMenu = true;
console.warn("[launchPage] openId getPermissionChangeTime 异常", _e);
} }
if (needFetchMenu) { if (needFetchMenu) {
try { try {
const r = await getMobileMenuApi(); const r = await getMobileMenuApi();
if (r?.result && Array.isArray(r.result) && r.result.length > 0) { if (r?.result && Array.isArray(r.result) && r.result.length > 0) {
menuStore.setMobileMenu(r.result); menuStore.setMobileMenu(r.result, loginUserId);
} }
} catch (_e) {} } catch (_e) {}
} else { } else if (savedMenu.length > 0) {
menuStore.setMobileMenu(savedMenu); // logout menuStore.setMobileMenu(savedMenu, loginUserId);
} }
PageUtils.toHome(gData.lxId, gData.action); PageUtils.toHome(gData.lxId, gData.action);

View File

@ -1,7 +1,7 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { jsFindByIdApi } from "@/api/base/jsApi"; import { jsFindByIdApi } from "@/api/base/jsApi";
import { xkkclxFindAllApi, getXkkcDetailByIdApi } from "@/api/base/xkApi"; import { xkkclxFindAllApi, getXkkcDetailByIdApi } from "@/api/base/xkApi";
import { clear } from "console"; import { xqFindPageForSelectApi, gzlGetDqXqAndZcApi } from "@/api/base/server";
/** /**
* *
@ -155,6 +155,20 @@ export const useCommonStore = defineStore({
} }
return Promise.resolve(this.data.xkkc[id]); return Promise.resolve(this.data.xkkc[id]);
}, },
/** 学期列表findPage供下拉选择Pinia 持久化缓存) */
async getXqListForSelect(): Promise<any> {
return this.getCacheList(xqFindPageForSelectApi, "xqFindPage");
},
/** 当前学期与周次Pinia 持久化,供学年学期筛选默认项) */
async getDqXqAndZc(): Promise<any> {
if (!this.data.dqXqAndZc) {
const res = await gzlGetDqXqAndZcApi();
if (res && res.resultCode === 1) {
this.data.dqXqAndZc = res.result;
}
}
return Promise.resolve(this.data.dqXqAndZc);
},
}, },
persist: { persist: {
enabled: true, enabled: true,

View File

@ -6,24 +6,53 @@ export const useMenuStore = defineStore({
state: () => ({ state: () => ({
/** 树形菜单数据,持久化到 localStorage key: app-Menu-jzd家长端与教师端 app-Menu-jsd 区分,避免同域下菜单互相覆盖) */ /** 树形菜单数据,持久化到 localStorage key: app-Menu-jzd家长端与教师端 app-Menu-jsd 区分,避免同域下菜单互相覆盖) */
mobileMenu: [] as MobileMenuTreeNode[], mobileMenu: [] as MobileMenuTreeNode[],
/** 菜单所属用户 ID换账号时与当前用户不一致则丢弃缓存 */
menuUserId: "" as string,
}), }),
getters: { getters: {
getMobileMenu(): MobileMenuTreeNode[] { getMobileMenu(): MobileMenuTreeNode[] {
return this.mobileMenu || []; return this.mobileMenu || [];
}, },
getMenuUserId(): string {
return this.menuUserId || "";
},
}, },
actions: { actions: {
/** 清空菜单 */ /** 清空菜单 */
clearMenu() { clearMenu() {
this.mobileMenu = []; this.mobileMenu = [];
this.menuUserId = "";
}, },
/** /**
* *
* @param menu * @param menu
* @param userId ID
*/ */
setMobileMenu(menu: MobileMenuTreeNode[]) { setMobileMenu(menu: MobileMenuTreeNode[], userId?: string | number) {
this.clearMenu(); this.clearMenu();
this.mobileMenu = menu && Array.isArray(menu) ? [...menu] : []; this.mobileMenu = menu && Array.isArray(menu) ? [...menu] : [];
if (userId != null && userId !== "") {
this.menuUserId = String(userId);
}
},
/** 缓存是否属于指定用户且非空 */
isMenuCacheForUser(userId: string | number | undefined | null): boolean {
if (userId == null || userId === "" || !this.menuUserId || !this.mobileMenu?.length) {
return false;
}
return String(userId) === String(this.menuUserId);
},
/**
*
*/
shouldRefreshMenu(
userId: string | number | undefined | null,
serverChangeTime: string,
localChangeTime: string
): boolean {
if (!this.isMenuCacheForUser(userId)) return true;
if (serverChangeTime && serverChangeTime !== (localChangeTime || "")) return true;
return false;
}, },
}, },
persist: { persist: {

7
src/utils/menuCache.ts Normal file
View File

@ -0,0 +1,7 @@
/** 从家长端登录用户信息解析 userId */
export function resolveJzdUserId(user: unknown): string {
if (!user || typeof user !== "object") return "";
const u = user as Record<string, unknown>;
const id = u.userId ?? u.id;
return id != null && id !== "" ? String(id) : "";
}

View File

@ -0,0 +1,8 @@
import type { useCommonStore } from "@/store/modules/common";
type CommonStore = ReturnType<typeof useCommonStore>;
/** Pinia 持久化common.data.dqXqAndZc */
export async function getPersistedDqXqAndZc(store: CommonStore): Promise<any> {
return (await store.getDqXqAndZc()) ?? {};
}