From e5ba8f097a391a324f52d53e961efb1b3549480f Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Sun, 17 May 2026 09:05:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app.pid | 2 +- backend/app/crud/collection_protocol.py | 16 +- web-admin/src/api/collection-protocol.ts | 5 +- web-admin/src/utils/fullscreen-loading.ts | 3 +- .../cell-storage/protocol-audit/index.vue | 51 +++++- .../protocol/ProtocolAuditDialog.vue | 165 ++++++++++++++++++ .../protocol/ProtocolViewDialog.vue | 125 ++++++++++++- 7 files changed, 353 insertions(+), 14 deletions(-) create mode 100644 web-admin/src/views/cell-storage/protocol/ProtocolAuditDialog.vue diff --git a/backend/app.pid b/backend/app.pid index eccd63642..eefb32fbf 100644 --- a/backend/app.pid +++ b/backend/app.pid @@ -1 +1 @@ -92212 +25041 diff --git a/backend/app/crud/collection_protocol.py b/backend/app/crud/collection_protocol.py index aa5e7a4c2..046dac971 100644 --- a/backend/app/crud/collection_protocol.py +++ b/backend/app/crud/collection_protocol.py @@ -253,12 +253,14 @@ def _audit_to_dict( a: BizProtocolAudit, *, protocol_no: str = "", + auditor_user_name: str = "", ) -> Dict[str, Any]: return { "id": a.id, "collectionProtocolId": a.collection_protocol_id, "protocolNo": protocol_no, "auditorUserId": a.auditor_user_id or "", + "auditorUserName": auditor_user_name or "", "auditRemark": a.audit_remark or "", "auditStatus": a.audit_status or "", "tenantNo": a.tenant_no, @@ -636,7 +638,19 @@ def list_audits( .limit(page_size) .all() ) - out = [_audit_to_dict(a, protocol_no=pn or "") for a, pn in rows] + auditor_ids = list({a.auditor_user_id for a, _ in rows if a.auditor_user_id}) + auditor_name_map: Dict[str, str] = {} + if auditor_ids: + for u in db.query(SysUser.id, SysUser.nick_name).filter(SysUser.id.in_(auditor_ids)).all(): + auditor_name_map[u.id] = u.nick_name or "" + out = [ + _audit_to_dict( + a, + protocol_no=pn or "", + auditor_user_name=auditor_name_map.get(a.auditor_user_id or "", ""), + ) + for a, pn in rows + ] return out, int(total) diff --git a/web-admin/src/api/collection-protocol.ts b/web-admin/src/api/collection-protocol.ts index 7f8d2695f..0742a180d 100644 --- a/web-admin/src/api/collection-protocol.ts +++ b/web-admin/src/api/collection-protocol.ts @@ -51,12 +51,13 @@ export function fetchCollectionProtocolAuditList(params?: { export function fetchSubmitProtocolAudit( protocolId: string, - data: { auditStatus: string; auditRemark?: string } + data: { auditStatus: string; auditRemark?: string }, + options?: { showSuccessMessage?: boolean } ) { return request.post({ url: `/web/admin/collection-protocols/${protocolId}/audit`, data, - showSuccessMessage: true + showSuccessMessage: options?.showSuccessMessage ?? true }) } diff --git a/web-admin/src/utils/fullscreen-loading.ts b/web-admin/src/utils/fullscreen-loading.ts index a9442a7cd..91c5bceaa 100644 --- a/web-admin/src/utils/fullscreen-loading.ts +++ b/web-admin/src/utils/fullscreen-loading.ts @@ -21,5 +21,6 @@ export async function withFullscreenLoading( export const fullscreenLoadingText = { save: '正在保存,请稍候…', delete: '正在删除,请稍候…', - batchDelete: '正在批量删除,请稍候…' + batchDelete: '正在批量删除,请稍候…', + batchAudit: '正在提交审核,请稍候…' } as const diff --git a/web-admin/src/views/cell-storage/protocol-audit/index.vue b/web-admin/src/views/cell-storage/protocol-audit/index.vue index bf4b4a08b..b93054a1a 100644 --- a/web-admin/src/views/cell-storage/protocol-audit/index.vue +++ b/web-admin/src/views/cell-storage/protocol-audit/index.vue @@ -56,6 +56,10 @@ 新增 + + + 审核 + 批量删除 @@ -208,7 +212,7 @@ /> 查看 + 审核 编辑 @@ -247,8 +254,20 @@ @saved="onFormSaved" /> + + + - + @@ -261,10 +280,14 @@ Edit, Delete, Search, - RefreshLeft + RefreshLeft, + CircleCheck } from '@element-plus/icons-vue' import ProtocolFormDialog from '../protocol/ProtocolFormDialog.vue' import ProtocolViewDialog from '../protocol/ProtocolViewDialog.vue' + import ProtocolAuditDialog, { + type ProtocolAuditTarget + } from '../protocol/ProtocolAuditDialog.vue' import { withFullscreenLoading, fullscreenLoadingText } from '@/utils/fullscreen-loading' import { fetchCollectionProtocolList, @@ -372,6 +395,28 @@ loadData() } + /* ====== 审核 ====== */ + const auditDialogVisible = ref(false) + const auditTargets = ref([]) + + function openAuditDialog(row?: CollectionProtocolRow) { + const rows = row ? [row] : selectedRows.value + if (!rows.length) { + ElMessage.warning('请先选择要审核的协议单') + return + } + auditTargets.value = rows.map((r) => ({ + id: String(r.id), + protocolNo: r.protocolNo != null ? String(r.protocolNo) : undefined + })) + auditDialogVisible.value = true + } + + function onAuditSubmitted() { + selectedRows.value = [] + loadData() + } + /* ====== 删除 ====== */ function handleDelete(row: CollectionProtocolRow) { ElMessageBox.confirm(`确认删除协议号「${row.protocolNo}」?`, '删除确认', { diff --git a/web-admin/src/views/cell-storage/protocol/ProtocolAuditDialog.vue b/web-admin/src/views/cell-storage/protocol/ProtocolAuditDialog.vue new file mode 100644 index 000000000..9f9a20cbb --- /dev/null +++ b/web-admin/src/views/cell-storage/protocol/ProtocolAuditDialog.vue @@ -0,0 +1,165 @@ + + + + + + diff --git a/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue b/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue index a7c6dfd8b..0d088fc60 100644 --- a/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue +++ b/web-admin/src/views/cell-storage/protocol/ProtocolViewDialog.vue @@ -318,6 +318,65 @@ + +
+

审核记录

+ + + + + + + + + + + +
@@ -352,6 +411,7 @@ import { Close } from '@element-plus/icons-vue' import ProtocolAttachmentReadonly from './ProtocolAttachmentReadonly.vue' import { + fetchCollectionProtocolAuditList, fetchCollectionProtocolDetail, type CollectionProtocolRow } from '@/api/collection-protocol' @@ -365,17 +425,22 @@ fetchUserList } from '@/api/admin' - const props = defineProps<{ - modelValue: boolean - protocolId: string | null - listRow?: CollectionProtocolRow | null - }>() + const props = withDefaults( + defineProps<{ + modelValue: boolean + protocolId: string | null + listRow?: CollectionProtocolRow | null + /** 展示审核记录 Tab(采集协议单审核页) */ + showAuditHistory?: boolean + }>(), + { showAuditHistory: false } + ) const emit = defineEmits<{ (e: 'update:modelValue', val: boolean): void }>() - const formTabs = [ + const baseFormTabs = [ { key: 'basic', label: '基本信息' }, { key: 'party', label: '客户信息' }, { key: 'collection', label: '采集信息' }, @@ -383,8 +448,18 @@ { key: 'payment', label: '缴费信息' } ] as const + const formTabs = computed(() => { + const tabs: Array<{ key: string; label: string }> = [...baseFormTabs] + if (props.showAuditHistory) { + tabs.push({ key: 'audit', label: '审核记录' }) + } + return tabs + }) + const loading = ref(false) + const auditLoading = ref(false) const bundle = ref | null>(null) + const auditRecords = ref>>([]) const activeTab = ref('basic') const sampleTypeOptions = ref>([]) @@ -476,6 +551,18 @@ return m[s] || s || '—' } + function auditStatusLabel(s: string) { + if (s === 'shtg') return '审核通过' + if (s === 'shth') return '审核退回' + return s || '—' + } + + function auditorDisplay(row: Record) { + const name = String(row.auditorUserName || '').trim() + if (name) return name + return dash(row.auditorUserId) + } + function payModeLabel(s: string) { if (s === 'dj') return '趸交' if (s === 'fq') return '分期' @@ -643,14 +730,39 @@ await Promise.all(tasks) } + async function loadAuditRecords() { + if (!props.showAuditHistory || !props.protocolId) { + auditRecords.value = [] + return + } + auditLoading.value = true + try { + const pno = + String(protocol.value?.protocolNo || props.listRow?.protocolNo || '').trim() || undefined + const res = await fetchCollectionProtocolAuditList({ + page: 1, + pageSize: 100, + keyword: pno + }) + const pid = props.protocolId + auditRecords.value = (res.list || []).filter( + (row) => String(row.collectionProtocolId || '') === pid + ) + } finally { + auditLoading.value = false + } + } + async function loadDetail() { if (!props.protocolId) return loading.value = true bundle.value = null + auditRecords.value = [] try { const data = await fetchCollectionProtocolDetail(props.protocolId) bundle.value = data as Record await loadLookups(data as Record) + await loadAuditRecords() } finally { loading.value = false } @@ -667,6 +779,7 @@ function handleClosed() { bundle.value = null + auditRecords.value = [] activeTab.value = 'basic' }