feat(bx-mobile): add bxClient and retarget business APIs to shanxia2.0

Remove legacy bx user/role/menu/auth APIs. Wire auth store to portal
temporarily so the app still typechecks; IAM admin pages use local stubs
until Task 6 drops their routes.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-08-09 16:06:12 +08:00
1 parent c7a8930ad6
commit 3f1faeae34
9 files changed
+340 -262

No files matched your search

+96
View File
@@ -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<boolean> | null = null
async function tryRefresh(): Promise<boolean> {
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
+1 -64
View File
@@ -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'
+76 -116
View File
@@ -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<string, any>) => client.get('/users/', { params }),
dropdown: () => client.get('/users/dropdown'),
getById: (id: number) => client.get(`/users/${id}`),
create: (data: Record<string, any>) => client.post('/users/', data),
update: (id: number, data: Record<string, any>) => 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<string, any>) => client.get('/roles/', { params }),
getById: (id: number) => client.get(`/roles/${id}`),
create: (data: Record<string, any>) => client.post('/roles/', data),
update: (id: number, data: Record<string, any>) => 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<string, any>) => client.post('/menus/', data),
update: (id: number, data: Record<string, any>) => 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<string, any>) => client.post('/expense-items/', data),
update: (id: number, data: Record<string, any>) => 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<string, any>) => bxClient.post('/expense-items/', data),
update: (id: BxId, data: Record<string, any>) => bxClient.put(`/expense-items/${id}`, data),
delete: (id: BxId) => bxClient.delete(`/expense-items/${id}`),
}
export const workflowApi = {
list: (params?: Record<string, any>) => client.get('/workflows/', { params }),
getByBizType: (bizType: string) => client.get(`/workflows/biz/${bizType}`),
getById: (id: number) => client.get(`/workflows/${id}`),
create: (data: Record<string, any>) => client.post('/workflows/', data),
update: (id: number, data: Record<string, any>) => client.put(`/workflows/${id}`, data),
saveNodes: (id: number, nodes: Record<string, any>[]) =>
client.put(`/workflows/${id}/nodes`, nodes),
delete: (id: number) => client.delete(`/workflows/${id}`),
list: (params?: Record<string, any>) => bxClient.get('/workflows/', { params }),
getByBizType: (bizType: string) => bxClient.get(`/workflows/biz/${bizType}`),
getById: (id: BxId) => bxClient.get(`/workflows/${id}`),
create: (data: Record<string, any>) => bxClient.post('/workflows/', data),
update: (id: BxId, data: Record<string, any>) => bxClient.put(`/workflows/${id}`, data),
saveNodes: (id: BxId, nodes: Record<string, any>[]) =>
bxClient.put(`/workflows/${id}/nodes`, nodes),
delete: (id: BxId) => bxClient.delete(`/workflows/${id}`),
}
export const advanceApi = {
list: (params?: Record<string, any>) => client.get('/advances/', { params }),
my: (params?: Record<string, any>) => client.get('/advances/my', { params }),
myStats: () => client.get('/advances/my/stats'),
list: (params?: Record<string, any>) => bxClient.get('/advances/', { params }),
my: (params?: Record<string, any>) => bxClient.get('/advances/my', { params }),
myStats: () => bxClient.get('/advances/my/stats'),
pendingApproval: (params?: Record<string, any>) =>
client.get('/advances/pending-approval', { params }),
bxClient.get('/advances/pending-approval', { params }),
approvedByMe: (params?: Record<string, any>) =>
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<string, any>) => client.post('/advances/', data),
update: (id: number, data: Record<string, any>) => 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<string, any>) => bxClient.post('/advances/', data),
update: (id: BxId, data: Record<string, any>) => 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<string, any>) => client.get('/reimburses/', { params }),
my: (params?: Record<string, any>) => client.get('/reimburses/my', { params }),
myStats: () => client.get('/reimburses/my/stats'),
generateTitle: () => bxClient.get('/reimburses/generate-title'),
list: (params?: Record<string, any>) => bxClient.get('/reimburses/', { params }),
my: (params?: Record<string, any>) => bxClient.get('/reimburses/my', { params }),
myStats: () => bxClient.get('/reimburses/my/stats'),
pendingApproval: (params?: Record<string, any>) =>
client.get('/reimburses/pending-approval', { params }),
bxClient.get('/reimburses/pending-approval', { params }),
approvedByMe: (params?: Record<string, any>) =>
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<string, any>) => client.post('/reimburses/', data),
update: (id: number, data: Record<string, any>) => 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<string, any>) => bxClient.post('/reimburses/', data),
update: (id: BxId, data: Record<string, any>) => 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<string, any>) => 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<string, any>) => 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<string, any>) =>
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<string, any>) =>
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<string, any>) =>
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<string, any>) => 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}`,
}
+100 -47
View File
@@ -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<UserInfo | null>(null)
const menuCodes = ref<string[]>([])
const isSuperAdmin = ref(false)
const allMenus = ref(false)
/** 兼容旧首页:Task 7 前保持空数组 */
const menuList = ref<MenuItem[]>([])
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,
}
})
+13 -1
View File
@@ -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'
+28 -1
View File
@@ -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'
+20 -1
View File
@@ -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'
+3 -16
View File
@@ -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, any>): 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() {
+3 -16
View File
@@ -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, any>): 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() {