feat: add department context with single-department transparent support

- Add Department type to types.ts
- Add loadMyDepartments / switchDepartment to store/org.ts
- Auto-load departments after org switch in main.ts
- Single-department orgs auto-select default department
- localStorage persistence for CurrentDepartmentId
This commit is contained in:
2026-05-02 16:37:32 +08:00
parent 7f44a2a401
commit 55ae961b2b
3 changed files with 65 additions and 2 deletions
+7 -1
View File
@@ -72,7 +72,7 @@ const parseCurrentOrgIdFromToken = (token: string | null): string | undefined =>
router.beforeEach(async (to, from , next) => {
document.title = to.meta.title as string;
let { store, read, login, logout, chooseRole, getUnreadTongzhis, set_editing } = useUser();
let { store: orgStore, loadMyOrgs, switchOrg } = useOrg();
let { store: orgStore, loadMyOrgs, switchOrg, loadMyDepartments } = useOrg();
set_editing(false)
store.unionid = (store.unionid || cookie.get(HtyUnionIDToken) || '').toString();
if (!store.current.hty_id) {
@@ -173,6 +173,8 @@ router.beforeEach(async (to, from , next) => {
if (currentAuthToken) {
window.localStorage.setItem(HtySudoToken, currentAuthToken);
}
// load departments after org switch (single-department transparent)
await loadMyDepartments();
} else if (organizations.length > 1) {
await router.replace('/org/select');
next();
@@ -182,6 +184,10 @@ router.beforeEach(async (to, from , next) => {
window.localStorage.setItem(CurrentOrgId, tokenOrgId);
orgStore.currentOrgId = tokenOrgId;
}
// load departments if not already loaded (after org context is established)
if (orgStore.currentOrgId && orgStore.departments.length === 0) {
await loadMyDepartments();
}
}
let {stopTimer} = useTimer()
+47 -1
View File
@@ -1,19 +1,23 @@
import { reactive } from "vue";
import request from "~/utils/request";
import { CurrentOrgId, HtyAuthToken, HtySudoToken } from "~/utils";
import type { Organization } from "~/types";
import type { Department, Organization } from "~/types";
import { showDialog, showFailToast } from "vant";
interface OrgState {
currentOrgId?: string;
orgs: Organization[];
homepageMd?: string;
departments: Department[];
currentDepartmentId?: string;
}
const store = reactive<OrgState>({
currentOrgId: window.localStorage.getItem(CurrentOrgId) || undefined,
orgs: [],
homepageMd: "",
departments: [],
currentDepartmentId: window.localStorage.getItem("CurrentDepartmentId") || undefined,
});
type JwtPayloadDebug = {
@@ -151,6 +155,46 @@ export default function useOrg() {
return "";
};
const loadMyDepartments = async () => {
const { r, d, e } = await request({
url: "/api/v1/uc/org/departments/my",
method: "GET",
});
if (r) {
store.departments = d || [];
// single department: auto-select
if (store.departments.length === 1) {
store.currentDepartmentId = store.departments[0].id;
window.localStorage.setItem("CurrentDepartmentId", store.currentDepartmentId);
}
return store.departments;
}
store.departments = [];
console.warn("[loadMyDepartments]", e);
return [];
};
const switchDepartment = async (departmentId: string) => {
const { r, d, e } = await request({
url: "/api/v1/uc/org/department/switch",
method: "POST",
data: { department_id: departmentId },
});
if (!r) {
showFailToast(e);
return false;
}
if (typeof d !== "string") {
showFailToast("部门切换失败:返回 token 非法");
return false;
}
window.localStorage.setItem(HtyAuthToken, d);
window.localStorage.setItem(HtySudoToken, d);
window.localStorage.setItem("CurrentDepartmentId", departmentId);
store.currentDepartmentId = departmentId;
return true;
};
const saveHomepage = async (orgId: string, markdownText: string) => {
const { r, d, e } = await request({
url: "/api/v1/uc/org/save_homepage",
@@ -174,5 +218,7 @@ export default function useOrg() {
switchOrg,
getHomepage,
saveHomepage,
loadMyDepartments,
switchDepartment,
};
}
+11
View File
@@ -67,6 +67,17 @@ export interface Organization {
is_delete?: boolean;
}
export interface Department {
id: string;
org_id: string;
dept_name: string;
dept_desc?: string;
supervisor_user_info_id?: string;
is_default: boolean;
dept_status: string;
is_delete?: boolean;
}
export interface StudentSummary {
student_id: string;
student_name: string;