From 80b612a9134405b1ac3405b782be24be2737d8f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E7=90=A6?= Date: Sat, 11 Jul 2026 15:53:44 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E8=AE=A1=E7=AE=97=E9=83=A8=E9=97=A8?= =?UTF-8?q?=E5=BC=80=E7=A5=A8=E4=BB=B7=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../finance/department-finance/api/index.ts | 64 +++++++ .../components/ConfigModal.vue | 67 ++++++++ .../components/GenerateReportModal.vue | 79 +++++++++ .../components/ReportDetailDrawer.vue | 160 ++++++++++++++++++ .../finance/department-finance/index.vue | 138 +++++++++++++++ .../reconciliation/api/invoice-price.ts | 39 +++++ .../components/InvoicePriceDrawer.vue | 112 ++++++++++++ .../views/finance/reconciliation/index.vue | 29 +++- 8 files changed, 687 insertions(+), 1 deletion(-) create mode 100644 apps/web-antd/src/views/finance/department-finance/api/index.ts create mode 100644 apps/web-antd/src/views/finance/department-finance/components/ConfigModal.vue create mode 100644 apps/web-antd/src/views/finance/department-finance/components/GenerateReportModal.vue create mode 100644 apps/web-antd/src/views/finance/department-finance/components/ReportDetailDrawer.vue create mode 100644 apps/web-antd/src/views/finance/department-finance/index.vue create mode 100644 apps/web-antd/src/views/finance/reconciliation/api/invoice-price.ts create mode 100644 apps/web-antd/src/views/finance/reconciliation/components/InvoicePriceDrawer.vue diff --git a/apps/web-antd/src/views/finance/department-finance/api/index.ts b/apps/web-antd/src/views/finance/department-finance/api/index.ts new file mode 100644 index 00000000..96364ab2 --- /dev/null +++ b/apps/web-antd/src/views/finance/department-finance/api/index.ts @@ -0,0 +1,64 @@ +import { requestClient } from '#/api/request'; + +const prefix = 'department-finance-report/'; + +/** + * 报表批次列表 + */ +export async function getDepartmentFinanceReportListApi(params: Record) { + return requestClient.get(`${prefix}list`, { params }); +} + +/** + * 报表详情 + */ +export async function getDepartmentFinanceReportDetailApi(id: number) { + return requestClient.get(`${prefix}detail`, { params: { id } }); +} + +/** + * 诊所药品供货明细 + */ +export async function getDepartmentFinanceStoreDrugDetailApi(params: { + store_id: number; + report_id?: number; + search_time?: [string, string]; +}) { + return requestClient.get(`${prefix}store-drug-detail`, { params }); +} + +/** + * 手动生成报表(须传统计区间) + */ +export async function generateDepartmentFinanceReportApi(data: { + search_time: [string, string]; +}) { + return requestClient.post(`${prefix}generate`, data); +} + +/** + * 读取生成配置 + */ +export async function getDepartmentFinanceConfigApi() { + return requestClient.get(`${prefix}config`); +} + +/** + * 保存生成配置 + */ +export async function saveDepartmentFinanceConfigApi(data: { + generate_day: number; + generate_hour: number; +}) { + return requestClient.post(`${prefix}save-config`, data); +} + +/** + * 导出 Excel + */ +export async function exportDepartmentFinanceReportApi(params: { + id: number; + include_drugs?: 0 | 1; +}) { + return requestClient.download(`${prefix}export-excel`, { params }); +} diff --git a/apps/web-antd/src/views/finance/department-finance/components/ConfigModal.vue b/apps/web-antd/src/views/finance/department-finance/components/ConfigModal.vue new file mode 100644 index 00000000..3df69621 --- /dev/null +++ b/apps/web-antd/src/views/finance/department-finance/components/ConfigModal.vue @@ -0,0 +1,67 @@ + + + diff --git a/apps/web-antd/src/views/finance/department-finance/components/GenerateReportModal.vue b/apps/web-antd/src/views/finance/department-finance/components/GenerateReportModal.vue new file mode 100644 index 00000000..3b3377e5 --- /dev/null +++ b/apps/web-antd/src/views/finance/department-finance/components/GenerateReportModal.vue @@ -0,0 +1,79 @@ + + + diff --git a/apps/web-antd/src/views/finance/department-finance/components/ReportDetailDrawer.vue b/apps/web-antd/src/views/finance/department-finance/components/ReportDetailDrawer.vue new file mode 100644 index 00000000..707b6f45 --- /dev/null +++ b/apps/web-antd/src/views/finance/department-finance/components/ReportDetailDrawer.vue @@ -0,0 +1,160 @@ + + + diff --git a/apps/web-antd/src/views/finance/department-finance/index.vue b/apps/web-antd/src/views/finance/department-finance/index.vue new file mode 100644 index 00000000..521c6b7e --- /dev/null +++ b/apps/web-antd/src/views/finance/department-finance/index.vue @@ -0,0 +1,138 @@ + + + diff --git a/apps/web-antd/src/views/finance/reconciliation/api/invoice-price.ts b/apps/web-antd/src/views/finance/reconciliation/api/invoice-price.ts new file mode 100644 index 00000000..485eff1f --- /dev/null +++ b/apps/web-antd/src/views/finance/reconciliation/api/invoice-price.ts @@ -0,0 +1,39 @@ +import { requestClient } from '#/api/request'; + +import dayjs from 'dayjs'; + +const prefix = 'reconciliation/'; + +function formatSearchTimeParam(st: unknown): [string, string] | undefined { + if (!Array.isArray(st) || st.length < 2) return undefined; + const fmt = (v: unknown) => { + if (v && typeof v === 'object' && 'format' in v && typeof (v as { format: (s: string) => string }).format === 'function') { + return (v as dayjs.Dayjs).format('YYYY-MM-DD'); + } + const d = dayjs(v as string | number); + return d.isValid() ? d.format('YYYY-MM-DD') : ''; + }; + const a = fmt(st[0]); + const b = fmt(st[1]); + if (!a || !b) return undefined; + return [a, b]; +} + +/** + * 诊所开票价格(部门财务口径) + */ +export async function getInvoicePriceApi(params: { + store_id: number; + search_time: unknown; +}) { + const search_time = formatSearchTimeParam(params.search_time); + if (!search_time) { + return Promise.reject(new Error('时间范围无效')); + } + return requestClient.get(`${prefix}invoice-price`, { + params: { + store_id: params.store_id, + search_time, + }, + }); +} diff --git a/apps/web-antd/src/views/finance/reconciliation/components/InvoicePriceDrawer.vue b/apps/web-antd/src/views/finance/reconciliation/components/InvoicePriceDrawer.vue new file mode 100644 index 00000000..45b0b571 --- /dev/null +++ b/apps/web-antd/src/views/finance/reconciliation/components/InvoicePriceDrawer.vue @@ -0,0 +1,112 @@ + + + diff --git a/apps/web-antd/src/views/finance/reconciliation/index.vue b/apps/web-antd/src/views/finance/reconciliation/index.vue index 26a65887..f52c0a34 100644 --- a/apps/web-antd/src/views/finance/reconciliation/index.vue +++ b/apps/web-antd/src/views/finance/reconciliation/index.vue @@ -33,6 +33,7 @@ import { } from '#/views/finance/reconciliation/api'; import ReconciliationExportModal from './components/ReconciliationExportModal.vue'; +import InvoicePriceDrawer from './components/InvoicePriceDrawer.vue'; import StatisticsReconciliation from './components/statistics.vue'; import { loadExcludeZeroSales, @@ -77,6 +78,13 @@ const [ExportModal, exportModalApi] = useVbenModal({ connectedComponent: ReconciliationExportModal, }); +const invoicePriceDrawerRef = ref>(); + +/** 平台财务等可查看开票价格 */ +const canViewInvoicePrice = computed( + () => Number(userInfo.value?.roles?.user_type) === 2, +); + const activeTab = ref('store'); /** 整页共用时间 */ @@ -268,6 +276,15 @@ function goDrug(storeId: number, name: string) { activeTab.value = 'drug'; } +/** 打开诊所开票价格抽屉 */ +function openInvoicePrice(storeId: number, name: string) { + invoicePriceDrawerRef.value?.open({ + store_id: storeId, + store_name: name, + search_time: searchTimeParam(), + }); +} + function clearDrugStoreFilter() { currentStoreId.value = null; currentStoreName.value = ''; @@ -306,7 +323,7 @@ const storeColumns = [ { title: '总销售总价', key: 'total_sales_price', width: 120 }, { title: '总供货总价', key: 'total_supply_price', width: 120 }, { title: '挂号费用', key: 'registration_price', width: 100 }, - { title: '操作', key: 'action', width: 100, fixed: 'right' as const }, + { title: '操作', key: 'action', width: 160, fixed: 'right' as const }, ]; const drugColumnsAll = [ @@ -355,6 +372,7 @@ watch(byOrder, () => { title="对账单" > + { size: 'small', onClick: () => goDrug(record.id, record.name), }, + ...(canViewInvoicePrice + ? [{ + label: '开票价格', + type: 'link', + icon: 'mdi:receipt-text', + size: 'small', + onClick: () => openInvoicePrice(record.id, record.name), + }] + : []), ]" :drop-down-actions="[]" />