diff --git a/bx-mobile/src/api/index.ts b/bx-mobile/src/api/index.ts index 14a463c5b..70af4eddf 100644 --- a/bx-mobile/src/api/index.ts +++ b/bx-mobile/src/api/index.ts @@ -31,6 +31,7 @@ export const advanceApi = { bxClient.get('/advances/approved-by-me', { params }), getById: (id: BxId) => bxClient.get(`/advances/${id}`), getApprovals: (id: BxId) => bxClient.get(`/advances/${id}/approvals`), + getApprovalHistory: (id: BxId) => bxClient.get(`/advances/${id}/approval-history`), 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`), @@ -42,7 +43,17 @@ export const advanceApi = { } export const reimburseApi = { - generateTitle: () => bxClient.get('/reimburses/generate-title'), + generateTitle: ( + reimburseDate?: string, + excludeApplyId?: BxId, + applicantId?: BxId, + ) => { + const params: Record = {} + if (reimburseDate) params.reimburseDate = reimburseDate + if (excludeApplyId) params.excludeApplyId = String(excludeApplyId) + if (applicantId) params.applicantId = String(applicantId) + return bxClient.get('/reimburses/generate-title', { params }) + }, list: (params?: Record) => bxClient.get('/reimburses/', { params }), my: (params?: Record) => bxClient.get('/reimburses/my', { params }), myStats: () => bxClient.get('/reimburses/my/stats'), @@ -52,10 +63,12 @@ export const reimburseApi = { bxClient.get('/reimburses/approved-by-me', { params }), getById: (id: BxId) => bxClient.get(`/reimburses/${id}`), getApprovals: (id: BxId) => bxClient.get(`/reimburses/${id}/approvals`), + getApprovalHistory: (id: BxId) => bxClient.get(`/reimburses/${id}/approval-history`), create: (data: Record) => bxClient.post('/reimburses/', data), update: (id: BxId, data: Record) => bxClient.put(`/reimburses/${id}`, data), submit: (id: BxId, data?: Record) => bxClient.post(`/reimburses/${id}/submit`, data), + withdraw: (id: BxId) => bxClient.post(`/reimburses/${id}/withdraw`), approve: (id: BxId, data: { approvalOpinion?: string }) => bxClient.post(`/reimburses/${id}/approve`, data), reject: (id: BxId, data: { approvalOpinion?: string }) => diff --git a/bx-mobile/src/api/portalAdmin.ts b/bx-mobile/src/api/portalAdmin.ts new file mode 100644 index 000000000..c7e301e5e --- /dev/null +++ b/bx-mobile/src/api/portalAdmin.ts @@ -0,0 +1,6 @@ +import portalClient from './portalClient' + +/** 门户用户下拉(与 web-admin fetchUserOptions 同源) */ +export const portalAdminApi = { + userOptions: () => portalClient.get('/web/admin/users/options'), +} diff --git a/bx-mobile/src/constants/bxMenuCatalog.ts b/bx-mobile/src/constants/bxMenuCatalog.ts index 4d9f0a91d..8687c5c36 100644 --- a/bx-mobile/src/constants/bxMenuCatalog.ts +++ b/bx-mobile/src/constants/bxMenuCatalog.ts @@ -141,4 +141,4 @@ export const BX_MENU_CATALOG: BxMenuEntry[] = [ ] /** 当前已开放阶段:发版时改为 1 → 2 → 3 */ -export const BX_MENU_CURRENT_PHASE: BxMenuPhase = 2 +export const BX_MENU_CURRENT_PHASE: BxMenuPhase = 3 diff --git a/bx-mobile/src/hooks/useApplicantOptions.ts b/bx-mobile/src/hooks/useApplicantOptions.ts new file mode 100644 index 000000000..6ebe90758 --- /dev/null +++ b/bx-mobile/src/hooks/useApplicantOptions.ts @@ -0,0 +1,21 @@ +import { ref } from 'vue' +import { portalAdminApi } from '@/api/portalAdmin' + +export function useApplicantOptions() { + const applicantOptions = ref<{ label: string; value: string }[]>([]) + + async function loadApplicantOptions() { + try { + const res: any = await portalAdminApi.userOptions() + const data = res?.data ?? res ?? [] + applicantOptions.value = (Array.isArray(data) ? data : []).map((u: any) => ({ + value: String(u.value ?? u.id ?? ''), + label: String(u.label ?? u.nickName ?? u.realName ?? u.username ?? ''), + })) + } catch { + applicantOptions.value = [] + } + } + + return { applicantOptions, loadApplicantOptions } +} diff --git a/bx-mobile/src/router/index.ts b/bx-mobile/src/router/index.ts index 414157352..031c2d15f 100644 --- a/bx-mobile/src/router/index.ts +++ b/bx-mobile/src/router/index.ts @@ -129,7 +129,25 @@ const routes = [ component: () => import('@/views/invoice/InvoiceWalletAdminPage.vue'), meta: { menuCodes: ['bx:invoiceWallet:admin'] }, }, - /* P3 再注册 admin 配置;IAM(users/roles/menus)已移除 */ + { + path: 'admin/expenses', + name: 'ExpenseProject', + component: () => import('@/views/admin/ExpenseProjectPage.vue'), + meta: { menuCodes: ['bx:expenseItem:list'] }, + }, + { + path: 'admin/wf-advance', + name: 'WfAdvance', + component: () => import('@/views/admin/WfAdvancePage.vue'), + meta: { menuCodes: ['bx:workflowAdvance:list'] }, + }, + { + path: 'admin/wf-reimburse', + name: 'WfReimburse', + component: () => import('@/views/admin/WfReimbursePage.vue'), + meta: { menuCodes: ['bx:workflowReimburse:list'] }, + }, + /* IAM(users/roles/menus)已移除 */ { path: 'profile', name: 'Profile', component: () => import('@/views/profile/ProfilePage.vue') }, ], }, diff --git a/bx-mobile/src/views/admin/ExpenseProjectPage.vue b/bx-mobile/src/views/admin/ExpenseProjectPage.vue index 39d1423d6..a09f094cd 100644 --- a/bx-mobile/src/views/admin/ExpenseProjectPage.vue +++ b/bx-mobile/src/views/admin/ExpenseProjectPage.vue @@ -27,9 +27,9 @@
- + - + {{ item.itemName }} @@ -41,7 +41,7 @@
-
+
项目编码
@@ -191,7 +191,7 @@ const router = useRouter() const expenseTree = ref([]) const loading = ref(false) const refreshing = ref(false) -const expandedIds = ref([]) +const expandedIds = ref([]) const showForm = ref(false) const showParentPicker = ref(false) @@ -200,29 +200,29 @@ const loadingMap = reactive>({}) const dialogMode = ref<'add' | 'addChild' | 'edit'>('add') const form = reactive({ - id: 0, + id: '' as string, itemName: '', itemCode: '', level: 1, - parentId: 0, + parentId: '0' as string, sortOrder: '1', status: 1, remark: '', }) const statusSwitch = computed({ - get: () => form.status === 1, + get: () => form.status === 1 || form.status === ('1' as any), set: (v: boolean) => { form.status = v ? 1 : 0 }, }) -const parentOptions = computed(() => expenseTree.value.filter(r => r.level === 1)) +const parentOptions = computed(() => expenseTree.value.filter((r: any) => Number(r.level) === 1)) const parentColumns = computed(() => - parentOptions.value.map(p => ({ text: p.itemName, value: p.id })) + parentOptions.value.map((p: any) => ({ text: p.itemName, value: String(p.id) })), ) const parentDisplay = computed(() => { - if (form.parentId === 0) return '' - return parentOptions.value.find(p => p.id === form.parentId)?.itemName ?? '' + if (!form.parentId || form.parentId === '0') return '' + return parentOptions.value.find((p: any) => String(p.id) === form.parentId)?.itemName ?? '' }) const dialogTitle = computed(() => { @@ -231,10 +231,11 @@ const dialogTitle = computed(() => { return '编辑项目' }) -function toggleExpand(id: number) { - const idx = expandedIds.value.indexOf(id) +function toggleExpand(id: string | number) { + const sid = String(id) + const idx = expandedIds.value.indexOf(sid) if (idx === -1) { - expandedIds.value.push(id) + expandedIds.value.push(sid) } else { expandedIds.value.splice(idx, 1) } @@ -242,8 +243,14 @@ function toggleExpand(id: number) { function resetForm() { Object.assign(form, { - id: 0, itemName: '', itemCode: '', level: 1, - parentId: 0, sortOrder: '1', status: 1, remark: '', + id: '', + itemName: '', + itemCode: '', + level: 1, + parentId: '0', + sortOrder: '1', + status: 1, + remark: '', }) } @@ -252,7 +259,7 @@ function openAddDialog() { isEdit.value = false resetForm() form.level = 1 - form.parentId = 0 + form.parentId = '0' showForm.value = true } @@ -261,7 +268,7 @@ function openAddChildDialog(parent: any) { isEdit.value = false resetForm() form.level = 2 - form.parentId = parent.id + form.parentId = String(parent.id) showForm.value = true } @@ -269,11 +276,11 @@ function openEditDialog(item: any) { dialogMode.value = 'edit' isEdit.value = true Object.assign(form, { - id: item.id, + id: String(item.id), itemName: item.itemName ?? '', itemCode: item.itemCode ?? '', level: item.level ?? 1, - parentId: item.parentId ?? 0, + parentId: item.parentId == null || item.parentId === 0 ? '0' : String(item.parentId), sortOrder: String(item.sortOrder ?? 1), status: item.status ?? 1, remark: item.remark ?? '', @@ -283,7 +290,7 @@ function openEditDialog(item: any) { function onParentConfirm({ selectedOptions }: any) { if (selectedOptions.length > 0) { - form.parentId = selectedOptions[0].value + form.parentId = String(selectedOptions[0].value) } showParentPicker.value = false } @@ -293,7 +300,7 @@ async function loadTree() { try { const res: any = await expenseItemApi.tree() expenseTree.value = res.data ?? [] - expandedIds.value = expenseTree.value.map(m => m.id) + expandedIds.value = expenseTree.value.map((m: any) => String(m.id)) } catch (e: any) { showToast(e?.message || '加载数据失败') } finally { @@ -311,7 +318,7 @@ function validateExpenseItemForm(): boolean { showToast('请输入项目名称') return false } - if (form.level === 2 && (!form.parentId || form.parentId === 0)) { + if (form.level === 2 && (!form.parentId || form.parentId === '0')) { showToast('请选择上级项目') return false } @@ -329,7 +336,7 @@ async function handleSubmit() { itemName: form.itemName, itemCode: form.itemCode, level: form.level, - parentId: form.parentId, + parentId: form.parentId === '0' ? '0' : form.parentId, sortOrder, status: form.status, remark: form.remark, diff --git a/bx-mobile/src/views/admin/WfAdvancePage.vue b/bx-mobile/src/views/admin/WfAdvancePage.vue index 0b331aa3e..32419bab8 100644 --- a/bx-mobile/src/views/admin/WfAdvancePage.vue +++ b/bx-mobile/src/views/admin/WfAdvancePage.vue @@ -129,6 +129,7 @@ import { useRouter } from 'vue-router' import { showToast, showDialog } from 'vant' import { DELETE_CONFIRM_BUTTON_COLOR } from '@/constants/dialog' import { workflowApi } from '@/api' +import { portalAdminApi } from '@/api/portalAdmin' import { statusLabel, statusTagClass } from '@/utils/format' import StatusTag from '@/components/mobile/StatusTag.vue' @@ -143,21 +144,26 @@ const showApproverPicker = ref(false) const isEdit = ref(false) const loadingMap = reactive>({}) -const userOptions = ref<{ id: number; realName: string; displayName: string }[]>([]) +const userOptions = ref< + { id: string; realName: string; displayName: string }[] +>([]) const userColumns = computed(() => - userOptions.value.map(u => ({ text: u.displayName, value: u.id })) + userOptions.value.map((u) => ({ text: u.displayName, value: u.id })), ) const formData = reactive({ - editingNodeId: null as number | null, + editingNodeId: null as string | null, nodeName: '', - approverId: null as number | null, + approverId: null as string | null, approverName: '', }) const approverDisplay = computed(() => { - if (formData.approverId == null) return '' - return userOptions.value.find(u => u.id === formData.approverId)?.displayName ?? formData.approverName + if (formData.approverId == null || formData.approverId === '') return '' + return ( + userOptions.value.find((u) => u.id === String(formData.approverId))?.displayName ?? + formData.approverName + ) }) function nodeKey(node: Record): string { @@ -165,8 +171,21 @@ function nodeKey(node: Record): string { } async function loadUsers() { - // 旧 bx userApi 已移除;P3 再接门户用户下拉 - userOptions.value = [] + try { + const res: any = await portalAdminApi.userOptions() + const data = res?.data ?? res ?? [] + userOptions.value = (Array.isArray(data) ? data : []).map((u: any) => { + const name = u.label || u.nickName || u.realName || '' + const account = u.userNo || u.username || '' + return { + id: String(u.value ?? u.id ?? ''), + realName: name, + displayName: account ? `${name}(${account})` : name, + } + }) + } catch { + userOptions.value = [] + } } async function loadWfDef() { @@ -203,10 +222,11 @@ function openAddDialog() { function openEditDialog(node: Record) { isEdit.value = true + const aid = node.approverId ?? node.approver_id ?? null Object.assign(formData, { - editingNodeId: node.id ?? null, + editingNodeId: node.id != null ? String(node.id) : null, nodeName: node.nodeName ?? '', - approverId: node.approverId ?? node.approver_id ?? null, + approverId: aid != null && aid !== '' ? String(aid) : null, approverName: node.approverName ?? node.approver_name ?? '', }) showForm.value = true @@ -214,8 +234,8 @@ function openEditDialog(node: Record) { function onApproverConfirm({ selectedOptions }: any) { if (selectedOptions.length > 0) { - formData.approverId = selectedOptions[0].value - formData.approverName = selectedOptions[0].text.split('(')[0] + formData.approverId = String(selectedOptions[0].value) + formData.approverName = String(selectedOptions[0].text || '').split('(')[0] } showApproverPicker.value = false } @@ -235,7 +255,7 @@ async function handleSubmit() { try { if (isEdit.value && formData.editingNodeId != null) { const newNodes = nodes.value.map((n: Record) => - n.id === formData.editingNodeId + String(n.id) === formData.editingNodeId ? { ...n, nodeName: formData.nodeName, approverId: formData.approverId, approverName: formData.approverName } : n ) diff --git a/bx-mobile/src/views/admin/WfReimbursePage.vue b/bx-mobile/src/views/admin/WfReimbursePage.vue index e0e9a6be2..553f729e6 100644 --- a/bx-mobile/src/views/admin/WfReimbursePage.vue +++ b/bx-mobile/src/views/admin/WfReimbursePage.vue @@ -129,6 +129,7 @@ import { useRouter } from 'vue-router' import { showToast, showDialog } from 'vant' import { DELETE_CONFIRM_BUTTON_COLOR } from '@/constants/dialog' import { workflowApi } from '@/api' +import { portalAdminApi } from '@/api/portalAdmin' import { statusLabel, statusTagClass } from '@/utils/format' import StatusTag from '@/components/mobile/StatusTag.vue' @@ -143,21 +144,26 @@ const showApproverPicker = ref(false) const isEdit = ref(false) const loadingMap = reactive>({}) -const userOptions = ref<{ id: number; realName: string; displayName: string }[]>([]) +const userOptions = ref< + { id: string; realName: string; displayName: string }[] +>([]) const userColumns = computed(() => - userOptions.value.map(u => ({ text: u.displayName, value: u.id })) + userOptions.value.map((u) => ({ text: u.displayName, value: u.id })), ) const formData = reactive({ - editingNodeId: null as number | null, + editingNodeId: null as string | null, nodeName: '', - approverId: null as number | null, + approverId: null as string | null, approverName: '', }) const approverDisplay = computed(() => { - if (formData.approverId == null) return '' - return userOptions.value.find(u => u.id === formData.approverId)?.displayName ?? formData.approverName + if (formData.approverId == null || formData.approverId === '') return '' + return ( + userOptions.value.find((u) => u.id === String(formData.approverId))?.displayName ?? + formData.approverName + ) }) function nodeKey(node: Record): string { @@ -165,8 +171,21 @@ function nodeKey(node: Record): string { } async function loadUsers() { - // 旧 bx userApi 已移除;P3 再接门户用户下拉 - userOptions.value = [] + try { + const res: any = await portalAdminApi.userOptions() + const data = res?.data ?? res ?? [] + userOptions.value = (Array.isArray(data) ? data : []).map((u: any) => { + const name = u.label || u.nickName || u.realName || '' + const account = u.userNo || u.username || '' + return { + id: String(u.value ?? u.id ?? ''), + realName: name, + displayName: account ? `${name}(${account})` : name, + } + }) + } catch { + userOptions.value = [] + } } async function loadWfDef() { @@ -203,10 +222,11 @@ function openAddDialog() { function openEditDialog(node: Record) { isEdit.value = true + const aid = node.approverId ?? node.approver_id ?? null Object.assign(formData, { - editingNodeId: node.id ?? null, + editingNodeId: node.id != null ? String(node.id) : null, nodeName: node.nodeName ?? '', - approverId: node.approverId ?? node.approver_id ?? null, + approverId: aid != null && aid !== '' ? String(aid) : null, approverName: node.approverName ?? node.approver_name ?? '', }) showForm.value = true @@ -214,8 +234,8 @@ function openEditDialog(node: Record) { function onApproverConfirm({ selectedOptions }: any) { if (selectedOptions.length > 0) { - formData.approverId = selectedOptions[0].value - formData.approverName = selectedOptions[0].text.split('(')[0] + formData.approverId = String(selectedOptions[0].value) + formData.approverName = String(selectedOptions[0].text || '').split('(')[0] } showApproverPicker.value = false } @@ -235,7 +255,7 @@ async function handleSubmit() { try { if (isEdit.value && formData.editingNodeId != null) { const newNodes = nodes.value.map((n: Record) => - n.id === formData.editingNodeId + String(n.id) === formData.editingNodeId ? { ...n, nodeName: formData.nodeName, approverId: formData.approverId, approverName: formData.approverName } : n ) diff --git a/bx-mobile/src/views/advance/AdvanceDetailPage.vue b/bx-mobile/src/views/advance/AdvanceDetailPage.vue index f62d472fa..c8505c85a 100644 --- a/bx-mobile/src/views/advance/AdvanceDetailPage.vue +++ b/bx-mobile/src/views/advance/AdvanceDetailPage.vue @@ -384,15 +384,29 @@ async function loadDetail() { return } - // 加载审批流程 + // 审批历史优先;失败则回退当前审批节点 try { - const apprRes: any = await advanceApi.getApprovals(id) - const nodes = apprRes.data || [] - approvals.value = nodes.sort( - (a: any, b: any) => (a.nodeOrder ?? 0) - (b.nodeOrder ?? 0) - ) + const histRes: any = await advanceApi.getApprovalHistory(id) + const hist = histRes.data || [] + if (Array.isArray(hist) && hist.length) { + approvals.value = hist + } else { + const apprRes: any = await advanceApi.getApprovals(id) + const nodes = apprRes.data || [] + approvals.value = nodes.sort( + (a: any, b: any) => (a.nodeOrder ?? 0) - (b.nodeOrder ?? 0), + ) + } } catch { - // 审批流程加载失败不影响详情 + try { + const apprRes: any = await advanceApi.getApprovals(id) + const nodes = apprRes.data || [] + approvals.value = nodes.sort( + (a: any, b: any) => (a.nodeOrder ?? 0) - (b.nodeOrder ?? 0), + ) + } catch { + // 审批流程加载失败不影响详情 + } } // 加载附件 diff --git a/bx-mobile/src/views/approval/ApprovalDetailPage.vue b/bx-mobile/src/views/approval/ApprovalDetailPage.vue index f2a3f8b24..577043414 100644 --- a/bx-mobile/src/views/approval/ApprovalDetailPage.vue +++ b/bx-mobile/src/views/approval/ApprovalDetailPage.vue @@ -427,13 +427,29 @@ async function loadDetail() { detail.value = res.data || res } - // Load approval flow - const apprRes: any = await api.getApprovals(id) - if (apprRes?.code === 200 || apprRes?.data) { - const nodes = apprRes.data || apprRes - approvals.value = Array.isArray(nodes) - ? nodes.sort((a: any, b: any) => (a.nodeOrder ?? 0) - (b.nodeOrder ?? 0)) - : [] + // 审批历史优先;失败则回退当前审批节点 + try { + const histRes: any = await api.getApprovalHistory(id) + const hist = histRes?.data ?? histRes + if (Array.isArray(hist) && hist.length) { + approvals.value = hist + } else { + const apprRes: any = await api.getApprovals(id) + const nodes = apprRes?.data ?? apprRes + approvals.value = Array.isArray(nodes) + ? nodes.sort((a: any, b: any) => (a.nodeOrder ?? 0) - (b.nodeOrder ?? 0)) + : [] + } + } catch { + try { + const apprRes: any = await api.getApprovals(id) + const nodes = apprRes?.data ?? apprRes + approvals.value = Array.isArray(nodes) + ? nodes.sort((a: any, b: any) => (a.nodeOrder ?? 0) - (b.nodeOrder ?? 0)) + : [] + } catch { + approvals.value = [] + } } // Load attachments(申请) diff --git a/bx-mobile/src/views/reimburse/ReimburseApplyPage.vue b/bx-mobile/src/views/reimburse/ReimburseApplyPage.vue index c796f5868..c9be7d2c8 100644 --- a/bx-mobile/src/views/reimburse/ReimburseApplyPage.vue +++ b/bx-mobile/src/views/reimburse/ReimburseApplyPage.vue @@ -18,6 +18,26 @@

报销信息

{{ editingId ? '编辑' : '新建' }}
+
+ +
+
+
+ {{ applicantLabel || '请选择申请人' }} +
+
+ +
+
+
+ + +
+ + + +
@@ -248,11 +277,21 @@ import { formatExpenseName, type ExpenseItem, } from '@/hooks/useExpenseItemTree' +import { useApplicantOptions } from '@/hooks/useApplicantOptions' +import { useAuthStore } from '@/stores/auth' import { SUBMIT_APPROVE_BUTTON_COLOR } from '@/constants/dialog' import BottomActionBar from '@/components/mobile/BottomActionBar.vue' const router = useRouter() const route = useRoute() +const auth = useAuthStore() + +function todayYmd() { + const d = new Date() + const m = String(d.getMonth() + 1).padStart(2, '0') + const day = String(d.getDate()).padStart(2, '0') + return `${d.getFullYear()}-${m}-${day}` +} interface DetailLine { expenseItemId: number | string @@ -263,6 +302,8 @@ interface DetailLine { const form = reactive({ title: '', remark: '', + applicantId: '' as string, + reimburseDate: todayYmd(), details: [{ expenseItemId: '' as string | number, amount: '', remark: '' }] as DetailLine[], }) @@ -271,8 +312,44 @@ const editingId = ref(null) const showExpensePicker = ref(false) const pickingLineIndex = ref(0) const selectedLeftId = ref(null) +const showApplicantPicker = ref(false) const { expenseTree, flatList, load: loadExpenseItems } = useExpenseItemTree() +const { applicantOptions, loadApplicantOptions } = useApplicantOptions() + +const applicantLabel = computed(() => { + const opt = applicantOptions.value.find((o) => o.value === form.applicantId) + return opt?.label || '' +}) + +const applicantPickerColumns = computed(() => + applicantOptions.value.map((o) => ({ text: o.label, value: o.value })), +) + +function onApplicantConfirm({ selectedOptions }: any) { + const opt = selectedOptions?.[0] + if (opt?.value) { + form.applicantId = String(opt.value) + refreshTitle() + refreshWallet() + } + showApplicantPicker.value = false +} + +async function refreshTitle() { + if (!form.reimburseDate) return + try { + const res: any = await reimburseApi.generateTitle( + form.reimburseDate, + editingId.value || undefined, + form.applicantId || undefined, + ) + const t = res?.data + form.title = typeof t === 'string' ? t : t?.title || form.title + } catch { + /* keep */ + } +} const walletBalance = ref(0) const walletInvoices = ref([]) @@ -308,9 +385,10 @@ function money(v: unknown) { async function refreshWallet() { try { + const owner = form.applicantId || undefined const [sumRes, invRes]: any[] = await Promise.all([ - invoiceWalletApi.mineSummary(), - invoiceApi.list({ page: 1, pageSize: 200 }), + invoiceWalletApi.mineSummary(owner ? { userId: owner } : undefined), + invoiceApi.list({ page: 1, pageSize: 200, userId: owner }), ]) walletBalance.value = Number(sumRes?.data?.balance ?? 0) const d = invRes?.data @@ -485,6 +563,8 @@ async function onSubmit() { const payload = { title: form.title || undefined, remark: form.remark || undefined, + applicantId: form.applicantId || undefined, + reimburseDate: form.reimburseDate || undefined, details: validDetails.map((d) => ({ expenseItemId: String(d.expenseItemId), amount: Number(d.amount), @@ -563,6 +643,8 @@ async function loadEditData() { const data = res.data || res form.title = data.title || '' form.remark = data.remark || '' + form.applicantId = String(data.applicantId || auth.user?.id || '') + form.reimburseDate = data.reimburseDate || todayYmd() form.details = (data.details || []).map((d: any) => ({ expenseItemId: d.expenseId ?? d.expenseItemId ?? '', amount: String(d.amount ?? ''), @@ -573,9 +655,7 @@ async function loadEditData() { } const linked = data.invoices || [] selectedInvoiceIds.value = linked.map((x: any) => String(x.id || x.invoiceId)).filter(Boolean) - if (data.status && data.status !== 'draft' && data.status !== 'rejected') { - showToast('旧草稿若仍含本地发票附件,请改走发票夹关联') - } + await refreshWallet() } catch (e: any) { showToast(e?.message || '加载数据失败') } @@ -596,17 +676,13 @@ watch( onMounted(async () => { sessionStorage.removeItem('bx_edit_resubmit') - await Promise.all([loadExpenseItems(), refreshWallet()]) + if (!form.applicantId) form.applicantId = String(auth.user?.id || '') + await Promise.all([loadExpenseItems(), loadApplicantOptions(), refreshWallet()]) await loadEditData() - if (!editingId.value && !form.title) { - try { - const res: any = await reimburseApi.generateTitle() - if (res.data) { - form.title = typeof res.data === 'string' ? res.data : res.data.title || '' - } - } catch { - /* skip */ - } + if (!editingId.value) { + if (!form.applicantId) form.applicantId = String(auth.user?.id || '') + if (!form.reimburseDate) form.reimburseDate = todayYmd() + await refreshTitle() } }) diff --git a/bx-mobile/src/views/reimburse/ReimburseDetailPage.vue b/bx-mobile/src/views/reimburse/ReimburseDetailPage.vue index a2966b9ec..88858dee1 100644 --- a/bx-mobile/src/views/reimburse/ReimburseDetailPage.vue +++ b/bx-mobile/src/views/reimburse/ReimburseDetailPage.vue @@ -456,6 +456,15 @@ :disabled="deleteLoading" @click="handleDelete" >删除 +