feat(web): update views
- AppShellRouteView.vue: update app shell route view - AuditView.vue: update audit view - EmployeeManagementView.vue: update employee management view - PoliciesView.vue: update policies view - RequestsView.vue: update requests view - TravelReimbursementCreateView.vue: update travel form view - TravelRequestDetailView.vue: update travel detail view
This commit is contained in:
@@ -34,6 +34,7 @@
|
|||||||
:active-range="activeRange"
|
:active-range="activeRange"
|
||||||
:employee-summary="employeeSummary"
|
:employee-summary="employeeSummary"
|
||||||
:knowledge-summary="knowledgeSummary"
|
:knowledge-summary="knowledgeSummary"
|
||||||
|
:request-summary="requestSummary"
|
||||||
:custom-range="customRange"
|
:custom-range="customRange"
|
||||||
@update:search="search = $event"
|
@update:search="search = $event"
|
||||||
@update:active-range="activeRange = $event"
|
@update:active-range="activeRange = $event"
|
||||||
@@ -97,18 +98,24 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<TravelRequestDetailView
|
<TravelRequestDetailView
|
||||||
v-else-if="activeView === 'requests' && detailMode && selectedTravelRequest"
|
v-else-if="activeView === 'requests' && detailMode && selectedRequest"
|
||||||
:request="selectedTravelRequest"
|
:request="selectedRequest"
|
||||||
@back-to-requests="closeRequestDetail"
|
@back-to-requests="closeRequestDetail"
|
||||||
@open-assistant="openSmartEntry"
|
@open-assistant="openSmartEntry"
|
||||||
|
@request-updated="handleRequestUpdated"
|
||||||
|
@request-deleted="handleRequestDeleted"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<RequestsView
|
<RequestsView
|
||||||
v-else-if="activeView === 'requests'"
|
v-else-if="activeView === 'requests'"
|
||||||
:filtered-requests="filteredRequests"
|
:filtered-requests="filteredRequests"
|
||||||
|
:has-data="requests.length > 0"
|
||||||
|
:loading="requestsLoading"
|
||||||
|
:error="requestsError"
|
||||||
@ask="openRequestDetail"
|
@ask="openRequestDetail"
|
||||||
@approve="handleApprove"
|
@approve="handleApprove"
|
||||||
@reject="handleReject"
|
@reject="handleReject"
|
||||||
|
@reload="reloadRequests"
|
||||||
@create-request="openTravelCreate"
|
@create-request="openTravelCreate"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -129,6 +136,7 @@
|
|||||||
:entry-source="smartEntryContext.source"
|
:entry-source="smartEntryContext.source"
|
||||||
:request-context="smartEntryContext.request"
|
:request-context="smartEntryContext.request"
|
||||||
@close="closeSmartEntry"
|
@close="closeSmartEntry"
|
||||||
|
@draft-saved="handleDraftSaved"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -173,9 +181,12 @@ const {
|
|||||||
filteredRequests,
|
filteredRequests,
|
||||||
filters,
|
filters,
|
||||||
handleApprove,
|
handleApprove,
|
||||||
|
handleDraftSaved,
|
||||||
handleNavigate,
|
handleNavigate,
|
||||||
handleOpenChat,
|
handleOpenChat,
|
||||||
handleReject,
|
handleReject,
|
||||||
|
handleRequestDeleted,
|
||||||
|
handleRequestUpdated,
|
||||||
handleUpload,
|
handleUpload,
|
||||||
messageList,
|
messageList,
|
||||||
messages,
|
messages,
|
||||||
@@ -184,8 +195,13 @@ const {
|
|||||||
openSmartEntry,
|
openSmartEntry,
|
||||||
openTravelCreate,
|
openTravelCreate,
|
||||||
ranges,
|
ranges,
|
||||||
|
requestSummary,
|
||||||
|
requestsError,
|
||||||
|
requestsLoading,
|
||||||
|
reloadRequests,
|
||||||
|
requests,
|
||||||
search,
|
search,
|
||||||
selectedTravelRequest,
|
selectedRequest,
|
||||||
sendMessage,
|
sendMessage,
|
||||||
sending,
|
sending,
|
||||||
smartEntryContext,
|
smartEntryContext,
|
||||||
|
|||||||
@@ -605,16 +605,20 @@
|
|||||||
</article>
|
</article>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<Teleport to="body">
|
<ConfirmDialog
|
||||||
<div v-if="versionSwitchTarget" class="modal-backdrop" @click.self="cancelVersionSwitch">
|
:open="Boolean(versionSwitchTarget)"
|
||||||
<section class="version-modal panel" role="dialog" aria-modal="true" aria-labelledby="version-switch-title">
|
badge="切换版本"
|
||||||
<div class="card-head">
|
badge-tone="info"
|
||||||
<div>
|
title="切换规则版本"
|
||||||
<h3 id="version-switch-title">切换规则版本</h3>
|
description="切换后编辑器只会替换当前展示内容,不会直接回滚后端当前版本。"
|
||||||
<p>切换后编辑器只会替换当前展示内容,不会直接回滚后端当前版本。</p>
|
cancel-text="取消"
|
||||||
</div>
|
confirm-text="确认切换"
|
||||||
</div>
|
busy-text="切换中..."
|
||||||
|
confirm-tone="primary"
|
||||||
|
confirm-icon="mdi mdi-swap-horizontal"
|
||||||
|
@close="cancelVersionSwitch"
|
||||||
|
@confirm="confirmVersionSwitch"
|
||||||
|
>
|
||||||
<div class="version-modal-summary">
|
<div class="version-modal-summary">
|
||||||
<div>
|
<div>
|
||||||
<span>当前展示版本</span>
|
<span>当前展示版本</span>
|
||||||
@@ -623,22 +627,15 @@
|
|||||||
<i class="mdi mdi-arrow-right"></i>
|
<i class="mdi mdi-arrow-right"></i>
|
||||||
<div>
|
<div>
|
||||||
<span>目标版本</span>
|
<span>目标版本</span>
|
||||||
<strong>{{ versionSwitchTarget.version }}</strong>
|
<strong>{{ versionSwitchTarget?.version }}</strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="version-modal-note">
|
<div v-if="versionSwitchTarget" class="version-modal-note">
|
||||||
<strong>{{ versionSwitchTarget.note }}</strong>
|
<strong>{{ versionSwitchTarget.note }}</strong>
|
||||||
<span>{{ versionSwitchTarget.time }}</span>
|
<span>{{ versionSwitchTarget.time }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
</ConfirmDialog>
|
||||||
<footer class="modal-actions">
|
|
||||||
<button class="minor-action" type="button" @click="cancelVersionSwitch">取消</button>
|
|
||||||
<button class="major-action" type="button" @click="confirmVersionSwitch">确认切换</button>
|
|
||||||
</footer>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</Teleport>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -547,6 +547,22 @@
|
|||||||
</footer>
|
</footer>
|
||||||
</article>
|
</article>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
|
<ConfirmDialog
|
||||||
|
:open="disableDialogOpen"
|
||||||
|
badge="停用账号"
|
||||||
|
badge-tone="warning"
|
||||||
|
:title="`确认停用 ${selectedEmployee?.name || '该员工'} 的账号吗?`"
|
||||||
|
description="停用后该员工将无法继续登录系统,相关个人操作入口也会立即失效。"
|
||||||
|
cancel-text="取消"
|
||||||
|
confirm-text="确认停用"
|
||||||
|
busy-text="停用中..."
|
||||||
|
confirm-tone="danger"
|
||||||
|
confirm-icon="mdi mdi-account-cancel-outline"
|
||||||
|
:busy="actionState === 'disable'"
|
||||||
|
@close="closeDisableDialog"
|
||||||
|
@confirm="confirmDisableEmployeeAccount"
|
||||||
|
/>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -277,6 +277,22 @@
|
|||||||
</Transition>
|
</Transition>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ConfirmDialog
|
||||||
|
:open="deleteDialogOpen"
|
||||||
|
badge="删除文件"
|
||||||
|
badge-tone="danger"
|
||||||
|
:title="`确认删除文件“${deleteTargetDocument?.name || ''}”吗?`"
|
||||||
|
description="删除后该知识库文件及其当前版本记录将不可恢复,请确认本次操作。"
|
||||||
|
cancel-text="取消"
|
||||||
|
confirm-text="确认删除"
|
||||||
|
busy-text="删除中..."
|
||||||
|
confirm-tone="danger"
|
||||||
|
confirm-icon="mdi mdi-delete-outline"
|
||||||
|
:busy="Boolean(deletingId)"
|
||||||
|
@close="closeDeleteDialog"
|
||||||
|
@confirm="confirmDeleteDocument"
|
||||||
|
/>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="travel-page">
|
<section class="travel-page">
|
||||||
<article class="travel-list panel">
|
<article class="travel-list panel">
|
||||||
<nav class="status-tabs" aria-label="差旅报销状态">
|
<nav class="status-tabs" aria-label="个人报销状态">
|
||||||
<button
|
<button
|
||||||
v-for="tab in tabs"
|
v-for="tab in tabs"
|
||||||
:key="tab"
|
:key="tab"
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<div class="filter-set">
|
<div class="filter-set">
|
||||||
<div class="list-search">
|
<div class="list-search">
|
||||||
<i class="mdi mdi-magnify"></i>
|
<i class="mdi mdi-magnify"></i>
|
||||||
<input type="search" placeholder="搜索申请人、单号、费用类型..." />
|
<input v-model="listKeyword" type="search" placeholder="搜索单号、事由、报销类型..." />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="date-range-filter" :class="{ open: datePopover }">
|
<div class="date-range-filter" :class="{ open: datePopover }">
|
||||||
@@ -61,48 +61,64 @@
|
|||||||
<p class="hint"><i class="mdi mdi-information-outline"></i> 点击任意行可查看单据详情</p>
|
<p class="hint"><i class="mdi mdi-information-outline"></i> 点击任意行可查看单据详情</p>
|
||||||
|
|
||||||
<div class="table-wrap">
|
<div class="table-wrap">
|
||||||
<table>
|
<div v-if="loading" class="table-state">
|
||||||
|
<i class="mdi mdi-loading mdi-spin"></i>
|
||||||
|
<strong>正在加载真实报销数据</strong>
|
||||||
|
<p>列表将直接展示后端返回的个人报销单据。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="error" class="table-state error">
|
||||||
|
<i class="mdi mdi-alert-circle-outline"></i>
|
||||||
|
<strong>报销列表加载失败</strong>
|
||||||
|
<p>{{ error }}</p>
|
||||||
|
<button class="retry-btn" type="button" @click="emit('reload')">重新加载</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="showEmpty" class="table-state empty">
|
||||||
|
<i class="mdi mdi-inbox-arrow-down-outline"></i>
|
||||||
|
<strong>{{ emptyState.title }}</strong>
|
||||||
|
<p>{{ emptyState.desc }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table v-else>
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col class="col-id">
|
<col class="col-id">
|
||||||
<col class="col-reason">
|
<col class="col-type">
|
||||||
<col class="col-city">
|
<col class="col-title">
|
||||||
<col class="col-period">
|
<col class="col-occurred">
|
||||||
<col class="col-apply">
|
<col class="col-apply">
|
||||||
<col class="col-amount">
|
<col class="col-amount">
|
||||||
<col class="col-node">
|
<col class="col-node">
|
||||||
<col class="col-approval">
|
<col class="col-approval">
|
||||||
<col class="col-travel">
|
|
||||||
</colgroup>
|
</colgroup>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>单号</th>
|
<th>单号</th>
|
||||||
<th>出差事由</th>
|
<th>报销类型</th>
|
||||||
<th>出差城市</th>
|
<th>报销事由</th>
|
||||||
<th>出差时间</th>
|
<th>发生时间</th>
|
||||||
<th>申请时间</th>
|
<th>申请时间</th>
|
||||||
<th>申请金额</th>
|
<th>金额</th>
|
||||||
<th>当前节点</th>
|
<th>当前节点</th>
|
||||||
<th>审批状态</th>
|
<th>审批状态</th>
|
||||||
<th>商旅状态</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="row in visibleRows" :key="row.id" @click="emit('ask', row)">
|
<tr v-for="row in visibleRows" :key="row.id" @click="emit('ask', row)">
|
||||||
<td><strong class="doc-id">{{ row.id }}</strong></td>
|
<td><strong class="doc-id">{{ row.documentNo || row.id }}</strong></td>
|
||||||
|
<td><span class="type-tag" :class="row.typeTone">{{ row.typeLabel }}</span></td>
|
||||||
<td>{{ row.reason }}</td>
|
<td>{{ row.reason }}</td>
|
||||||
<td>{{ row.city }}</td>
|
<td>{{ row.occurredDisplay }}</td>
|
||||||
<td>{{ row.period }}</td>
|
|
||||||
<td>{{ row.applyTime }}</td>
|
<td>{{ row.applyTime }}</td>
|
||||||
<td>{{ row.amount }}</td>
|
<td>{{ row.amountDisplay }}</td>
|
||||||
<td>{{ row.node }}</td>
|
<td>{{ row.node }}</td>
|
||||||
<td><span class="status-tag" :class="row.approvalTone">{{ row.approval }}</span></td>
|
<td><span class="status-tag" :class="row.approvalTone">{{ row.approval }}</span></td>
|
||||||
<td><span class="status-tag" :class="row.travelTone">{{ row.travel }}</span></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="list-foot">
|
<footer v-if="showTable" class="list-foot">
|
||||||
<span class="page-summary">共 {{ totalCount }} 条,目前第 {{ currentPage }} 页</span>
|
<span class="page-summary">共 {{ totalCount }} 条,目前第 {{ currentPage }} 页</span>
|
||||||
<div class="pager" aria-label="分页">
|
<div class="pager" aria-label="分页">
|
||||||
<button class="page-nav" type="button" :disabled="currentPage === 1" aria-label="上一页" @click="currentPage--"><i class="mdi mdi-chevron-left"></i></button>
|
<button class="page-nav" type="button" :disabled="currentPage === 1" aria-label="上一页" @click="currentPage--"><i class="mdi mdi-chevron-left"></i></button>
|
||||||
|
|||||||
@@ -281,7 +281,11 @@
|
|||||||
v-for="item in reviewFactCards"
|
v-for="item in reviewFactCards"
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
class="review-side-metric-card"
|
class="review-side-metric-card"
|
||||||
:class="{ editable: item.editor, editing: reviewInlineEditorKey === item.key }"
|
:class="{
|
||||||
|
editable: item.editor,
|
||||||
|
editing: reviewInlineEditorKey === item.key,
|
||||||
|
invalid: Boolean(reviewInlineErrors[item.key])
|
||||||
|
}"
|
||||||
@click="openInlineReviewEditor(item.key)"
|
@click="openInlineReviewEditor(item.key)"
|
||||||
>
|
>
|
||||||
<span class="review-side-metric-icon">
|
<span class="review-side-metric-icon">
|
||||||
@@ -289,39 +293,46 @@
|
|||||||
</span>
|
</span>
|
||||||
<div class="review-side-metric-copy">
|
<div class="review-side-metric-copy">
|
||||||
<small>{{ item.label }}</small>
|
<small>{{ item.label }}</small>
|
||||||
<template v-if="reviewInlineEditorKey === item.key && item.key === 'occurred_date'">
|
<template v-if="reviewInlineEditorKey === item.key && item.editor === 'date'">
|
||||||
<input
|
<input
|
||||||
v-model="reviewInlineForm.occurred_date"
|
v-model="reviewInlineForm[item.modelKey]"
|
||||||
class="review-inline-input"
|
|
||||||
type="date"
|
|
||||||
@click.stop
|
|
||||||
@change="commitInlineReviewEditor"
|
|
||||||
@keydown.enter.prevent="commitInlineReviewEditor"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template v-else-if="reviewInlineEditorKey === item.key && item.key === 'amount'">
|
|
||||||
<input
|
|
||||||
v-model="reviewInlineForm.amount"
|
|
||||||
class="review-inline-input"
|
class="review-inline-input"
|
||||||
|
:class="{ invalid: Boolean(reviewInlineErrors[item.key]) }"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="例如 200.00元"
|
:placeholder="`仅支持 ${DATE_INPUT_FORMAT}`"
|
||||||
@click.stop
|
@click.stop
|
||||||
|
@input="clearInlineReviewFieldError(item.key)"
|
||||||
@blur="commitInlineReviewEditor"
|
@blur="commitInlineReviewEditor"
|
||||||
@keydown.enter.prevent="commitInlineReviewEditor"
|
@keydown.enter.prevent="commitInlineReviewEditor"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="reviewInlineEditorKey === item.key && item.key === 'customer_name'">
|
<template v-else-if="reviewInlineEditorKey === item.key && item.editor === 'amount'">
|
||||||
<input
|
<input
|
||||||
v-model="reviewInlineForm.customer_name"
|
v-model="reviewInlineForm[item.modelKey]"
|
||||||
class="review-inline-input"
|
class="review-inline-input"
|
||||||
|
:class="{ invalid: Boolean(reviewInlineErrors[item.key]) }"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="请输入客户名称"
|
:placeholder="item.placeholder"
|
||||||
@click.stop
|
@click.stop
|
||||||
|
@input="clearInlineReviewFieldError(item.key)"
|
||||||
@blur="commitInlineReviewEditor"
|
@blur="commitInlineReviewEditor"
|
||||||
@keydown.enter.prevent="commitInlineReviewEditor"
|
@keydown.enter.prevent="commitInlineReviewEditor"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="reviewInlineEditorKey === item.key && item.key === 'scene'">
|
<template v-else-if="reviewInlineEditorKey === item.key && item.editor === 'text'">
|
||||||
|
<input
|
||||||
|
v-model="reviewInlineForm[item.modelKey]"
|
||||||
|
class="review-inline-input"
|
||||||
|
:class="{ invalid: Boolean(reviewInlineErrors[item.key]) }"
|
||||||
|
type="text"
|
||||||
|
:placeholder="item.placeholder"
|
||||||
|
@click.stop
|
||||||
|
@input="clearInlineReviewFieldError(item.key)"
|
||||||
|
@blur="commitInlineReviewEditor"
|
||||||
|
@keydown.enter.prevent="commitInlineReviewEditor"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="reviewInlineEditorKey === item.key && item.editor === 'select'">
|
||||||
<div class="review-inline-select-list" @click.stop>
|
<div class="review-inline-select-list" @click.stop>
|
||||||
<button
|
<button
|
||||||
v-for="scene in REVIEW_SCENE_OPTIONS"
|
v-for="scene in REVIEW_SCENE_OPTIONS"
|
||||||
@@ -335,7 +346,10 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<strong v-else>{{ item.value }}</strong>
|
<strong v-else :title="item.value">{{ item.value }}</strong>
|
||||||
|
<span v-if="reviewInlineErrors[item.key]" class="review-inline-error">
|
||||||
|
{{ reviewInlineErrors[item.key] }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span v-if="item.key !== 'attachments'" class="review-side-edit-hint">修改</span>
|
<span v-if="item.key !== 'attachments'" class="review-side-edit-hint">修改</span>
|
||||||
<span v-else class="review-side-edit-hint upload">{{ reviewInlinePendingFiles.length ? '已选择' : '上传' }}</span>
|
<span v-else class="review-side-edit-hint upload">{{ reviewInlinePendingFiles.length ? '已选择' : '上传' }}</span>
|
||||||
@@ -366,14 +380,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="reviewOtherCategoryOpen" class="review-other-category-popover">
|
<div v-if="reviewOtherCategoryOpen" class="review-other-category-popover">
|
||||||
<button
|
<button
|
||||||
v-for="item in REVIEW_OTHER_CATEGORY_OPTIONS"
|
v-for="item in reviewOtherCategoryOptions"
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
type="button"
|
type="button"
|
||||||
class="review-other-category-option"
|
class="review-other-category-option"
|
||||||
:class="{ active: reviewSelectedOtherCategory === item.label }"
|
:class="{ active: reviewSelectedOtherCategory === item.label }"
|
||||||
@click="selectReviewOtherCategory(item)"
|
@click="selectReviewOtherCategory(item)"
|
||||||
>
|
>
|
||||||
{{ item.label }}
|
{{ item.label }} · {{ item.confidenceLabel }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -381,13 +395,28 @@
|
|||||||
<section class="review-side-card review-side-risk-card">
|
<section class="review-side-card review-side-risk-card">
|
||||||
<div class="review-side-head">
|
<div class="review-side-head">
|
||||||
<strong>合规提醒 / 风险评分</strong>
|
<strong>合规提醒 / 风险评分</strong>
|
||||||
<span class="review-side-risk-score">{{ reviewRiskScore }}/100</span>
|
<span class="review-side-risk-score" :class="{ empty: reviewRiskScore === null }">
|
||||||
|
{{ reviewRiskScore === null ? '无' : `${reviewRiskScore}/100` }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="review-side-risk-summary">{{ reviewRiskSummary }}</p>
|
<p class="review-side-risk-summary">{{ reviewRiskSummary }}</p>
|
||||||
<ul class="review-side-risk-list">
|
<ul v-if="reviewRiskItems.length" class="review-side-risk-list">
|
||||||
<li v-for="item in reviewRiskItems" :key="item">{{ item }}</li>
|
<li v-for="item in reviewRiskItems" :key="item">{{ item }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<button type="button" class="review-side-link" :disabled="submitting || reviewActionBusy" @click="explainCurrentReviewRisk">
|
<div v-else-if="reviewRiskEmpty" class="review-side-empty">
|
||||||
|
<span class="review-side-empty-icon">
|
||||||
|
<i class="mdi mdi-shield-check-outline"></i>
|
||||||
|
</span>
|
||||||
|
<strong>暂无风险评分</strong>
|
||||||
|
<p>当前版本还没有返回结构化风险评分结果,这里先不展示虚拟分数。</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
v-if="reviewRiskActionAvailable"
|
||||||
|
type="button"
|
||||||
|
class="review-side-link"
|
||||||
|
:disabled="submitting || reviewActionBusy"
|
||||||
|
@click="explainCurrentReviewRisk"
|
||||||
|
>
|
||||||
查看全部风险项
|
查看全部风险项
|
||||||
<i class="mdi mdi-chevron-right"></i>
|
<i class="mdi mdi-chevron-right"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -451,21 +480,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<Transition name="assistant-modal">
|
<ConfirmDialog
|
||||||
<div v-if="reviewCancelDialogOpen" class="assistant-overlay review-overlay">
|
:open="reviewCancelDialogOpen"
|
||||||
<section class="review-confirm-modal">
|
badge="取消核对"
|
||||||
<header>
|
badge-tone="warning"
|
||||||
<span class="assistant-badge warning">取消核对</span>
|
title="确认放弃本次识别结果?"
|
||||||
<h3>确认放弃本次识别结果?</h3>
|
description="关闭后将退出当前核对窗口,本次尚未确认的修改不会继续保留。"
|
||||||
<p>关闭后将退出当前核对窗口,本次尚未确认的修改不会继续保留。</p>
|
cancel-text="返回继续核对"
|
||||||
</header>
|
confirm-text="确认取消"
|
||||||
<div class="review-confirm-actions">
|
busy-text="处理中..."
|
||||||
<button type="button" class="secondary-dialog-btn" :disabled="reviewActionBusy" @click="closeCancelReviewDialog">返回继续核对</button>
|
confirm-tone="danger"
|
||||||
<button type="button" class="danger-dialog-btn" :disabled="reviewActionBusy" @click="confirmCancelReview">确认取消</button>
|
confirm-icon="mdi mdi-close-circle-outline"
|
||||||
</div>
|
:busy="reviewActionBusy"
|
||||||
</section>
|
@close="closeCancelReviewDialog"
|
||||||
</div>
|
@confirm="confirmCancelReview"
|
||||||
</Transition>
|
/>
|
||||||
|
|
||||||
<Transition name="assistant-modal">
|
<Transition name="assistant-modal">
|
||||||
<div v-if="reviewEditDialogOpen" class="assistant-overlay review-overlay">
|
<div v-if="reviewEditDialogOpen" class="assistant-overlay review-overlay">
|
||||||
|
|||||||
@@ -1,165 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="approval-page">
|
<section class="approval-page">
|
||||||
<Teleport to="body">
|
|
||||||
<Transition name="detail-modal">
|
|
||||||
<div v-if="aiEntryOpen" class="detail-overlay" @click.self="closeAiEntry">
|
|
||||||
<div class="detail-modal ai-entry-modal">
|
|
||||||
<header class="modal-header">
|
|
||||||
<div class="header-left">
|
|
||||||
<div class="req-badge">AI</div>
|
|
||||||
<div class="header-title-group">
|
|
||||||
<h2>智能录入费用明细</h2>
|
|
||||||
<p>描述票据、行程或费用场景,AI 会整理成可追加的费用条目。</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="header-right">
|
|
||||||
<button class="close-btn" type="button" aria-label="关闭" @click="closeAiEntry">
|
|
||||||
<i class="mdi mdi-close"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="modal-body ai-entry-body">
|
|
||||||
<div class="ai-entry-grid">
|
|
||||||
<section class="ai-chat-card">
|
|
||||||
<input
|
|
||||||
ref="aiFileInput"
|
|
||||||
class="ai-file-input"
|
|
||||||
type="file"
|
|
||||||
multiple
|
|
||||||
accept=".pdf,.jpg,.jpeg,.png,.webp,.doc,.docx,.xls,.xlsx"
|
|
||||||
@change="handleAiFilesChange"
|
|
||||||
/>
|
|
||||||
<div class="ai-chat-scroll">
|
|
||||||
<article
|
|
||||||
v-for="message in aiMessages"
|
|
||||||
:key="message.id"
|
|
||||||
class="ai-chat-bubble"
|
|
||||||
:class="message.role"
|
|
||||||
>
|
|
||||||
<span class="ai-chat-avatar">
|
|
||||||
<i :class="message.role === 'assistant' ? 'mdi mdi-robot-outline' : 'mdi mdi-account-circle-outline'"></i>
|
|
||||||
</span>
|
|
||||||
<div class="ai-chat-content">
|
|
||||||
<header>
|
|
||||||
<strong>{{ message.role === 'assistant' ? 'AI 录入助手' : '我' }}</strong>
|
|
||||||
</header>
|
|
||||||
<p>{{ message.text }}</p>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ai-composer">
|
|
||||||
<div class="ai-composer-surface">
|
|
||||||
<textarea
|
|
||||||
v-model="aiDraft"
|
|
||||||
rows="3"
|
|
||||||
placeholder="例如:7月12日从上海虹桥到杭州东高铁二等座236元,已上传车票和行程单。"
|
|
||||||
/>
|
|
||||||
<div class="ai-composer-actions">
|
|
||||||
<button class="ai-upload-btn" type="button" aria-label="上传单据" @click="triggerAiUpload">
|
|
||||||
<i class="mdi mdi-paperclip"></i>
|
|
||||||
</button>
|
|
||||||
<button class="ai-send-btn" type="button" aria-label="发送给 AI" :disabled="!canSendAiEntry" @click="sendAiEntry">
|
|
||||||
<i class="mdi mdi-send"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="uploadedAiFiles.length" class="ai-upload-list">
|
|
||||||
<span v-for="file in uploadedAiFiles" :key="file.name" class="ai-upload-chip">
|
|
||||||
<i class="mdi mdi-paperclip"></i>
|
|
||||||
{{ file.name }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<aside class="ai-preview-card">
|
|
||||||
<div class="ai-preview-head">
|
|
||||||
<div>
|
|
||||||
<h3>识别结果</h3>
|
|
||||||
<p>确认后会直接追加到费用明细表。</p>
|
|
||||||
</div>
|
|
||||||
<span v-if="pendingAiExpense" class="attachment-pill neutral">待确认</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="pendingAiExpense" class="ai-preview-fields">
|
|
||||||
<div class="preview-field">
|
|
||||||
<span>日期</span>
|
|
||||||
<strong>{{ pendingAiExpense.time }} {{ pendingAiExpense.dayLabel }}</strong>
|
|
||||||
</div>
|
|
||||||
<div class="preview-field">
|
|
||||||
<span>费用项目</span>
|
|
||||||
<strong>{{ pendingAiExpense.name }}</strong>
|
|
||||||
</div>
|
|
||||||
<div class="preview-field">
|
|
||||||
<span>分类</span>
|
|
||||||
<strong>{{ pendingAiExpense.category }}</strong>
|
|
||||||
</div>
|
|
||||||
<div class="preview-field">
|
|
||||||
<span>金额</span>
|
|
||||||
<strong>{{ pendingAiExpense.amount }}</strong>
|
|
||||||
</div>
|
|
||||||
<div class="preview-field full">
|
|
||||||
<span>说明</span>
|
|
||||||
<strong>{{ pendingAiExpense.desc }}</strong>
|
|
||||||
<p>{{ pendingAiExpense.detail }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="preview-field full">
|
|
||||||
<span>附件</span>
|
|
||||||
<strong>{{ pendingAiExpense.attachmentStatus }}</strong>
|
|
||||||
<p>{{ pendingAiExpense.attachmentHint }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="ai-preview-actions">
|
|
||||||
<button class="ai-preview-secondary" type="button" @click="regenerateAiEntry">
|
|
||||||
<i class="mdi mdi-refresh"></i>
|
|
||||||
重新生成
|
|
||||||
</button>
|
|
||||||
<button class="ai-preview-primary" type="button" @click="applyAiExpense">
|
|
||||||
<i class="mdi mdi-plus-circle-outline"></i>
|
|
||||||
加入费用明细
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else class="ai-preview-empty">
|
|
||||||
<i class="mdi mdi-robot-outline"></i>
|
|
||||||
<p>发送一段费用描述后,这里会生成结构化结果。</p>
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</Teleport>
|
|
||||||
|
|
||||||
<div class="approval-detail">
|
<div class="approval-detail">
|
||||||
<div class="detail-scroll">
|
<div class="detail-scroll">
|
||||||
<article class="detail-hero panel">
|
<article class="detail-hero panel">
|
||||||
<div class="applicant-card">
|
<div class="applicant-card">
|
||||||
<div class="portrait">{{ profile.avatar }}</div>
|
<div class="portrait">{{ profile.avatar }}</div>
|
||||||
<div>
|
<div>
|
||||||
<h2>{{ profile.name }} <span>{{ profile.department }}</span></h2>
|
<h2>{{ profile.name }} <span>{{ request.typeLabel }}</span></h2>
|
||||||
<p>申请时间 <strong>{{ request.applyTime }}</strong></p>
|
<p>{{ profile.department }} <strong>申请时间 {{ request.applyTime }}</strong></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="hero-stat">
|
<div v-for="stat in heroStats" :key="stat.label" class="hero-stat">
|
||||||
<span>金额</span>
|
<span>{{ stat.label }}</span>
|
||||||
<strong>{{ expenseTotal }}</strong>
|
<strong v-if="stat.kind === 'text'">{{ stat.value }}</strong>
|
||||||
</div>
|
<b v-else :class="[stat.className, stat.tone]">{{ stat.value }}</b>
|
||||||
<div class="hero-stat">
|
|
||||||
<span>审批状态</span>
|
|
||||||
<b class="state-pill">{{ request.node }}</b>
|
|
||||||
</div>
|
|
||||||
<div class="hero-stat">
|
|
||||||
<span>商旅状态</span>
|
|
||||||
<b :class="['risk-pill', request.travelTone]">{{ request.travel }}</b>
|
|
||||||
</div>
|
|
||||||
<div class="hero-stat">
|
|
||||||
<span>申请状态</span>
|
|
||||||
<strong class="countdown"><i class="mdi mdi-clock-outline"></i> {{ request.approval }}</strong>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="hero-summary-panel">
|
<div class="hero-summary-panel">
|
||||||
@@ -174,9 +29,9 @@
|
|||||||
|
|
||||||
<div class="progress-block">
|
<div class="progress-block">
|
||||||
<div class="progress-head">
|
<div class="progress-head">
|
||||||
<h3>当前进度</h3>
|
<h3>{{ isTravelRequest ? '差旅进度' : '报销进度' }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress-line">
|
<div class="progress-line" :style="{ '--progress-columns': progressSteps.length }">
|
||||||
<div
|
<div
|
||||||
v-for="step in progressSteps"
|
v-for="step in progressSteps"
|
||||||
:key="step.label"
|
:key="step.label"
|
||||||
@@ -208,13 +63,16 @@
|
|||||||
<div class="detail-card-head">
|
<div class="detail-card-head">
|
||||||
<div>
|
<div>
|
||||||
<h3>费用明细</h3>
|
<h3>费用明细</h3>
|
||||||
<p>按发生时间逐笔展示,附件与系统校验直接在表内完成核对。</p>
|
<p>
|
||||||
|
{{ isTravelRequest ? '按出行时间逐笔核对票据与差旅规则。' : '按业务发生时间逐笔核对票据、用途说明与系统校验。' }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button class="smart-entry-btn" type="button" @click="openAiEntry">
|
<button class="smart-entry-btn" type="button" @click="openAiEntry">
|
||||||
<i class="mdi mdi-robot-outline"></i>
|
<i class="mdi mdi-robot-outline"></i>
|
||||||
<span>智能录入</span>
|
<span>智能录入</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="detail-expense-table">
|
<div class="detail-expense-table">
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
@@ -225,28 +83,72 @@
|
|||||||
<th>金额</th>
|
<th>金额</th>
|
||||||
<th>附件材料</th>
|
<th>附件材料</th>
|
||||||
<th>系统校验</th>
|
<th>系统校验</th>
|
||||||
|
<th v-if="isDraftRequest">操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<template v-for="item in expenseItems" :key="item.id">
|
<template v-for="item in expenseItems" :key="item.id">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="expense-time">
|
<td class="expense-time">
|
||||||
|
<template v-if="editingExpenseId === item.id">
|
||||||
|
<div class="cell-editor">
|
||||||
|
<input v-model="expenseEditor.itemDate" class="editor-input" type="date" />
|
||||||
|
<span>{{ item.dayLabel }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<strong>{{ item.time }}</strong>
|
<strong>{{ item.time }}</strong>
|
||||||
<span>{{ item.dayLabel }}</span>
|
<span>{{ item.dayLabel }}</span>
|
||||||
|
</template>
|
||||||
</td>
|
</td>
|
||||||
<td class="expense-type">
|
<td class="expense-type">
|
||||||
|
<template v-if="editingExpenseId === item.id">
|
||||||
|
<div class="cell-editor">
|
||||||
|
<select v-model="expenseEditor.itemType" class="editor-select">
|
||||||
|
<option v-for="option in expenseTypeOptions" :key="option.value" :value="option.value">
|
||||||
|
{{ option.label }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<span>编辑费用项目</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<strong>{{ item.name }}</strong>
|
<strong>{{ item.name }}</strong>
|
||||||
<span>{{ item.category }}</span>
|
<span>{{ item.category }}</span>
|
||||||
|
</template>
|
||||||
</td>
|
</td>
|
||||||
<td class="expense-desc">
|
<td class="expense-desc">
|
||||||
|
<template v-if="editingExpenseId === item.id">
|
||||||
|
<div class="cell-editor editor-stack">
|
||||||
|
<input v-model="expenseEditor.itemReason" class="editor-input" type="text" placeholder="输入费用说明" />
|
||||||
|
<input v-model="expenseEditor.itemLocation" class="editor-input" type="text" placeholder="输入业务地点" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<strong>{{ item.desc }}</strong>
|
<strong>{{ item.desc }}</strong>
|
||||||
<span>{{ item.detail }}</span>
|
<span>{{ item.detail }}</span>
|
||||||
|
</template>
|
||||||
</td>
|
</td>
|
||||||
<td class="expense-amount">
|
<td class="expense-amount">
|
||||||
|
<template v-if="editingExpenseId === item.id">
|
||||||
|
<div class="cell-editor">
|
||||||
|
<input v-model="expenseEditor.itemAmount" class="editor-input" type="number" min="0" step="0.01" placeholder="输入金额" />
|
||||||
|
<span>保存后自动格式化为人民币</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<strong>{{ item.amount }}</strong>
|
<strong>{{ item.amount }}</strong>
|
||||||
<span v-if="item.tone !== 'ok'" :class="['over-tag', item.tone]">{{ item.status }}</span>
|
<span v-if="item.tone !== 'ok'" :class="['over-tag', item.tone]">{{ item.status }}</span>
|
||||||
|
</template>
|
||||||
</td>
|
</td>
|
||||||
<td class="expense-attachment">
|
<td class="expense-attachment">
|
||||||
|
<template v-if="editingExpenseId === item.id">
|
||||||
|
<div class="cell-editor editor-stack">
|
||||||
|
<input v-model="expenseEditor.invoiceId" class="editor-input" type="text" placeholder="输入票据标识或附件名称" />
|
||||||
|
<span>示例:invoice-2026-0513.jpg</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<div class="expense-attachment-main">
|
<div class="expense-attachment-main">
|
||||||
<span :class="['attachment-pill', item.attachmentTone]">{{ item.attachmentStatus }}</span>
|
<span :class="['attachment-pill', item.attachmentTone]">{{ item.attachmentStatus }}</span>
|
||||||
<button
|
<button
|
||||||
@@ -259,16 +161,48 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<span class="attachment-hint">{{ item.attachmentHint }}</span>
|
<span class="attachment-hint">{{ item.attachmentHint }}</span>
|
||||||
|
</template>
|
||||||
</td>
|
</td>
|
||||||
<td class="expense-risk">
|
<td class="expense-risk">
|
||||||
<template v-if="showExpenseRisk(item)">
|
<template v-if="showExpenseRisk(item)">
|
||||||
<span :class="['risk-inline-tag', item.riskTone]">{{ item.riskLabel }}</span>
|
<span :class="['risk-inline-tag', resolveExpenseIssues(item).length ? 'medium' : item.riskTone]">
|
||||||
<p>{{ item.riskText }}</p>
|
{{ resolveExpenseIssues(item).length ? '待补充' : item.riskLabel }}
|
||||||
|
</span>
|
||||||
|
<p>{{ resolveExpenseIssues(item).length ? resolveExpenseIssues(item).join(',') : item.riskText }}</p>
|
||||||
</template>
|
</template>
|
||||||
</td>
|
</td>
|
||||||
|
<td v-if="isDraftRequest" class="expense-action-cell">
|
||||||
|
<div v-if="editingExpenseId === item.id" class="row-action-group">
|
||||||
|
<button
|
||||||
|
class="inline-action primary"
|
||||||
|
type="button"
|
||||||
|
:disabled="savingExpenseId === item.id || submitBusy || deleteBusy"
|
||||||
|
@click="saveExpenseEdit(item)"
|
||||||
|
>
|
||||||
|
{{ savingExpenseId === item.id ? '保存中' : '保存' }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="inline-action"
|
||||||
|
type="button"
|
||||||
|
:disabled="savingExpenseId === item.id || submitBusy || deleteBusy"
|
||||||
|
@click="cancelExpenseEdit"
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
v-else
|
||||||
|
class="inline-action"
|
||||||
|
type="button"
|
||||||
|
:disabled="actionBusy"
|
||||||
|
@click="startExpenseEdit(item)"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="expandedExpenseId === item.id" class="expense-expand-row">
|
<tr v-if="expandedExpenseId === item.id" class="expense-expand-row">
|
||||||
<td colspan="6">
|
<td :colspan="isDraftRequest ? 7 : 6">
|
||||||
<div class="expense-files">
|
<div class="expense-files">
|
||||||
<span v-for="file in item.attachments" :key="file" class="expense-file-chip">
|
<span v-for="file in item.attachments" :key="file" class="expense-file-chip">
|
||||||
<i class="mdi mdi-paperclip"></i>
|
<i class="mdi mdi-paperclip"></i>
|
||||||
@@ -279,19 +213,31 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
<tr class="total-row">
|
<tr class="total-row">
|
||||||
<td colspan="3">合计</td>
|
<td :colspan="isDraftRequest ? 4 : 3">合计</td>
|
||||||
<td>{{ expenseTotal }}</td>
|
<td>{{ expenseTotal }}</td>
|
||||||
<td>{{ uploadedExpenseCount }} 项已上传票据</td>
|
<td>{{ uploadedExpenseCount }} 项已关联票据</td>
|
||||||
<td>1 项待补材料,1 项需补充超标说明</td>
|
<td>{{ expenseSummaryText }}</td>
|
||||||
|
<td v-if="isDraftRequest">-</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
<article v-if="isDraftRequest" class="detail-card panel validation-card">
|
||||||
|
<div class="validation-head">
|
||||||
|
<h3>提交校验</h3>
|
||||||
|
<span :class="['validation-pill', validationTone]">{{ canSubmit ? '可提交' : '待完善' }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="validation-summary">{{ validationSummary }}</p>
|
||||||
|
<ul v-if="draftBlockingIssues.length" class="validation-list">
|
||||||
|
<li v-for="issue in draftBlockingIssues" :key="issue">{{ issue }}</li>
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
|
||||||
<article class="detail-card panel">
|
<article class="detail-card panel">
|
||||||
<h3>申请说明</h3>
|
<h3>申请说明</h3>
|
||||||
<textarea rows="3" :value="detailNote" placeholder="输入申请说明..." />
|
<div class="detail-note">{{ detailNote }}</div>
|
||||||
</article>
|
</article>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
@@ -300,15 +246,37 @@
|
|||||||
<footer class="detail-actions">
|
<footer class="detail-actions">
|
||||||
<button class="back-action" type="button" @click="emit('backToRequests')">
|
<button class="back-action" type="button" @click="emit('backToRequests')">
|
||||||
<i class="mdi mdi-arrow-left"></i>
|
<i class="mdi mdi-arrow-left"></i>
|
||||||
<span>退回列表</span>
|
<span>返回报销列表</span>
|
||||||
|
</button>
|
||||||
|
<div v-if="isDraftRequest" class="approval-action-group" aria-label="申请操作">
|
||||||
|
<button class="reject-action" type="button" :disabled="actionBusy" @click="handleDeleteDraft">
|
||||||
|
<i class="mdi mdi-trash-can-outline"></i>
|
||||||
|
{{ deleteBusy ? '删除中' : '删除草稿' }}
|
||||||
|
</button>
|
||||||
|
<button class="approve-action" type="button" :disabled="!canSubmit" @click="handleSubmit">
|
||||||
|
<i class="mdi mdi-send-circle-outline"></i>
|
||||||
|
{{ submitBusy ? '提交中' : '提交审批' }}
|
||||||
</button>
|
</button>
|
||||||
<div class="approval-action-group" aria-label="申请操作">
|
|
||||||
<button class="approve-action" type="button"><i class="mdi mdi-send-circle-outline"></i> 提交审批</button>
|
|
||||||
<button class="reject-action" type="button"><i class="mdi mdi-close-circle-outline"></i> 撤回申请</button>
|
|
||||||
<button class="supplement-action" type="button"><i class="mdi mdi-pencil-outline"></i> 编辑申请</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p v-else class="detail-action-hint">当前单据已进入流程,详情页仅展示状态与费用明细。</p>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ConfirmDialog
|
||||||
|
:open="deleteDialogOpen"
|
||||||
|
badge="删除草稿"
|
||||||
|
badge-tone="danger"
|
||||||
|
:title="`确认删除草稿 ${request.id} 吗?`"
|
||||||
|
description="删除后该草稿及其当前费用明细将不可恢复,请确认本次操作。"
|
||||||
|
cancel-text="取消"
|
||||||
|
confirm-text="确认删除"
|
||||||
|
busy-text="删除中..."
|
||||||
|
confirm-tone="danger"
|
||||||
|
confirm-icon="mdi mdi-trash-can-outline"
|
||||||
|
:busy="deleteBusy"
|
||||||
|
@close="closeDeleteDialog"
|
||||||
|
@confirm="confirmDeleteDraft"
|
||||||
|
/>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user