diff --git a/bx-mobile/src/api/bxClient.ts b/bx-mobile/src/api/bxClient.ts new file mode 100644 index 000000000..b1c63d419 --- /dev/null +++ b/bx-mobile/src/api/bxClient.ts @@ -0,0 +1,96 @@ +import axios, { type InternalAxiosRequestConfig } from 'axios' +import { showToast } from 'vant' +import { portalAuthApi } from './portalAuth' + +export class BizError extends Error { + code?: number + constructor(message: string, code?: number) { + super(message) + this.code = code + this.name = 'BizError' + } +} + +const BX_COLLECTION_ROOT = + /^\/(reimburses|advances|payments|workflows|expense-items|invoices|invoice-wallets|invoice-ledgers)$/ + +function normalizeBxUrl(url?: string): string | undefined { + if (!url || url.endsWith('/')) return url + return BX_COLLECTION_ROOT.test(url) ? `${url}/` : url +} + +const bxClient = axios.create({ + baseURL: import.meta.env.VITE_API_URL || '/api', + timeout: 0, + headers: { 'Content-Type': 'application/json' }, +}) + +let refreshing: Promise | null = null + +async function tryRefresh(): Promise { + const rt = localStorage.getItem('refreshToken') + if (!rt) return false + try { + const res: any = await portalAuthApi.refreshToken(rt) + const token = res?.data?.token + const refreshToken = res?.data?.refreshToken + if (!token) return false + localStorage.setItem('accessToken', token) + if (refreshToken) localStorage.setItem('refreshToken', refreshToken) + return true + } catch { + return false + } +} + +bxClient.interceptors.request.use((config: InternalAxiosRequestConfig) => { + if (config.url) config.url = normalizeBxUrl(config.url) + if (config.data instanceof FormData) { + delete (config.headers as any)['Content-Type'] + } + const token = localStorage.getItem('accessToken') + if (token) config.headers.Authorization = `Bearer ${token}` + return config +}) + +bxClient.interceptors.response.use( + (res) => { + if (res.config.responseType === 'blob') return res.data + const body = res.data + if (body?.code !== undefined && body.code !== 0) { + showToast(body.msg || '请求失败') + return Promise.reject(new BizError(body.msg || '请求失败', body.code)) + } + return body + }, + async (err) => { + const original = err.config + if (err.response?.status === 401 && original && !(original as any)._retry) { + ;(original as any)._retry = true + if (!refreshing) { + refreshing = tryRefresh().finally(() => { + refreshing = null + }) + } + const ok = await refreshing + if (ok) { + original.headers = original.headers || {} + original.headers.Authorization = `Bearer ${localStorage.getItem('accessToken')}` + return bxClient.request(original) + } + localStorage.removeItem('accessToken') + localStorage.removeItem('refreshToken') + showToast('登录已过期,请重新登录') + if (window.location.pathname !== '/login') { + window.location.href = '/login' + } + } else if (err.response?.status !== 401) { + const msg = + err.response?.data?.msg || err.response?.data?.detail || err.message || '网络异常' + showToast(msg) + } + return Promise.reject(err) + }, +) + +export default bxClient diff --git a/bx-mobile/src/api/client.ts b/bx-mobile/src/api/client.ts index e96b2b21b..79850227d 100644 --- a/bx-mobile/src/api/client.ts +++ b/bx-mobile/src/api/client.ts @@ -1,64 +1 @@ -import axios from 'axios' -import { showToast } from 'vant' -import { useAuthStore } from '@/stores/auth' - -const client = axios.create({ - baseURL: '/api', - timeout: 0, // 0 = no timeout - headers: { 'Content-Type': 'application/json' }, -}) - -client.interceptors.request.use((config) => { - const token = localStorage.getItem('token') - if (token) { - config.headers.Authorization = `Bearer ${token}` - } - return config -}) - -/** - * Business error marker — page-level catch blocks should check this - * to avoid double-toasting (interceptor already showed the toast). - */ -export class BizError extends Error { - code?: number - constructor(message: string, code?: number) { - super(message) - this.code = code - this.name = 'BizError' - } -} - -client.interceptors.response.use( - (res) => { - const body = res.data - if (body.code !== 0 && body.code !== undefined) { - showToast(body.msg || '请求失败') - return Promise.reject(new BizError(body.msg || '请求失败', body.code)) - } - return body - }, - (err) => { - if (err.response?.status === 401) { - const auth = useAuthStore() - auth.logout() - showToast('登录已过期,请重新登录') - if (window.location.pathname !== '/login') { - window.location.href = '/login' - } - } else if (err.response?.status === 403 && err.response?.data?.detail === '用户已被禁用') { - const auth = useAuthStore() - auth.logout() - showToast('账号已被禁用,请联系管理员') - if (window.location.pathname !== '/login') { - window.location.href = '/login' - } - } else { - const msg = err.response?.data?.msg || err.response?.data?.detail || err.message || '网络异常' - showToast(msg) - } - return Promise.reject(err) - }, -) - -export default client +export { default, BizError } from './bxClient' diff --git a/bx-mobile/src/api/index.ts b/bx-mobile/src/api/index.ts index cdfaa3694..1c11f8eb9 100644 --- a/bx-mobile/src/api/index.ts +++ b/bx-mobile/src/api/index.ts @@ -1,122 +1,85 @@ -import client from './client' +import bxClient from './bxClient' -export const authApi = { - login: (data: { username: string; password: string }) => - client.post('/auth/login', data), - register: (data: { username: string; password: string; realName: string }) => - client.post('/auth/register', data), - me: () => client.get('/auth/me'), -} - -export const userApi = { - list: (params?: Record) => client.get('/users/', { params }), - dropdown: () => client.get('/users/dropdown'), - getById: (id: number) => client.get(`/users/${id}`), - create: (data: Record) => client.post('/users/', data), - update: (id: number, data: Record) => client.put(`/users/${id}`, data), - delete: (id: number) => client.delete(`/users/${id}`), - resetPassword: (id: number) => client.put(`/users/${id}/reset-password`), -} - -export const roleApi = { - list: (params?: Record) => client.get('/roles/', { params }), - getById: (id: number) => client.get(`/roles/${id}`), - create: (data: Record) => client.post('/roles/', data), - update: (id: number, data: Record) => client.put(`/roles/${id}`, data), - delete: (id: number) => client.delete(`/roles/${id}`), - getUsers: (roleId: number) => client.get(`/roles/${roleId}/users`), - assignUsers: (id: number, userIds: number[]) => - client.put(`/roles/${id}/users`, { userIds }), - assignMenus: (id: number, menuIds: number[]) => - client.put(`/roles/${id}/menus`, { menuIds }), -} - -export const menuApi = { - mine: () => client.get('/menus/mine'), - tree: () => client.get('/menus/tree'), - getByRole: (roleId: number) => client.get(`/menus/role/${roleId}`), - getById: (id: number) => client.get(`/menus/${id}`), - create: (data: Record) => client.post('/menus/', data), - update: (id: number, data: Record) => client.put(`/menus/${id}`, data), - delete: (id: number) => client.delete(`/menus/${id}`), -} +export type BxId = string | number export const expenseItemApi = { - tree: () => client.get('/expense-items/tree'), - getById: (id: number) => client.get(`/expense-items/${id}`), - create: (data: Record) => client.post('/expense-items/', data), - update: (id: number, data: Record) => client.put(`/expense-items/${id}`, data), - delete: (id: number) => client.delete(`/expense-items/${id}`), + tree: () => bxClient.get('/expense-items/tree'), + getById: (id: BxId) => bxClient.get(`/expense-items/${id}`), + create: (data: Record) => bxClient.post('/expense-items/', data), + update: (id: BxId, data: Record) => bxClient.put(`/expense-items/${id}`, data), + delete: (id: BxId) => bxClient.delete(`/expense-items/${id}`), } export const workflowApi = { - list: (params?: Record) => client.get('/workflows/', { params }), - getByBizType: (bizType: string) => client.get(`/workflows/biz/${bizType}`), - getById: (id: number) => client.get(`/workflows/${id}`), - create: (data: Record) => client.post('/workflows/', data), - update: (id: number, data: Record) => client.put(`/workflows/${id}`, data), - saveNodes: (id: number, nodes: Record[]) => - client.put(`/workflows/${id}/nodes`, nodes), - delete: (id: number) => client.delete(`/workflows/${id}`), + list: (params?: Record) => bxClient.get('/workflows/', { params }), + getByBizType: (bizType: string) => bxClient.get(`/workflows/biz/${bizType}`), + getById: (id: BxId) => bxClient.get(`/workflows/${id}`), + create: (data: Record) => bxClient.post('/workflows/', data), + update: (id: BxId, data: Record) => bxClient.put(`/workflows/${id}`, data), + saveNodes: (id: BxId, nodes: Record[]) => + bxClient.put(`/workflows/${id}/nodes`, nodes), + delete: (id: BxId) => bxClient.delete(`/workflows/${id}`), } export const advanceApi = { - list: (params?: Record) => client.get('/advances/', { params }), - my: (params?: Record) => client.get('/advances/my', { params }), - myStats: () => client.get('/advances/my/stats'), + list: (params?: Record) => bxClient.get('/advances/', { params }), + my: (params?: Record) => bxClient.get('/advances/my', { params }), + myStats: () => bxClient.get('/advances/my/stats'), pendingApproval: (params?: Record) => - client.get('/advances/pending-approval', { params }), + bxClient.get('/advances/pending-approval', { params }), approvedByMe: (params?: Record) => - client.get('/advances/approved-by-me', { params }), - getById: (id: number) => client.get(`/advances/${id}`), - getApprovals: (id: number) => client.get(`/advances/${id}/approvals`), - create: (data: Record) => client.post('/advances/', data), - update: (id: number, data: Record) => client.put(`/advances/${id}`, data), - submit: (id: number) => client.post(`/advances/${id}/submit`), - approve: (id: number, data: { approvalOpinion?: string }) => - client.post(`/advances/${id}/approve`, data), - reject: (id: number, data: { approvalOpinion?: string }) => - client.post(`/advances/${id}/reject`, data), - delete: (id: number) => client.delete(`/advances/${id}`), + bxClient.get('/advances/approved-by-me', { params }), + getById: (id: BxId) => bxClient.get(`/advances/${id}`), + getApprovals: (id: BxId) => bxClient.get(`/advances/${id}/approvals`), + create: (data: Record) => bxClient.post('/advances/', data), + update: (id: BxId, data: Record) => bxClient.put(`/advances/${id}`, data), + submit: (id: BxId) => bxClient.post(`/advances/${id}/submit`), + approve: (id: BxId, data: { approvalOpinion?: string }) => + bxClient.post(`/advances/${id}/approve`, data), + reject: (id: BxId, data: { approvalOpinion?: string }) => + bxClient.post(`/advances/${id}/reject`, data), + delete: (id: BxId) => bxClient.delete(`/advances/${id}`), } export const reimburseApi = { - generateTitle: () => client.get('/reimburses/generate-title'), - list: (params?: Record) => client.get('/reimburses/', { params }), - my: (params?: Record) => client.get('/reimburses/my', { params }), - myStats: () => client.get('/reimburses/my/stats'), + generateTitle: () => bxClient.get('/reimburses/generate-title'), + list: (params?: Record) => bxClient.get('/reimburses/', { params }), + my: (params?: Record) => bxClient.get('/reimburses/my', { params }), + myStats: () => bxClient.get('/reimburses/my/stats'), pendingApproval: (params?: Record) => - client.get('/reimburses/pending-approval', { params }), + bxClient.get('/reimburses/pending-approval', { params }), approvedByMe: (params?: Record) => - client.get('/reimburses/approved-by-me', { params }), - getById: (id: number) => client.get(`/reimburses/${id}`), - getApprovals: (id: number) => client.get(`/reimburses/${id}/approvals`), - create: (data: Record) => client.post('/reimburses/', data), - update: (id: number, data: Record) => client.put(`/reimburses/${id}`, data), - submit: (id: number) => client.post(`/reimburses/${id}/submit`), - approve: (id: number, data: { approvalOpinion?: string }) => - client.post(`/reimburses/${id}/approve`, data), - reject: (id: number, data: { approvalOpinion?: string }) => - client.post(`/reimburses/${id}/reject`, data), - delete: (id: number) => client.delete(`/reimburses/${id}`), + bxClient.get('/reimburses/approved-by-me', { params }), + getById: (id: BxId) => bxClient.get(`/reimburses/${id}`), + getApprovals: (id: BxId) => bxClient.get(`/reimburses/${id}/approvals`), + create: (data: Record) => bxClient.post('/reimburses/', data), + update: (id: BxId, data: Record) => bxClient.put(`/reimburses/${id}`, data), + submit: (id: BxId) => bxClient.post(`/reimburses/${id}/submit`), + approve: (id: BxId, data: { approvalOpinion?: string }) => + bxClient.post(`/reimburses/${id}/approve`, data), + reject: (id: BxId, data: { approvalOpinion?: string }) => + bxClient.post(`/reimburses/${id}/reject`, data), + delete: (id: BxId) => bxClient.delete(`/reimburses/${id}`), } export const paymentApi = { - pending: (params?: Record) => client.get('/payments/pending', { params }), - pay: (id: number, data?: { payRemark?: string; actualAmount?: number }) => - client.post(`/payments/${id}/pay`, data), - offset: (id: number, data: { advanceId: number; offsetAmount: number; remark?: string }) => - client.post(`/payments/${id}/offset`, data), + pending: (params?: Record) => bxClient.get('/payments/pending', { params }), + pay: (id: BxId, data?: { payRemark?: string; actualAmount?: number }) => + bxClient.post(`/payments/${id}/pay`, data), + offset: ( + id: BxId, + data: { advanceId: BxId; offsetAmount: number; remark?: string }, + ) => bxClient.post(`/payments/${id}/offset`, data), batchOffset: ( - id: number, + id: BxId, data: { - items: Array<{ advanceId: number; offsetAmount: number }> + items: Array<{ advanceId: BxId; offsetAmount: number }> payAmount: number remark?: string }, - ) => client.post(`/payments/${id}/batch-offset`, data), + ) => bxClient.post(`/payments/${id}/batch-offset`, data), advanceIssues: (params?: Record) => - client.get('/payments/advance-issues', { + bxClient.get('/payments/advance-issues', { params: params ? { page: params.page, @@ -127,41 +90,38 @@ export const paymentApi = { : undefined, }), exportAdvanceIssues: (params?: Record) => - client.get('/payments/advance-issues/export', { + bxClient.get('/payments/advance-issues/export', { params: params ? { issue_status: params.issueStatus, keyword: params.keyword } : undefined, responseType: 'blob', }), - issueAdvance: ( - advanceId: number, - data: { issueRemark?: string; actualAmount?: number }, - ) => client.post(`/payments/advance/${advanceId}/issue`, data), - processed: (params?: Record) => - client.get('/payments/processed', { params }), - advanceOffsetRecords: (advanceId: number) => - client.get(`/payments/advance/${advanceId}/offset-records`), - reimburseOffsetRecords: (reimburseId: number) => - client.get(`/payments/${reimburseId}/offset-records`), + issueAdvance: (advanceId: BxId, data: { issueRemark?: string; actualAmount?: number }) => + bxClient.post(`/payments/advance/${advanceId}/issue`, data), + processed: (params?: Record) => bxClient.get('/payments/processed', { params }), + advanceOffsetRecords: (advanceId: BxId) => + bxClient.get(`/payments/advance/${advanceId}/offset-records`), + reimburseOffsetRecords: (reimburseId: BxId) => + bxClient.get(`/payments/${reimburseId}/offset-records`), } export const attachmentApi = { - upload: (file: File, bizType: string = '', bizId: number = 0) => { + upload: (file: File, bizType: string = '', bizId: BxId = 0) => { const form = new FormData() form.append('file', file) - return client.post('/attachments/upload', form, { - headers: { 'Content-Type': 'multipart/form-data' }, + return bxClient.post('/attachments/upload', form, { params: { biz_type: bizType, biz_id: bizId }, }) }, - download: (id: number) => client.get(`/attachments/download/${id}`, { responseType: 'blob' }), - preview: (id: number) => client.get(`/attachments/preview/${id}`), - delete: (id: number) => client.delete(`/attachments/${id}`), - listByBiz: (bizType: string, bizId: number) => - client.get('/attachments/list', { params: { biz_type: bizType, biz_id: bizId } }), - bindBiz: (ids: number[], bizType: string, bizId: number) => - client.put('/attachments/bind-biz', null, { + download: (id: BxId) => + bxClient.get(`/attachments/download/${id}`, { responseType: 'blob' }), + preview: (id: BxId) => bxClient.get(`/attachments/preview/${id}`), + delete: (id: BxId) => bxClient.delete(`/attachments/${id}`), + listByBiz: (bizType: string, bizId: BxId) => + bxClient.get('/attachments/list', { params: { biz_type: bizType, biz_id: bizId } }), + bindBiz: (ids: BxId[], bizType: string, bizId: BxId) => + bxClient.put('/attachments/bind-biz', null, { params: { ids: ids.join(','), biz_type: bizType, biz_id: bizId }, }), - downloadUrl: (id: number) => `/api/attachments/download/${id}`, + downloadUrl: (id: BxId) => `/api/attachments/download/${id}`, } diff --git a/bx-mobile/src/stores/auth.ts b/bx-mobile/src/stores/auth.ts index 100435482..d75fc9671 100644 --- a/bx-mobile/src/stores/auth.ts +++ b/bx-mobile/src/stores/auth.ts @@ -1,17 +1,21 @@ import { defineStore } from 'pinia' import { ref, computed } from 'vue' -import { authApi, menuApi } from '@/api' +import { portalAuthApi } from '@/api/portalAuth' +import { filterVisibleEntries } from '@/utils/bxPermission' +import type { BxMenuEntry } from '@/constants/bxMenuCatalog' +/** 门户用户(id 为 UUID 字符串) */ export interface UserInfo { - id: number + id: string username: string realName: string phone?: string - email?: string - avatar?: string - roleList: { id: number; roleName: string; roleCode: string }[] + role?: string + roles?: string[] + avatarUrl?: string | null } +/** @deprecated 旧 bx 菜单树;首页请用 visibleEntries(Task 7) */ export interface MenuItem { id: number parentId: number @@ -25,74 +29,123 @@ export interface MenuItem { } export const useAuthStore = defineStore('auth', () => { - const token = ref(localStorage.getItem('token') || '') + const accessToken = ref( + localStorage.getItem('accessToken') || localStorage.getItem('token') || '', + ) + const refreshToken = ref(localStorage.getItem('refreshToken') || '') const user = ref(null) + const menuCodes = ref([]) + const isSuperAdmin = ref(false) + const allMenus = ref(false) + /** 兼容旧首页:Task 7 前保持空数组 */ const menuList = ref([]) - const isLoggedIn = computed(() => !!token.value) - const realName = computed(() => user.value?.realName || '') - const roleCodes = computed(() => user.value?.roleList?.map((r) => r.roleCode) || []) + const isLoggedIn = computed(() => !!accessToken.value) + const realName = computed(() => user.value?.realName || user.value?.username || '') - const isAdmin = computed(() => roleCodes.value.includes('ADMIN')) - const isFinance = computed(() => roleCodes.value.includes('FINANCE')) - const isGeneralManager = computed(() => roleCodes.value.includes('GENERAL_MANAGER')) + const visibleEntries = computed((): BxMenuEntry[] => + filterVisibleEntries({ + isSuperAdmin: isSuperAdmin.value, + all: allMenus.value, + menuCodes: menuCodes.value, + }), + ) + + /** @deprecated 旧角色码;路由守卫将改为 menuCodes(Task 6) */ + const roleCodes = computed(() => { + const roles = user.value?.roles || [] + if (user.value?.role) return Array.from(new Set([user.value.role, ...roles])) + return roles + }) + const isAdmin = computed(() => false) + const isFinance = computed(() => false) + const isGeneralManager = computed(() => false) + + function persistTokens(token: string, rt?: string) { + accessToken.value = token + localStorage.setItem('accessToken', token) + localStorage.removeItem('token') + if (rt) { + refreshToken.value = rt + localStorage.setItem('refreshToken', rt) + } + } async function login(username: string, password: string) { - const res: any = await authApi.login({ username, password }) - if (res.code === 0) { - token.value = res.data.accessToken - localStorage.setItem('token', res.data.accessToken) - await fetchUser() - return true - } - throw new Error(res.msg || '登录失败') + const res: any = await portalAuthApi.login(username, password) + const token = res?.data?.token + if (!token) throw new Error(res?.message || '登录失败') + persistTokens(token, res?.data?.refreshToken) + await fetchUser() + return true } async function fetchUser() { try { - const res: any = await authApi.me() - if (res.code === 0) { - user.value = res.data - } - await fetchMenus() + const res: any = await portalAuthApi.currentUser() + user.value = res.data + const perm: any = await portalAuthApi.menuCodes() + isSuperAdmin.value = !!perm.data?.isSuperAdmin + allMenus.value = !!perm.data?.all + menuCodes.value = perm.data?.menuCodes || [] } catch { logout() } } async function fetchMenus() { - try { - const res: any = await menuApi.mine() - if (res.code === 0) { - menuList.value = res.data || [] - } - } catch { - menuList.value = [] - } + menuList.value = [] } - function logout() { - token.value = '' + async function logout() { + try { + await portalAuthApi.logout() + } catch { + /* ignore */ + } + accessToken.value = '' + refreshToken.value = '' user.value = null + menuCodes.value = [] + isSuperAdmin.value = false + allMenus.value = false menuList.value = [] + localStorage.removeItem('accessToken') + localStorage.removeItem('refreshToken') localStorage.removeItem('token') } + function can(menuCode: string) { + if (isSuperAdmin.value || allMenus.value) return true + return menuCodes.value.includes(menuCode) + } + function hasMenu(code: string): boolean { - function check(items: MenuItem[]): boolean { - for (const item of items) { - if (item.menuCode === code) return true - if (item.children?.length && check(item.children)) return true - } - return false - } - return check(menuList.value) + return can(code) } return { - token, user, menuList, - isLoggedIn, realName, roleCodes, - isAdmin, isFinance, isGeneralManager, - login, fetchUser, fetchMenus, logout, hasMenu, + accessToken, + refreshToken, + /** @deprecated 使用 accessToken */ + token: accessToken, + user, + menuList, + menuCodes, + isSuperAdmin, + allMenus, + isLoggedIn, + realName, + roleCodes, + isAdmin, + isFinance, + isGeneralManager, + visibleEntries, + login, + fetchUser, + fetchMenus, + logout, + can, + hasMenu, } }) diff --git a/bx-mobile/src/views/admin/MenuManagePage.vue b/bx-mobile/src/views/admin/MenuManagePage.vue index ce21b6196..85826323b 100644 --- a/bx-mobile/src/views/admin/MenuManagePage.vue +++ b/bx-mobile/src/views/admin/MenuManagePage.vue @@ -151,7 +151,19 @@ import { ref, reactive, computed, onMounted } from 'vue' import { useRouter } from 'vue-router' import { showToast, showDialog } from 'vant' import { DELETE_CONFIRM_BUTTON_COLOR } from '@/constants/dialog' -import { menuApi } from '@/api' +/** IAM 已迁门户;本页路由将在 Task 6 移除 */ +const menuApi: any = { + tree: async () => ({ code: 0, data: [] }), + create: async () => { + throw new Error('请使用门户系统管理菜单') + }, + update: async () => { + throw new Error('请使用门户系统管理菜单') + }, + delete: async () => { + throw new Error('请使用门户系统管理菜单') + }, +} import { menuTypeLabel, statusLabel } from '@/utils/format' import StatusTag from '@/components/mobile/StatusTag.vue' import MenuNode from '@/components/mobile/MenuNode.vue' diff --git a/bx-mobile/src/views/admin/RoleManagePage.vue b/bx-mobile/src/views/admin/RoleManagePage.vue index 7c837b004..536f9bdf1 100644 --- a/bx-mobile/src/views/admin/RoleManagePage.vue +++ b/bx-mobile/src/views/admin/RoleManagePage.vue @@ -237,8 +237,35 @@ import { ref, reactive, computed, onMounted, h, defineComponent } from 'vue' import { useRouter } from 'vue-router' import { showToast, showConfirmDialog } from 'vant' import { DELETE_CONFIRM_BUTTON_COLOR } from '@/constants/dialog' -import { roleApi, menuApi, userApi } from '@/api' import { BizError } from '@/api/client' + +/** IAM 已迁门户;本页路由将在 Task 6 移除 */ +const roleApi: any = { + list: async () => ({ code: 0, data: { rows: [], total: 0 } }), + create: async () => { + throw new Error('请使用门户系统管理角色') + }, + update: async () => { + throw new Error('请使用门户系统管理角色') + }, + delete: async () => { + throw new Error('请使用门户系统管理角色') + }, + getUsers: async () => ({ code: 0, data: [] }), + assignUsers: async () => { + throw new Error('请使用门户系统管理角色') + }, + assignMenus: async () => { + throw new Error('请使用门户系统管理角色') + }, +} +const menuApi: any = { + tree: async () => ({ code: 0, data: [] }), + getByRole: async () => ({ code: 0, data: [] }), +} +const userApi: any = { + list: async () => ({ code: 0, data: { rows: [], total: 0 } }), +} import { statusLabel } from '@/utils/format' import StatusTag from '@/components/mobile/StatusTag.vue' import { useKeywordSearch } from '@/hooks/useKeywordSearch' diff --git a/bx-mobile/src/views/admin/UserManagePage.vue b/bx-mobile/src/views/admin/UserManagePage.vue index 7f7b7b14b..d08c3adbd 100644 --- a/bx-mobile/src/views/admin/UserManagePage.vue +++ b/bx-mobile/src/views/admin/UserManagePage.vue @@ -226,8 +226,27 @@ import { ref, reactive, computed, onMounted } from 'vue' import { useRouter } from 'vue-router' import { showToast, showConfirmDialog } from 'vant' import { DELETE_CONFIRM_BUTTON_COLOR } from '@/constants/dialog' -import { userApi, roleApi } from '@/api' import { BizError } from '@/api/client' + +/** IAM 已迁门户;本页路由将在 Task 6 移除,保留空实现以免编译失败 */ +const userApi: any = { + list: async () => ({ code: 0, data: { rows: [], total: 0 } }), + create: async () => { + throw new Error('请使用门户系统管理用户') + }, + update: async () => { + throw new Error('请使用门户系统管理用户') + }, + delete: async () => { + throw new Error('请使用门户系统管理用户') + }, + resetPassword: async () => { + throw new Error('请使用门户系统管理用户') + }, +} +const roleApi: any = { + list: async () => ({ code: 0, data: { rows: [], total: 0 } }), +} import { statusLabel } from '@/utils/format' import StatusTag from '@/components/mobile/StatusTag.vue' import { useKeywordSearch } from '@/hooks/useKeywordSearch' diff --git a/bx-mobile/src/views/admin/WfAdvancePage.vue b/bx-mobile/src/views/admin/WfAdvancePage.vue index c0e3f4279..0b331aa3e 100644 --- a/bx-mobile/src/views/admin/WfAdvancePage.vue +++ b/bx-mobile/src/views/admin/WfAdvancePage.vue @@ -128,7 +128,7 @@ import { ref, reactive, computed, onMounted } from 'vue' import { useRouter } from 'vue-router' import { showToast, showDialog } from 'vant' import { DELETE_CONFIRM_BUTTON_COLOR } from '@/constants/dialog' -import { workflowApi, userApi } from '@/api' +import { workflowApi } from '@/api' import { statusLabel, statusTagClass } from '@/utils/format' import StatusTag from '@/components/mobile/StatusTag.vue' @@ -165,21 +165,8 @@ function nodeKey(node: Record): string { } async function loadUsers() { - try { - const res: any = await userApi.dropdown() - const data = res.data ?? res - userOptions.value = (data || []).map((u: any) => { - const name = u.realName || u.username - const roles = u.roles || '' - return { - id: u.id, - realName: name, - displayName: roles ? `${name}(${roles})` : name, - } - }) - } catch { - userOptions.value = [] - } + // 旧 bx userApi 已移除;P3 再接门户用户下拉 + userOptions.value = [] } async function loadWfDef() { diff --git a/bx-mobile/src/views/admin/WfReimbursePage.vue b/bx-mobile/src/views/admin/WfReimbursePage.vue index 3e7e21770..e0e9a6be2 100644 --- a/bx-mobile/src/views/admin/WfReimbursePage.vue +++ b/bx-mobile/src/views/admin/WfReimbursePage.vue @@ -128,7 +128,7 @@ import { ref, reactive, computed, onMounted } from 'vue' import { useRouter } from 'vue-router' import { showToast, showDialog } from 'vant' import { DELETE_CONFIRM_BUTTON_COLOR } from '@/constants/dialog' -import { workflowApi, userApi } from '@/api' +import { workflowApi } from '@/api' import { statusLabel, statusTagClass } from '@/utils/format' import StatusTag from '@/components/mobile/StatusTag.vue' @@ -165,21 +165,8 @@ function nodeKey(node: Record): string { } async function loadUsers() { - try { - const res: any = await userApi.dropdown() - const data = res.data ?? res - userOptions.value = (data || []).map((u: any) => { - const name = u.realName || u.username - const roles = u.roles || '' - return { - id: u.id, - realName: name, - displayName: roles ? `${name}(${roles})` : name, - } - }) - } catch { - userOptions.value = [] - } + // 旧 bx userApi 已移除;P3 再接门户用户下拉 + userOptions.value = [] } async function loadWfDef() {