fix: 修复员工服务、报销单审批及前端交互细节
- 修复员工创建时组织架构关联与邮箱校验逻辑 - 修复报销单API端点参数及预审流程调用 - 优化审批中心、差旅详情等前端页面交互 - 更新侧边栏导航与请求视图模型 - 补充员工服务与报销单相关测试用例
This commit is contained in:
@@ -71,7 +71,7 @@ const sidebarMeta = {
|
||||
overview: { label: '总览' },
|
||||
workbench: { label: '个人工作台' },
|
||||
requests: { label: '个人报销' },
|
||||
approval: { label: '审批中心', badge: '12' },
|
||||
approval: { label: '审批中心' },
|
||||
policies: { label: '知识管理' },
|
||||
audit: { label: '任务规则中心' },
|
||||
logs: { label: '日志管理' },
|
||||
|
||||
@@ -112,7 +112,11 @@ function resolveApprovalMeta(status) {
|
||||
return { key: 'draft', label: '草稿', tone: 'draft' }
|
||||
}
|
||||
|
||||
if (['supplement', 'returned'].includes(normalized)) {
|
||||
if (normalized === 'returned') {
|
||||
return { key: 'supplement', label: '待提交', tone: 'warning' }
|
||||
}
|
||||
|
||||
if (normalized === 'supplement') {
|
||||
return { key: 'supplement', label: '待补充', tone: 'warning' }
|
||||
}
|
||||
|
||||
@@ -128,6 +132,10 @@ function resolveApprovalMeta(status) {
|
||||
}
|
||||
|
||||
function resolveWorkflowNode(claim, approvalMeta) {
|
||||
if (String(claim?.status || '').trim().toLowerCase() === 'returned') {
|
||||
return '待提交'
|
||||
}
|
||||
|
||||
const rawNode = String(claim?.approval_stage || '').trim()
|
||||
|
||||
if (rawNode) {
|
||||
|
||||
@@ -74,7 +74,7 @@ const BACKEND_STATUS_META = {
|
||||
paid: { key: 'completed', label: '已完成', tone: 'success' },
|
||||
completed: { key: 'completed', label: '已完成', tone: 'success' },
|
||||
supplement: { key: 'supplement', label: '待补充', tone: 'warning' },
|
||||
returned: { key: 'supplement', label: '待补充', tone: 'warning' },
|
||||
returned: { key: 'supplement', label: '待提交', tone: 'warning' },
|
||||
rejected: { key: 'rejected', label: '已退回', tone: 'danger' },
|
||||
cancelled: { key: 'rejected', label: '已退回', tone: 'danger' }
|
||||
}
|
||||
|
||||
@@ -535,7 +535,7 @@
|
||||
badge="退回单据"
|
||||
badge-tone="warning"
|
||||
:title="`确认退回 ${selectedRow?.id || ''} 吗?`"
|
||||
description="退回后该单据会进入待补充状态,申请人需要补充后重新提交。"
|
||||
description="退回后该单据会回到待提交状态,申请人需要调整后重新提交并再次经过 AI 预审。"
|
||||
cancel-text="取消"
|
||||
confirm-text="确认退回"
|
||||
busy-text="退回中..."
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
<span>智能录入</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="isDraftRequest"
|
||||
v-if="isEditableRequest"
|
||||
class="smart-entry-btn secondary"
|
||||
type="button"
|
||||
:disabled="actionBusy"
|
||||
@@ -104,7 +104,7 @@
|
||||
<th class="col-amount">金额</th>
|
||||
<th class="col-attachment">附件材料</th>
|
||||
<th v-if="hasExpenseRiskColumn" class="col-risk">系统校验</th>
|
||||
<th v-if="isDraftRequest" class="col-action">操作</th>
|
||||
<th v-if="isEditableRequest" class="col-action">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -297,7 +297,7 @@
|
||||
</p>
|
||||
</template>
|
||||
</td>
|
||||
<td v-if="isDraftRequest" class="expense-action-cell col-action">
|
||||
<td v-if="isEditableRequest" class="expense-action-cell col-action">
|
||||
<div v-if="editingExpenseId === item.id" class="row-action-group">
|
||||
<button
|
||||
class="inline-action primary"
|
||||
@@ -366,7 +366,7 @@
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article v-if="isDraftRequest" class="detail-card panel validation-card">
|
||||
<article v-if="isEditableRequest" class="detail-card panel validation-card">
|
||||
<div class="validation-head">
|
||||
<div>
|
||||
<h3>AI建议</h3>
|
||||
@@ -393,10 +393,10 @@
|
||||
<i class="mdi mdi-arrow-left"></i>
|
||||
<span>返回报销列表</span>
|
||||
</button>
|
||||
<div v-if="isDraftRequest" class="approval-action-group" aria-label="申请操作">
|
||||
<div v-if="isEditableRequest" class="approval-action-group" aria-label="申请操作">
|
||||
<button class="reject-action" type="button" :disabled="actionBusy" @click="handleDeleteRequest">
|
||||
<i class="mdi mdi-trash-can-outline"></i>
|
||||
{{ deleteBusy ? '删除中' : '删除草稿' }}
|
||||
{{ deleteBusy ? '删除中' : deleteActionLabel }}
|
||||
</button>
|
||||
<button class="approve-action" type="button" :disabled="!canSubmit" @click="handleSubmit">
|
||||
<i class="mdi mdi-send-circle-outline"></i>
|
||||
@@ -500,7 +500,7 @@
|
||||
badge="退回单据"
|
||||
badge-tone="warning"
|
||||
:title="`确认退回 ${request.id} 吗?`"
|
||||
description="退回后该单据会进入待补充状态,申请人需要补充后重新提交。"
|
||||
description="退回后该单据会回到待提交状态,申请人需要调整后重新提交并再次经过 AI 预审。"
|
||||
cancel-text="取消"
|
||||
confirm-text="确认退回"
|
||||
busy-text="退回中..."
|
||||
|
||||
@@ -428,9 +428,9 @@ export default {
|
||||
actionBusy.value = true
|
||||
try {
|
||||
await returnExpenseClaim(row.claimId, {
|
||||
reason: '审批中心退回,请申请人补充后重新提交。'
|
||||
reason: '审批中心退回,请申请人调整后重新提交。'
|
||||
})
|
||||
toast(`${row.id} 已退回待补充。`)
|
||||
toast(`${row.id} 已退回待提交。`)
|
||||
returnDialogOpen.value = false
|
||||
selectedClaimId.value = ''
|
||||
await reload()
|
||||
|
||||
@@ -264,21 +264,21 @@ function formatEmployeeHistoryTime(value) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const matched = raw.match(
|
||||
/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?$/
|
||||
const chineseMatched = raw.match(
|
||||
/^(\d{4})年(\d{1,2})月(\d{1,2})日(\d{1,2})时(\d{1,2})分(?:\d{1,2}秒)?$/
|
||||
)
|
||||
if (!matched) {
|
||||
return raw
|
||||
if (chineseMatched) {
|
||||
const [, year, month, day, hour, minute] = chineseMatched
|
||||
return `${year}年${Number(month)}月${Number(day)}日${Number(hour)}时${Number(minute)}分`
|
||||
}
|
||||
|
||||
const year = Number.parseInt(matched[1], 10)
|
||||
const month = Number.parseInt(matched[2], 10)
|
||||
const day = Number.parseInt(matched[3], 10)
|
||||
const hour = Number.parseInt(matched[4] || '0', 10)
|
||||
const minute = Number.parseInt(matched[5] || '0', 10)
|
||||
const second = Number.parseInt(matched[6] || '0', 10)
|
||||
const isoMatched = raw.match(/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}))?/)
|
||||
if (isoMatched) {
|
||||
const [, year, month, day, hour = '0', minute = '0'] = isoMatched
|
||||
return `${year}年${Number(month)}月${Number(day)}日${Number(hour)}时${Number(minute)}分`
|
||||
}
|
||||
|
||||
return `${year}年${month}月${day}日${hour}时${minute}分${second}秒`
|
||||
return raw.replace(/(\d{1,2}分)\d{1,2}秒$/, '$1')
|
||||
}
|
||||
|
||||
function resolveOrganizationUnitCode(employee) {
|
||||
|
||||
@@ -454,8 +454,9 @@ export default {
|
||||
|
||||
const isTravelRequest = computed(() => request.value.detailVariant === 'travel')
|
||||
const isDraftRequest = computed(() => request.value.approvalKey === 'draft')
|
||||
const isEditableRequest = computed(() => ['draft', 'supplement'].includes(request.value.approvalKey))
|
||||
const canManageCurrentClaim = computed(() => canManageExpenseClaims(currentUser.value))
|
||||
const canDeleteRequest = computed(() => isDraftRequest.value || canManageCurrentClaim.value)
|
||||
const canDeleteRequest = computed(() => isEditableRequest.value || canManageCurrentClaim.value)
|
||||
const canReturnRequest = computed(() =>
|
||||
canManageCurrentClaim.value
|
||||
&& request.value.approvalKey === 'in_progress'
|
||||
@@ -584,7 +585,7 @@ export default {
|
||||
const uploadedExpenseCount = computed(() => expenseItems.value.filter((item) => item.attachments.length).length)
|
||||
const hasExpenseRiskColumn = computed(() => expenseItems.value.some((item) => item.attachments.length))
|
||||
const expenseTableColumnCount = computed(
|
||||
() => 5 + (hasExpenseRiskColumn.value ? 1 : 0) + (isDraftRequest.value ? 1 : 0)
|
||||
() => 5 + (hasExpenseRiskColumn.value ? 1 : 0) + (isEditableRequest.value ? 1 : 0)
|
||||
)
|
||||
const expenseSummaryText = computed(
|
||||
() => request.value.expenseTableSummary || '请继续补充票据、说明和系统校验结果。'
|
||||
@@ -595,9 +596,9 @@ export default {
|
||||
|| '暂无附加说明。可在这里补充特殊背景、例外原因、补件计划或其他需要财务和审批人重点关注的信息。'
|
||||
)
|
||||
const draftBlockingIssues = computed(() =>
|
||||
isDraftRequest.value ? buildDraftBlockingIssues(request.value, expenseItems.value) : []
|
||||
isEditableRequest.value ? buildDraftBlockingIssues(request.value, expenseItems.value) : []
|
||||
)
|
||||
const canSubmit = computed(() => isDraftRequest.value && draftBlockingIssues.value.length === 0 && !actionBusy.value)
|
||||
const canSubmit = computed(() => isEditableRequest.value && draftBlockingIssues.value.length === 0 && !actionBusy.value)
|
||||
const locationInputPlaceholder = computed(() => resolveLocationInputPlaceholder(expenseEditor.itemType))
|
||||
|
||||
function applyLocalExpenseItemPatch(itemId, patch) {
|
||||
@@ -807,7 +808,7 @@ export default {
|
||||
})
|
||||
|
||||
function startExpenseEdit(item) {
|
||||
if (!isDraftRequest.value || actionBusy.value) {
|
||||
if (!isEditableRequest.value || actionBusy.value) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -850,7 +851,7 @@ export default {
|
||||
}
|
||||
|
||||
async function handleAddExpenseItem() {
|
||||
if (!isDraftRequest.value || actionBusy.value) {
|
||||
if (!isEditableRequest.value || actionBusy.value) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -884,7 +885,7 @@ export default {
|
||||
}
|
||||
|
||||
function triggerExpenseUpload(item) {
|
||||
if (!isDraftRequest.value || actionBusy.value) {
|
||||
if (!isEditableRequest.value || actionBusy.value) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1198,10 +1199,10 @@ export default {
|
||||
returnBusy.value = true
|
||||
try {
|
||||
await returnExpenseClaim(request.value.claimId, {
|
||||
reason: '详情页退回,请申请人补充后重新提交。'
|
||||
reason: '详情页退回,请申请人调整后重新提交。'
|
||||
})
|
||||
returnDialogOpen.value = false
|
||||
toast(`${request.value.id} 已退回待补充。`)
|
||||
toast(`${request.value.id} 已退回待提交。`)
|
||||
emit('request-updated', { claimId: request.value.claimId })
|
||||
} catch (error) {
|
||||
toast(error?.message || '退回单据失败,请稍后重试。')
|
||||
@@ -1270,6 +1271,7 @@ export default {
|
||||
hasExpenseRiskColumn,
|
||||
heroFactItems,
|
||||
isDraftRequest,
|
||||
isEditableRequest,
|
||||
isTravelRequest,
|
||||
locationInputPlaceholder,
|
||||
openAiEntry,
|
||||
|
||||
Reference in New Issue
Block a user