SAAS模式调整
This commit is contained in:
parent
0562efd6e8
commit
73515ab6a5
@ -3,6 +3,9 @@ import {onHide, onLaunch, onShow} from "@dcloudio/uni-app";
|
||||
|
||||
onLaunch(() => {
|
||||
console.log("App Launch");
|
||||
// #ifdef H5
|
||||
document.title = "数字校园";
|
||||
// #endif
|
||||
});
|
||||
onShow(() => {
|
||||
console.log("App Show");
|
||||
|
||||
@ -119,13 +119,6 @@ export const jzXsQjActivitiHistoryApi = async (params: any) => {
|
||||
return await get("/activiti/history/historicFlow", params);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取用户最新信息(包含学生信息)
|
||||
*/
|
||||
export const getUserLatestInfoApi = async () => {
|
||||
return await get("/open/login/getLatestInfo");
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据课程ID查询作品执行数据(家长端)
|
||||
*/
|
||||
|
||||
16
src/api/system/config/index.ts
Normal file
16
src/api/system/config/index.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { get } from "@/utils/request";
|
||||
|
||||
/** 短时缓存,避免 launchPage 连续跳转时重复调用 */
|
||||
let _changeTimeCache: { promise: Promise<any>; ts: number } | null = null;
|
||||
const CACHE_MS = 10000; // 10 秒内复用
|
||||
|
||||
/** 获取权限变更时间戳,用于判断本地缓存的菜单是否需重新拉取 */
|
||||
export const getPermissionChangeTimeApi = () => {
|
||||
const now = Date.now();
|
||||
if (_changeTimeCache && now - _changeTimeCache.ts < CACHE_MS) {
|
||||
return _changeTimeCache.promise;
|
||||
}
|
||||
const promise = get("/api/comConfig/getPermissionChangeTime");
|
||||
_changeTimeCache = { promise, ts: now };
|
||||
return promise;
|
||||
};
|
||||
@ -44,11 +44,6 @@ export const weChatLogin = async (param: any) => {
|
||||
return await get("/userlogin/weloginByCode", param);
|
||||
};
|
||||
|
||||
//获取用户按钮权限
|
||||
export const authenticationApi = async (param: { userId: string }) => {
|
||||
return await get("/api/authentication/find-by-user", param);
|
||||
};
|
||||
|
||||
//获取公众号票据
|
||||
export const wxConfigApi = async (param: any) => {
|
||||
return await post("/userlogin/wxConfig", param);
|
||||
|
||||
17
src/api/system/menu.ts
Normal file
17
src/api/system/menu.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { get } from "@/utils/request";
|
||||
|
||||
/** 手机端菜单树节点 */
|
||||
export interface MobileMenuTreeNode {
|
||||
id: number;
|
||||
parentId: number | null;
|
||||
screenName: string;
|
||||
normalCss?: string;
|
||||
pagePath?: string;
|
||||
sortNum: number;
|
||||
authCode?: string;
|
||||
children: MobileMenuTreeNode[];
|
||||
}
|
||||
|
||||
/** 获取手机端菜单(家长端) */
|
||||
export const getMobileMenuApi = () =>
|
||||
get<{ result: MobileMenuTreeNode[] }>("/api/screen/find-mobile-menu");
|
||||
@ -24,7 +24,7 @@
|
||||
<view class="title-line"></view>
|
||||
</view>
|
||||
<view class="grid-menu">
|
||||
<view v-for="(item, index) in menuItems" :key="index" v-show="hasPermissionDirect(item.permissionKey)"
|
||||
<view v-for="(item, index) in menuItems" :key="item.id ?? index"
|
||||
class="grid-item" @click="handleMenuClick(item)">
|
||||
<view class="grid-icon-container">
|
||||
<view class="icon-background"></view>
|
||||
@ -66,195 +66,76 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, watch } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import { ref, computed, onMounted, watch, reactive } from "vue";
|
||||
import XsPicker from "@/pages/base/components/XsPicker/index.vue"
|
||||
import { cmsArticlePageApi, getUserLatestInfoApi } from "@/api/base/server";
|
||||
import { getNoticeListApi } from "@/api/base/notice";
|
||||
import { getMobileMenuApi } from "@/api/system/menu";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
import { hasPermission } from "@/utils/permission";
|
||||
import { useMenuStore } from "@/store/modules/menu";
|
||||
import { type MobileMenuTreeNode } from "@/api/system/menu";
|
||||
import { PageUtils } from "@/utils/pageUtil";
|
||||
import { useDebounce } from "@/utils/debounce";
|
||||
|
||||
const { getCurXs } = useUserStore();
|
||||
const { setData, getAppCode, setGlobal } = useDataStore();
|
||||
const userStore = useUserStore();
|
||||
const { getCurXs } = userStore;
|
||||
const dataStore = useDataStore();
|
||||
const menuStore = useMenuStore();
|
||||
const { setData, getAppCode, setGlobal } = dataStore;
|
||||
const { debounce } = useDebounce(2000);
|
||||
|
||||
// 刷新相关变量
|
||||
const { getLastRefreshTime, getRefreshInterval, setLastRefreshTime, updateStudentInfo, updateStudentList } = useUserStore();
|
||||
const REFRESH_INTERVAL = 7 * 24 * 60 * 60 * 1000; // 1周刷新一次(7天)
|
||||
/** 家长端菜单项(后端 getMobileMenuApi 动态加载) */
|
||||
interface HomeMenuItem {
|
||||
id?: number | string;
|
||||
title: string;
|
||||
icon: string;
|
||||
path?: string;
|
||||
permissionKey?: string;
|
||||
action?: string;
|
||||
lxId?: string;
|
||||
}
|
||||
|
||||
// 获取当前changeTime
|
||||
const getCurrentChangeTime = () => {
|
||||
try {
|
||||
const userDataStr = uni.getStorageSync('app-user');
|
||||
if (!userDataStr) return null;
|
||||
|
||||
const userData = typeof userDataStr === 'string' ? JSON.parse(userDataStr) : userDataStr;
|
||||
return userData?.changeTime || null;
|
||||
} catch (error) {
|
||||
console.error('获取changeTime失败:', error);
|
||||
return null;
|
||||
}
|
||||
/** 家长端特殊菜单:选课/退费/缴费需 lxId + action,后端 auth_code 映射 */
|
||||
const JZD_SPECIAL_MENU: Record<string, { lxId: string; action: string }> = {
|
||||
"school-xqkxk": { lxId: "962488654", action: "jf" }, // 兴趣课选课
|
||||
"school-jlbxk": { lxId: "816059832", action: "jf" }, // 俱乐部选课
|
||||
"school-jcjf": { lxId: "JC", action: "jf" }, // 就餐报名
|
||||
"school-xqk-tf": { lxId: "962488654", action: "tf" }, // 兴趣课退费
|
||||
"school-jlb-tf": { lxId: "816059832", action: "tf" }, // 俱乐部退费
|
||||
};
|
||||
|
||||
// 检查权限(带changeTime)
|
||||
const checkPermission = (permissionKey: string) => {
|
||||
const changeTime = getCurrentChangeTime();
|
||||
return hasPermission(permissionKey, changeTime);
|
||||
};
|
||||
|
||||
// 直接权限检查函数,避免缓存问题
|
||||
const hasPermissionDirect = (permissionKey: string) => {
|
||||
if (!permissionKey) return true;
|
||||
const userStore = useUserStore();
|
||||
const permissions = userStore.getAuth;
|
||||
if (!permissions || permissions.length === 0) return false;
|
||||
|
||||
const uniquePermissions = [...new Set(permissions)];
|
||||
return uniquePermissions.includes(permissionKey);
|
||||
};
|
||||
|
||||
// 检查是否需要刷新学生信息
|
||||
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]);
|
||||
}
|
||||
// 将菜单树扁平化为家长端结构
|
||||
function flattenMenuToItems(nodes: MobileMenuTreeNode[]): HomeMenuItem[] {
|
||||
const items: HomeMenuItem[] = [];
|
||||
const walk = (list: MobileMenuTreeNode[]) => {
|
||||
for (const node of list || []) {
|
||||
if (node.pagePath) {
|
||||
const icon = (node.normalCss && /^[a-zA-Z0-9_-]+$/.test(node.normalCss))
|
||||
? `/static/base/home/${node.normalCss}.png`
|
||||
: "/static/base/home/file-text-line.png";
|
||||
const authCode = node.authCode || "";
|
||||
const special = authCode ? JZD_SPECIAL_MENU[authCode] : undefined;
|
||||
items.push({
|
||||
id: node.id,
|
||||
title: node.screenName,
|
||||
icon,
|
||||
path: node.pagePath,
|
||||
permissionKey: authCode,
|
||||
...(special && { lxId: special.lxId, action: special.action }),
|
||||
});
|
||||
}
|
||||
if (node.children?.length) walk(node.children);
|
||||
}
|
||||
} catch (error) {
|
||||
// 处理错误,静默失败
|
||||
};
|
||||
if (nodes.length === 1 && !nodes[0].pagePath && (nodes[0].children?.length ?? 0) > 0) {
|
||||
walk(nodes[0].children!);
|
||||
} else {
|
||||
walk(nodes);
|
||||
}
|
||||
};
|
||||
return items;
|
||||
}
|
||||
|
||||
// 菜单项数据
|
||||
const menuItems = ref([
|
||||
{
|
||||
title: "班级课表",
|
||||
icon: "/static/base/home/book-read-line.png",
|
||||
path: "/pages/base/class-schedule/index",
|
||||
permissionKey: "school-bjkb", // 班级课表权限编码
|
||||
},
|
||||
{
|
||||
title: "学业监测",
|
||||
icon: "/static/base/home/file-search-line.png",
|
||||
path: "/pages/base/grades/list",
|
||||
permissionKey: "school-cjcx", // 成绩查询权限编码
|
||||
},
|
||||
{
|
||||
title: "在线请假",
|
||||
icon: "/static/base/home/draft-line.png",
|
||||
path: "/pages/base/qj/index",
|
||||
permissionKey: "school-zxqj", // 在线请假权限编码
|
||||
},
|
||||
// TODO:需求待协商硬件对接
|
||||
// {
|
||||
// title: "进出校园",
|
||||
// icon: "/static/base/home/file-transfer-line.png",
|
||||
// path: "/pages/base/campus-access/index",
|
||||
// permissionKey: "school-jcxy", // 进出校园权限编码
|
||||
// },
|
||||
{
|
||||
title: "家校沟通",
|
||||
icon: "/static/base/home/file-transfer-line.png",
|
||||
path: "/pages/base/jl/index",
|
||||
permissionKey: "school-jxgt", // 家校沟通权限编码
|
||||
},
|
||||
{
|
||||
title: "兴趣课",
|
||||
icon: "/static/base/home/file-text-line.png",
|
||||
path: "/pages/base/xk/xqk",
|
||||
permissionKey: "school-xqk", // 兴趣课权限编码
|
||||
},
|
||||
{
|
||||
title: "俱乐部",
|
||||
icon: "/static/base/home/contacts-book-3-line.png",
|
||||
path: "/pages/base/xk/jlb",
|
||||
permissionKey: "school-jlb", // 俱乐部权限编码
|
||||
},
|
||||
{
|
||||
title: "就餐详情",
|
||||
icon: "/static/base/home/jcxq.png",
|
||||
path: "/pages/base/jc/index",
|
||||
permissionKey: "school-jcxq",
|
||||
},
|
||||
{
|
||||
title: "兴趣课选课",
|
||||
icon: "/static/base/home/file-text-line.png",
|
||||
path: "/pages/base/gzs/index",
|
||||
permissionKey: "school-xqkxk", // 兴趣课选课权限编码
|
||||
action: 'jf',
|
||||
lxId: '962488654',
|
||||
},
|
||||
{
|
||||
title: "俱乐部选课",
|
||||
icon: "/static/base/home/contacts-book-3-line.png",
|
||||
path: "/pages/base/gzs/index",
|
||||
permissionKey: "school-jlbxk", // 俱乐部选课权限编码
|
||||
action: 'jf',
|
||||
lxId: '816059832',
|
||||
},
|
||||
{
|
||||
title: "就餐报名",
|
||||
icon: "/static/base/home/jcxq.png",
|
||||
path: "/pages/base/gzs/index",
|
||||
permissionKey: "school-jcjf",
|
||||
action: 'jf',
|
||||
lxId: 'JC',
|
||||
},
|
||||
{
|
||||
title: "兴趣课退费",
|
||||
icon: "/static/base/home/contacts-book-3-line.png",
|
||||
path: "/pages/base/gzs/tf",
|
||||
permissionKey: "school-xqk-tf",
|
||||
action: "tf",
|
||||
lxId: "962488654",
|
||||
},
|
||||
{
|
||||
title: "俱乐部退费",
|
||||
icon: "/static/base/home/contacts-book-3-line.png",
|
||||
path: "/pages/base/gzs/tf",
|
||||
permissionKey: "school-jlb-tf",
|
||||
action: "tf",
|
||||
lxId: "816059832",
|
||||
},
|
||||
{
|
||||
title: "新苗成长",
|
||||
icon: "/static/base/home/xszp.png",
|
||||
path: "/pages/base/xszp/index",
|
||||
permissionKey: "school-xszp",
|
||||
},
|
||||
]);
|
||||
const menuItems = ref<HomeMenuItem[]>([]);
|
||||
|
||||
// 通知公告数据
|
||||
const announcements = ref<any>([])
|
||||
@ -333,11 +214,26 @@ const getArticleList = async () => {
|
||||
|
||||
onMounted(async () => {
|
||||
setGlobal({ from: 'home' });
|
||||
|
||||
// 菜单由 launchPage 加载并写入 menuStore,此处读缓存
|
||||
const cachedMenu = menuStore.getMobileMenu;
|
||||
if (cachedMenu?.length > 0) {
|
||||
menuItems.value = flattenMenuToItems(cachedMenu);
|
||||
} else if (userStore.getToken) {
|
||||
// 首次注册登录场景:launchPage 可能因 token 未就绪返回空菜单,此处补拉
|
||||
try {
|
||||
const r = await getMobileMenuApi();
|
||||
if (r?.result && Array.isArray(r.result) && r.result.length > 0) {
|
||||
menuStore.setMobileMenu(r.result);
|
||||
menuItems.value = flattenMenuToItems(r.result);
|
||||
}
|
||||
} catch (_e) {}
|
||||
}
|
||||
|
||||
// 确保有学生年级信息才获取通知公告
|
||||
if (curXs.value && curXs.value.njmcId) {
|
||||
getArticleList();
|
||||
} else {
|
||||
// 如果store中没有学生信息,等待一下再尝试
|
||||
setTimeout(() => {
|
||||
if (curXs.value && curXs.value.njmcId) {
|
||||
getArticleList();
|
||||
@ -345,39 +241,11 @@ onMounted(async () => {
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// 初始化时检查是否需要刷新学生信息
|
||||
await checkAndRefreshStudentInfo();
|
||||
|
||||
// 检查权限缓存,确保权限数据是最新的
|
||||
const userStore = useUserStore();
|
||||
const changeTime = userStore.getChangeTime;
|
||||
if (changeTime) {
|
||||
// 如果有changeTime,检查是否需要刷新权限
|
||||
const { PermissionCacheManager } = await import('@/utils/permission');
|
||||
const cacheInfo = PermissionCacheManager.getCacheInfo();
|
||||
|
||||
if (cacheInfo.hasCache && cacheInfo.changeTime) {
|
||||
const serverTime = new Date(changeTime).getTime();
|
||||
const cacheTime = new Date(cacheInfo.changeTime).getTime();
|
||||
|
||||
if (serverTime > cacheTime) {
|
||||
// 服务器时间更新,刷新权限缓存
|
||||
const { refreshPermissionCache } = await import('@/utils/permission');
|
||||
const currentPermissions = userStore.getAuth;
|
||||
if (currentPermissions && currentPermissions.length > 0) {
|
||||
refreshPermissionCache(currentPermissions, changeTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查用户是否已关注服务号
|
||||
await checkSubscribeStatus();
|
||||
});
|
||||
|
||||
// 检查关注状态
|
||||
const checkSubscribeStatus = async () => {
|
||||
const userStore = useUserStore();
|
||||
const userInfo = userStore.getUser;
|
||||
|
||||
if (userInfo && !userInfo.subscribed) {
|
||||
@ -407,11 +275,6 @@ const showSubscribeReminder = () => {
|
||||
});
|
||||
};
|
||||
|
||||
// 页面显示时检查是否需要刷新
|
||||
onShow(async () => {
|
||||
await checkAndRefreshStudentInfo();
|
||||
});
|
||||
|
||||
// 监听学生信息变化,当学生信息更新时重新获取通知公告
|
||||
watch(curXs, (newXs, oldXs) => {
|
||||
if (newXs && newXs.njmcId) {
|
||||
|
||||
@ -15,12 +15,16 @@
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { useMenuStore } from "@/store/modules/menu";
|
||||
import { checkOpenId } from "@/api/system/login";
|
||||
import { refreshPermissionCache } from "@/utils/permission";
|
||||
import { getMobileMenuApi } from "@/api/system/menu";
|
||||
import { getPermissionChangeTimeApi } from "@/api/system/config";
|
||||
import { PageUtils } from "@/utils/pageUtil";
|
||||
|
||||
const { setGlobal } = useDataStore();
|
||||
const dataStore = useDataStore();
|
||||
const { setGlobal } = dataStore;
|
||||
const userStore = useUserStore();
|
||||
const menuStore = useMenuStore();
|
||||
const isShow = ref(true);
|
||||
|
||||
const toLogin = () => {
|
||||
@ -31,7 +35,7 @@ const toLogin = () => {
|
||||
|
||||
// 重构一下初始化的数据
|
||||
const initGlobalData = (data: any) => {
|
||||
let gData = data || {};
|
||||
let gData = { ...(data || {}) };
|
||||
let lxId = gData.lxId;
|
||||
// 兼容旧版本参数
|
||||
if (!lxId && gData.type) {
|
||||
@ -66,23 +70,82 @@ const initGlobalData = (data: any) => {
|
||||
|
||||
onLoad(async (data: any) => {
|
||||
const gData = initGlobalData(data);
|
||||
|
||||
// 从登录页跳转:已登录,按权限变更时间判断是否拉取菜单后进首页
|
||||
if (gData.fromLogin === "1") {
|
||||
let needFetchMenu = true;
|
||||
try {
|
||||
const timeRes = await getPermissionChangeTimeApi();
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gData.openId) {
|
||||
try {
|
||||
const res = await checkOpenId({ openId: gData.openId, appCode: "JZ" });
|
||||
|
||||
if (res.resultCode == 1 && res.result) {
|
||||
// 执行登录操作
|
||||
// 在 afterLoginAction 之前保存:afterLoginAction 会调用 logout 清空 changeTime 和 menuStore
|
||||
const savedChangeTime = userStore.getChangeTime || "";
|
||||
const savedMenu = menuStore.getMobileMenu || [];
|
||||
|
||||
userStore.afterLoginAction(res.result);
|
||||
|
||||
// 如果有changeTime参数,更新权限缓存
|
||||
if (gData.changeTime) {
|
||||
const currentPermissions = userStore.getAuth;
|
||||
|
||||
if (currentPermissions && currentPermissions.length > 0) {
|
||||
refreshPermissionCache(currentPermissions, data.changeTime);
|
||||
}
|
||||
// 判断是否需要拉取菜单:时间戳不一致或本地无菜单则重新请求
|
||||
let needFetchMenu = true;
|
||||
try {
|
||||
const timeRes = await getPermissionChangeTimeApi();
|
||||
const serverChangeTime = String((timeRes as any)?.result ?? "");
|
||||
needFetchMenu =
|
||||
serverChangeTime !== savedChangeTime || !savedMenu?.length;
|
||||
console.log("[launchPage] openId", {
|
||||
timeResRaw: JSON.stringify(timeRes),
|
||||
serverChangeTime,
|
||||
savedChangeTime,
|
||||
savedMenuLength: savedMenu?.length ?? 0,
|
||||
needFetchMenu,
|
||||
});
|
||||
if (serverChangeTime) userStore.setChangeTime(serverChangeTime);
|
||||
} catch (_e) {
|
||||
needFetchMenu = true;
|
||||
console.warn("[launchPage] openId 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) {}
|
||||
} else {
|
||||
menuStore.setMobileMenu(savedMenu); // 恢复被 logout 清空的菜单
|
||||
}
|
||||
|
||||
PageUtils.toHome(gData.lxId, gData.action);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -17,33 +17,16 @@
|
||||
|
||||
<view class="avatar-section">
|
||||
<view class="avatar-uploader-container-rect">
|
||||
<CustomUpload
|
||||
@select="(event:any) => afterRead(event, index)"
|
||||
@close="handleAvatarClose(index)"
|
||||
:sourceType="['camera', 'album']"
|
||||
:value="imagUrl(student.xstx)"
|
||||
>
|
||||
<view class="avatar-placeholder">
|
||||
<view class="wh-full flex-col-center">
|
||||
<svg
|
||||
t="1729656215869"
|
||||
class="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="5302"
|
||||
width="32"
|
||||
height="32"
|
||||
>
|
||||
<path
|
||||
d="M851.552 890.88 172.448 890.88c-74.592 0-135.296-60.672-135.296-135.296L37.152 370.752c0-74.624 60.672-135.328 135.296-135.328l132.16 0L302.912 195.904c0-34.624 28.192-62.816 62.816-62.816l302.016 0c29.408 0 53.312 23.904 53.312 53.312l0 49.024 130.464 0c74.592 0 135.296 60.672 135.296 135.328l0 384.832C986.816 830.208 926.144 890.88 851.552 890.88zM172.448 283.456c-48.128 0-87.296 39.168-87.296 87.328l0 384.832c0 48.128 39.168 87.296 87.296 87.296l679.104 0c48.128 0 87.296-39.168 87.296-87.296L938.848 370.752c0-48.16-39.168-87.328-87.296-87.328L716.8 283.424c-24.096 0-43.712-19.616-43.712-43.712L673.088 186.4c0-2.944-2.368-5.312-5.312-5.312l-302.016 0c-8.16 0-14.816 6.656-14.816 14.816L350.944 237.12c0 25.536-20.768 46.304-46.304 46.304L172.448 283.424zM512 755.84c-107.04 0-194.08-87.072-194.08-194.08S404.992 367.68 512 367.68s194.08 87.072 194.08 194.08S619.04 755.84 512 755.84zM512 415.68c-80.576 0-146.08 65.536-146.08 146.08S431.456 707.84 512 707.84s146.08-65.536 146.08-146.08S592.576 415.68 512 415.68zM816.8 438.016c-25.568 0-46.336-20.768-46.336-46.336s20.768-46.336 46.336-46.336 46.336 20.768 46.336 46.336S842.368 438.016 816.8 438.016zM816.8 390.016l-1.664 1.664c0 0.896 0.736 1.664 1.664 1.664L816.8 390.016z"
|
||||
fill="#cdcdcd"
|
||||
p-id="5303"
|
||||
></path>
|
||||
</svg>
|
||||
</view>
|
||||
</view>
|
||||
</CustomUpload>
|
||||
<ImageVideoUpload
|
||||
:image-list="student.xstx ? [{ url: student.xstx }] : []"
|
||||
@update:image-list="(list: any[]) => onAvatarUpdate(index, list)"
|
||||
:enable-video="false"
|
||||
:enable-file="false"
|
||||
:max-image-count="1"
|
||||
:upload-api="attachmentUpload"
|
||||
:show-section-title="false"
|
||||
:auto-upload="true"
|
||||
/>
|
||||
</view>
|
||||
<text class="avatar-upload-note">上传学生人像用于校园进出</text>
|
||||
</view>
|
||||
@ -120,18 +103,15 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { hideLoading, showLoading, showToast } from "@/utils/uniapp";
|
||||
import { ref } from "vue";
|
||||
import { nextTick, ref } from "vue";
|
||||
import { attachmentUpload } from "@/api/system/upload";
|
||||
import CustomUpload from "/src/components/BasicUpload/CustomUpload.vue";
|
||||
import { ImageVideoUpload } from "@/components/ImageVideoUpload";
|
||||
|
||||
import { useForm } from "@/components/BasicForm/hooks/useForm";
|
||||
import { loginRegisterJzApi } from "@/api/base/server";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
import { useDicStore } from "@/store/modules/dic";
|
||||
import { imagUrl } from "@/utils";
|
||||
import { refreshPermissionCache } from "@/utils/permission";
|
||||
import { PageUtils } from "@/utils/pageUtil";
|
||||
|
||||
const dicOptions = ref<any>([[[]]]);
|
||||
const dicPickerRef = ref();
|
||||
@ -221,34 +201,8 @@ const dicChanged = (dicArr: any) => {
|
||||
curXs.value.jzxsgxmc = dic.dictionaryCode;
|
||||
};
|
||||
|
||||
async function afterRead(event: any, index: number) {
|
||||
if (!event.tempFilePaths || event.tempFilePaths.length === 0) {
|
||||
showToast({ title: "图片选择失败", icon: "none" });
|
||||
return;
|
||||
}
|
||||
const tempFilePath = event.tempFilePaths[0];
|
||||
showLoading({ title: "上传中" });
|
||||
try {
|
||||
const res = (await attachmentUpload(tempFilePath)) as {
|
||||
result: Array<{ filePath: string }>;
|
||||
};
|
||||
const result = res.result;
|
||||
if (result && result.length > 0 && result[0].filePath) {
|
||||
students.value[index].xstx = result[0].filePath;
|
||||
showToast({ title: "上传成功" });
|
||||
} else {
|
||||
showToast({ title: "上传失败,请重试", icon: "none" });
|
||||
}
|
||||
} catch (error) {
|
||||
showToast({ title: "上传出错", icon: "none" });
|
||||
console.error("Upload error:", error);
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
function handleAvatarClose(index: number) {
|
||||
students.value[index].xstx = "";
|
||||
function onAvatarUpdate(index: number, list: any[]) {
|
||||
students.value[index].xstx = list[0]?.url || "";
|
||||
}
|
||||
|
||||
function addMoreChildren() {
|
||||
@ -301,15 +255,13 @@ async function submit() {
|
||||
hideLoading();
|
||||
if (res.resultCode == 1) {
|
||||
userStore.afterLoginAction(res.result);
|
||||
// 如果有changeTime参数,更新权限缓存
|
||||
if (res.result && res.result.changeTime) {
|
||||
const currentPermissions = userStore.getAuth;
|
||||
|
||||
if (currentPermissions && currentPermissions.length > 0) {
|
||||
refreshPermissionCache(currentPermissions, res.result.changeTime);
|
||||
}
|
||||
}
|
||||
PageUtils.toHome(getGlobal.lxId, getGlobal.action);
|
||||
// 等待 store 更新完成后再跳转,避免 launchPage 请求菜单时 token 尚未就绪
|
||||
await nextTick();
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: `/pages/system/launchPage/launchPage?fromLogin=1&lxId=${getGlobal.lxId || ""}&action=${getGlobal.action || ""}`,
|
||||
});
|
||||
}, 50);
|
||||
} else {
|
||||
showToast({ title: res.message || "提交失败", icon: "none" });
|
||||
}
|
||||
@ -363,17 +315,42 @@ onMounted(async () => {
|
||||
/* Keep avatar uploader styles relevant to CustomUpload */
|
||||
/* Replace with rectangular styles */
|
||||
.avatar-uploader-container-rect {
|
||||
/* Assuming uni.rpx units based on class names like wi-180, he-240 */
|
||||
width: 180rpx;
|
||||
height: 240rpx;
|
||||
margin: 0 auto 10px auto; /* mx-auto mb-10 */
|
||||
border-radius: 6px; /* r-md approximation */
|
||||
margin: 0 auto 10px auto;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #cccccc;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #fafafa;
|
||||
|
||||
/* 头像模式:图片自适应填满容器 */
|
||||
:deep(.image-video-upload) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
:deep(.upload-section),
|
||||
:deep(.image-list) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
:deep(.image-item),
|
||||
:deep(.add-btn) {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
:deep(.image-preview) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover; /* 自适应:保持比例填满,多余裁剪 */
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove old circular styles */
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { get } from "lodash";
|
||||
import { defineStore } from "pinia";
|
||||
import type { MobileMenuTreeNode } from "@/api/system/menu";
|
||||
|
||||
export const useDataStore = defineStore({
|
||||
id: "data",
|
||||
@ -8,6 +9,8 @@ export const useDataStore = defineStore({
|
||||
kcData: {},
|
||||
global: {},
|
||||
file: {},
|
||||
/** 手机端菜单树(家长端 home 页用) */
|
||||
mobileMenu: [] as MobileMenuTreeNode[],
|
||||
params: {},
|
||||
appCode: "JZ",
|
||||
qk: {},
|
||||
@ -54,6 +57,9 @@ export const useDataStore = defineStore({
|
||||
getXxts(): any {
|
||||
return this.xxts;
|
||||
},
|
||||
getMobileMenu(): MobileMenuTreeNode[] {
|
||||
return this.mobileMenu || [];
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
cleanData() {
|
||||
@ -68,6 +74,7 @@ export const useDataStore = defineStore({
|
||||
this.jcBz = {};
|
||||
this.lcgl = {};
|
||||
this.xxts = {};
|
||||
this.mobileMenu = [];
|
||||
this.$reset();
|
||||
},
|
||||
setData(data: any) {
|
||||
@ -103,6 +110,9 @@ export const useDataStore = defineStore({
|
||||
setXxts(data: any) {
|
||||
this.xxts = data;
|
||||
},
|
||||
setMobileMenu(menu: MobileMenuTreeNode[]) {
|
||||
this.mobileMenu = menu && Array.isArray(menu) ? menu : [];
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
enabled: true,
|
||||
|
||||
34
src/store/modules/menu.ts
Normal file
34
src/store/modules/menu.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { defineStore } from "pinia";
|
||||
import type { MobileMenuTreeNode } from "@/api/system/menu";
|
||||
|
||||
export const useMenuStore = defineStore({
|
||||
id: "app-Menu",
|
||||
state: () => ({
|
||||
/** 树形菜单数据,持久化到 localStorage key: app-Menu */
|
||||
mobileMenu: [] as MobileMenuTreeNode[],
|
||||
}),
|
||||
getters: {
|
||||
getMobileMenu(): MobileMenuTreeNode[] {
|
||||
return this.mobileMenu || [];
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
/** 清空菜单 */
|
||||
clearMenu() {
|
||||
this.mobileMenu = [];
|
||||
},
|
||||
/**
|
||||
* 设置菜单:拉取菜单时先清空再写入
|
||||
* @param menu 树形菜单数据
|
||||
*/
|
||||
setMobileMenu(menu: MobileMenuTreeNode[]) {
|
||||
this.clearMenu();
|
||||
this.mobileMenu = menu && Array.isArray(menu) ? [...menu] : [];
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
enabled: true,
|
||||
detached: true,
|
||||
H5Storage: localStorage,
|
||||
},
|
||||
});
|
||||
@ -1,11 +1,12 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { authenticationApi, loginCode, loginPass, weChatLogin, checkOpenId } from "@/api/system/login";
|
||||
import { loginCode, loginPass, weChatLogin, checkOpenId } from "@/api/system/login";
|
||||
import { AUTH_KEY } from "@/config";
|
||||
import { imagUrl } from "@/utils";
|
||||
import { useWebSocket } from '@/utils/webSocket/webSocket'
|
||||
import { useDicStore } from "@/store/modules/dic";
|
||||
import { useCommonStore } from "@/store/modules/common";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
import { useMenuStore } from "@/store/modules/menu";
|
||||
|
||||
const defWsCallback = (type: string, data: any) => {
|
||||
console.log('接收到 WebSocket 消息, 默认处理函数:', type, data);
|
||||
@ -210,11 +211,6 @@ export const useUserStore = defineStore({
|
||||
if (value[AUTH_KEY]) {
|
||||
this.setToken(value[AUTH_KEY])
|
||||
}
|
||||
authenticationApi({ userId: value.userId }).then(({ result }) => {
|
||||
if (result) {
|
||||
this.setAuth(result)
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* @description: 注销
|
||||
@ -224,6 +220,7 @@ export const useUserStore = defineStore({
|
||||
this.setUser('')
|
||||
this.setCurXs({})
|
||||
this.setAuth([])
|
||||
this.setChangeTime(''); // 清除权限变更时间,确保下次登录拉取最新菜单
|
||||
this.setXsPickerInitialized(false); // 注销时重置学生选择器状态
|
||||
this.exitWs();
|
||||
this.wsCallback = defWsCallback;
|
||||
@ -231,6 +228,7 @@ export const useUserStore = defineStore({
|
||||
useDicStore().cleanData()
|
||||
useCommonStore().cleanData();
|
||||
useDataStore().cleanData();
|
||||
useMenuStore().clearMenu(); // 清除菜单缓存
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user