fix: 对账单功能

This commit is contained in:
2025-04-17 16:19:53 +08:00
parent 596bbb2fa9
commit f3f870cd8e
7 changed files with 734 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
import { requestClient } from '#/api/request';
const prefix = 'reconciliation/';
/**
* 诊所对账单
* @param data
*/
export async function getReconciliationStoreList(data: any) {
return requestClient.get<any>(`${prefix}list`, { params: data });
}
/**
* 诊所对账单
* @param data
*/
export async function getReconciliationDrugList(data: any) {
return requestClient.get<any>(`${prefix}drug-list`, { params: data });
}

View File

@@ -0,0 +1,50 @@
<script setup lang="ts">
import { VbenIcon } from '@vben/common-ui';
defineOptions({
name: 'StatisticsReconciliation',
});
const props = defineProps({
statistics: {
required: true,
},
});
const formattedValue = (value: number | string, unit?: string) => {
if (typeof value === 'number') {
const numberValue = value.toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
return unit ? `${unit}${numberValue}` : numberValue;
}
return value;
};
</script>
<template>
<div class="grid grid-cols-4 gap-4 p-4 md:grid-cols-6 lg:grid-cols-8">
<div
v-for="(item, index) in props.statistics"
:key="index"
class="rounded-lg border p-4 shadow-sm"
>
<div class="flex items-center gap-3">
<div
:class="`rounded-lg p-2 ${item.color} bg-opacity-10`"
aria-hidden="true"
>
<VbenIcon :icon="item.icon" class="h-5 w-5" />
</div>
<div>
<p class="text-sm text-gray-500">{{ item.title }}</p>
<p class="mt-1 text-lg font-semibold">
{{ formattedValue(item.value, item.unit) }}
</p>
</div>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,84 @@
import type { VbenFormProps } from '#/adapter/form';
import dayjs from 'dayjs';
export const reconciliationStoreSearch: VbenFormProps = {
// 默认展开
collapsed: false,
schema: [
{
component: 'VbenInput',
componentProps: {
placeholder: '请输入诊所名称',
},
defaultValue: '',
fieldName: 'store_name',
label: '诊所名称',
},
{
component: 'RangePicker',
componentProps: {
format: 'YYYY-MM-DD', // 确保格式与你需要的一致
},
defaultValue: [dayjs().startOf('month'), dayjs()],
fieldName: 'search_time',
label: '时间范围',
},
],
// 控制表单是否显示折叠按钮
showCollapseButton: true,
submitButtonOptions: {
content: '查询',
},
// 是否在字段值改变时提交表单
submitOnChange: true,
// 按下回车时是否提交表单
submitOnEnter: false,
};
export const reconciliationDrugSearch: VbenFormProps = {
// 默认展开
collapsed: false,
schema: [
{
component: 'InputNumber',
componentProps: {
placeholder: '请输入诊所ID',
},
defaultValue: '',
fieldName: 'store_id',
dependencies: {
show: false,
triggerFields: ['store_id'],
},
label: '诊所ID',
},
{
component: 'VbenInput',
componentProps: {
placeholder: '请输入药品名称',
},
defaultValue: '',
fieldName: 'drug_name',
label: '药品名称',
},
{
component: 'RangePicker',
componentProps: {
format: 'YYYY-MM-DD', // 确保格式与你需要的一致
},
defaultValue: [dayjs().startOf('month'), dayjs()],
fieldName: 'search_time',
label: '时间范围',
},
],
// 控制表单是否显示折叠按钮
showCollapseButton: true,
submitButtonOptions: {
content: '查询',
},
// 是否在字段值改变时提交表单
submitOnChange: true,
// 按下回车时是否提交表单
submitOnEnter: false,
};

View File

@@ -0,0 +1,134 @@
import type { VxeGridProps } from '#/adapter/vxe-table';
interface RowType {
id: string;
nick_name: string;
email: string;
avatar: string;
roles: { name: string }[];
open_id: string;
code: string;
platform_id: string;
phone: string;
desc: string;
created_at: string;
}
export const storeReconciliationTable: VxeGridProps<RowType> = {
checkboxConfig: {
highlight: true,
labelField: '',
},
columnConfig: {
useKey: true,
},
rowConfig: {
useKey: true,
},
columns: [
{ field: 'name', title: '诊所名称' },
{
field: 'reconciliation.herbal_sales',
title: '草药',
slots: { default: 'herbal' },
},
// { field: 'reconciliation.herbal_sales', title: '草药销售数量' },
// { field: 'reconciliation.herbal_sales_price', title: '草药销售总价' },
// { field: 'reconciliation.herbal_supply_price', title: '草药供货总价' },
{
field: 'reconciliation.medicine_sales',
title: '西药',
slots: { default: 'medicine' },
},
// { field: 'reconciliation.medicine_sales', title: '西药销售数量' },
// { field: 'reconciliation.medicine_sales_price', title: '西药销售总价' },
// { field: 'reconciliation.medicine_supply_price', title: '西药供货总价' },
{
field: 'reconciliation.service_package_sales',
title: '服务包销售数量',
slots: { default: 'service-package' },
},
// { field: 'reconciliation.service_package_sales', title: '服务包销售数量' },
// {
// field: 'reconciliation.service_package_sales_price',
// title: '服务包销售总价',
// },
// {
// field: 'reconciliation.service_package_supply_price',
// title: '服务包供货总价',
// },
{ field: 'reconciliation.express_price', title: '其他费用', slots: { default: 'other'} },
{ field: 'reconciliation.total_sales_price', title: '总销售总价' },
{ field: 'reconciliation.total_supply_price', title: '总供货总价' },
// { field: 'reconciliation.express_price', title: '快递费用' },
// { field: 'reconciliation.process_price', title: '加工费用' },
// { field: 'reconciliation.treatment_price', title: '治疗费用' },
// { field: 'reconciliation.outpatient_price', title: '门诊费用' },
{ field: 'reconciliation.registration_price', title: '挂号费用' },
{ type: 'html', title: '操作', slots: { default: 'action' } },
],
keepSource: true,
pagerConfig: {},
border: false,
toolbarConfig: {
// 是否显示搜索表单控制按钮
// @ts-ignore 正式环境时有完整的类型声明
search: true,
refresh: true, // 刷新
print: false, // 打印
export: false, // 导出
// custom: true, // 自定义列
zoom: true, // 最大化最小化
slots: {
buttons: 'toolbar-buttons',
},
custom: {
// 自定义列-图标
icon: 'vxe-icon-menu',
},
},
showOverflow: false,
};
export const drugReconciliationTable: VxeGridProps<RowType> = {
checkboxConfig: {
highlight: true,
labelField: '',
},
columnConfig: {
useKey: true,
},
rowConfig: {
useKey: true,
},
columns: [
{ field: 'drug_name', title: '药名' },
{ field: 'type_txt', title: '药品类型' },
{ field: 'number', title: '出售数量' },
{ field: 'total_sales_price', title: '总销售金额' },
{ field: 'total_supply_price', title: '总供货金额' },
// { type: 'html', title: '操作', slots: { default: 'action' } },
],
keepSource: true,
pagerConfig: {},
border: false,
toolbarConfig: {
// 是否显示搜索表单控制按钮
// @ts-ignore 正式环境时有完整的类型声明
search: true,
refresh: true, // 刷新
print: false, // 打印
export: false, // 导出
// custom: true, // 自定义列
zoom: true, // 最大化最小化
slots: {
buttons: 'toolbar-buttons',
},
custom: {
// 自定义列-图标
icon: 'vxe-icon-menu',
},
},
showOverflow: false,
};

View File

@@ -0,0 +1,446 @@
<script lang="ts" setup>
import { ref } from 'vue';
import { AnalysisChartsTabs, Page } from '@vben/common-ui';
import { Tag, Tabs, TabPane } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { TableAction } from '#/components/table-action';
import {
getReconciliationDrugList,
getReconciliationStoreList
} from '#/views/finance/reconciliation/api';
import StatisticsReconciliation from './components/statistics.vue';
import { reconciliationDrugSearch, reconciliationStoreSearch } from './config/search';
import { drugReconciliationTable, storeReconciliationTable } from './config/table';
const [Grid, GridApi] = useVbenVxeGrid({
formOptions: reconciliationStoreSearch,
gridOptions: storeReconciliationTable,
});
const [DrugGrid, DrugGridApi] = useVbenVxeGrid({
formOptions: reconciliationDrugSearch,
gridOptions: drugReconciliationTable,
});
const chartTabs = [
{
label: '诊所',
value: 'store',
},
{
label: '药品',
value: 'drug',
},
];
const activeTab = ref('store');
const statistics = ref();
const initTableAjax = () => {
GridApi.setGridOptions({
proxyConfig: {
ajax: {
// 请求后端接口方法
query: async ({ page }, formValues) => {
return await getReconciliationStoreList({
page: page.currentPage,
pageSize: page.pageSize,
...formValues,
}).then((res) => {
statistics.value = null;
statistics.value = [
{
title: '总销售金额',
value: res.count.total_sales_price,
icon: 'mdi:cash-multiple',
color: 'text-blue-600',
unit: '¥',
},
{
title: '总供货额',
value: res.count.total_supply_price,
icon: 'mdi:truck-delivery',
color: 'text-gray-600',
unit: '¥',
},
{
title: '草药销量',
value: res.count.herbal_sales,
icon: 'mdi:leaf',
color: 'text-green-600',
},
{
title: '草药销售金额',
value: res.count.herbal_sales_price,
icon: 'mdi:currency-cny',
color: 'text-green-600',
unit: '¥',
},
{
title: '草药供货金额',
value: res.count.herbal_supply_price,
icon: 'mdi:package-variant',
color: 'text-green-600',
unit: '¥',
},
{
title: '成药销量',
value: res.count.medicine_sales,
icon: 'mdi:pill',
color: 'text-purple-600',
},
{
title: '成药销售金额',
value: res.count.medicine_sales_price,
icon: 'mdi:currency-usd',
color: 'text-purple-600',
unit: '¥',
},
{
title: '成药供货金额',
value: res.count.medicine_supply_price,
icon: 'mdi:pharmacy',
color: 'text-purple-600',
unit: '¥',
},
{
title: '快递费',
value: res.count.express_price,
icon: 'skill-icons:expressjs-dark',
color: 'text-orange-600',
unit: '¥',
},
{
title: '加工费',
value: res.count.process_price,
icon: 'mdi:factory',
color: 'text-yellow-600',
unit: '¥',
},
{
title: '诊疗费',
value: res.count.treatment_price,
icon: 'mdi:medical-bag',
color: 'text-red-600',
unit: '¥',
},
{
title: '门诊收入',
value: res.count.outpatient_price,
icon: 'mdi:hospital-box',
color: 'text-blue-600',
unit: '¥',
},
{
title: '挂号费',
value: res.count.registration_price,
icon: 'mdi:card-account-details',
color: 'text-pink-600',
unit: '¥',
},
];
return res.list;
});
},
},
},
});
// 药品
DrugGridApi.setGridOptions({
proxyConfig: {
ajax: {
// 请求后端接口方法
query: async ({ page }, formValues) => {
return await getReconciliationDrugList({
page: page.currentPage,
pageSize: page.pageSize,
...formValues,
}).then((res) => {
statistics.value = null;
statistics.value = [
{
title: '总销售金额',
value: res.count.total_sales_price,
icon: 'mdi:cash-multiple',
color: 'text-blue-600',
unit: '¥',
},
{
title: '总供货额',
value: res.count.total_supply_price,
icon: 'mdi:truck-delivery',
color: 'text-gray-600',
unit: '¥',
},
{
title: '销量',
value: res.count.number,
icon: 'streamline-emojis:pill',
color: 'text-purple-600',
},
];
return res.list;
});
},
},
},
});
};
const goDrug = (storeId) => {
reconciliationDrugSearch.schema[0].value = storeId;
activeTab.value = 'drug';
}
initTableAjax();
</script>
<template>
<Page
auto-content-height
title="对账单"
>
<StatisticsReconciliation
v-if="statistics"
:statistics="statistics"
/>
<Tabs v-model:active-key="activeTab" type="card" :destroyInactiveTabPane="true">
<TabPane key="store" tab="诊所">
<Grid>
<template #toolbar-buttons></template>
<template #toolbar-tools></template>
<template #herbal="{ row }">
<div class="mt-2">
<Tag color="green">
销售数量{{ row.reconciliation.herbal_sales }}
</Tag>
</div>
<div class="mt-2">
<Tag color="orange">
销售总价{{ row.reconciliation.herbal_sales_price }}
</Tag>
</div>
<div class="mt-2">
<Tag color="red">
供货总价{{ row.reconciliation.herbal_supply_price }}
</Tag>
</div>
</template>
<template #medicine="{ row }">
<div class="mt-2">
<Tag color="green">
销售数量{{ row.reconciliation.medicine_sales }}
</Tag>
</div>
<div class="mt-2">
<Tag color="orange">
销售总价{{ row.reconciliation.medicine_sales_price }}
</Tag>
</div>
<div class="mt-2">
<Tag color="red">
供货总价{{ row.reconciliation.medicine_supply_price }}
</Tag>
</div>
</template>
<template #service-package="{ row }">
<div class="mt-2">
<Tag color="green">
销售数量{{ row.reconciliation.service_package_sales }}
</Tag>
</div>
<div class="mt-2">
<Tag color="orange">
销售总价{{ row.reconciliation.service_package_sales_price }}
</Tag>
</div>
<div class="mt-2">
<Tag color="red">
供货总价{{ row.reconciliation.service_package_supply_price }}
</Tag>
</div>
</template>
<template #other="{ row }">
<div class="mt-2">
<Tag color="green">
快递费用{{ row.reconciliation.express_price }}
</Tag>
</div>
<div class="mt-2">
<Tag color="green">
加工费{{ row.reconciliation.process_price }}
</Tag>
</div>
<div class="mt-2">
<Tag color="green">
治疗费{{ row.reconciliation.treatment_price }}
</Tag>
</div>
<div class="mt-2">
<Tag color="green">
诊疗费{{ row.reconciliation.outpatient_price }}
</Tag>
</div>
</template>
<template #action="{ row }">
<TableAction
:actions="[
{
label: '查看',
type: 'link',
icon: 'marketeq:eye',
size: 'small',
onClick: goDrug.bind(null, row.id),
},
]"
:drop-down-actions="[]"
/>
</template>
</Grid>
</TabPane>
<TabPane key="drug" tab="药品">
<DrugGrid>
<template #toolbar-buttons></template>
<template #toolbar-tools></template>
<template #action="{ row }">
<TableAction
:actions="[
// {
// label: '编辑',
// type: 'link',
// icon: 'uil:edit',
// size: 'small',
// // auth: ['admin', 'sys:role:detail'],
// onClick: showModal.bind(null, row, true),
// },
]"
:drop-down-actions="[]"
/>
</template>
</DrugGrid>
</TabPane>
</Tabs>
<!-- <AnalysisChartsTabs :tabs="chartTabs" :value="activeTab" class="mt-5">-->
<!-- <template #store>-->
<!-- <Grid>-->
<!-- <template #toolbar-buttons></template>-->
<!-- <template #toolbar-tools></template>-->
<!-- <template #herbal="{ row }">-->
<!-- <div class="mt-2">-->
<!-- <Tag color="green">-->
<!-- 销售数量{{ row.reconciliation.herbal_sales }}-->
<!-- </Tag>-->
<!-- </div>-->
<!-- <div class="mt-2">-->
<!-- <Tag color="green">-->
<!-- 销售总价{{ row.reconciliation.herbal_sales_price }}-->
<!-- </Tag>-->
<!-- </div>-->
<!-- <div class="mt-2">-->
<!-- <Tag color="warning">-->
<!-- 供货总价{{ row.reconciliation.herbal_supply_price }}-->
<!-- </Tag>-->
<!-- </div>-->
<!-- </template>-->
<!-- <template #medicine="{ row }">-->
<!-- <div class="mt-2">-->
<!-- <Tag color="green">-->
<!-- 销售数量{{ row.reconciliation.medicine_sales }}-->
<!-- </Tag>-->
<!-- </div>-->
<!-- <div class="mt-2">-->
<!-- <Tag color="green">-->
<!-- 销售总价{{ row.reconciliation.medicine_sales_price }}-->
<!-- </Tag>-->
<!-- </div>-->
<!-- <div class="mt-2">-->
<!-- <Tag color="warning">-->
<!-- 供货总价{{ row.reconciliation.medicine_supply_price }}-->
<!-- </Tag>-->
<!-- </div>-->
<!-- </template>-->
<!-- <template #service-package="{ row }">-->
<!-- <div class="mt-2">-->
<!-- <Tag color="green">-->
<!-- 销售数量{{ row.reconciliation.service_package_sales }}-->
<!-- </Tag>-->
<!-- </div>-->
<!-- <div class="mt-2">-->
<!-- <Tag color="green">-->
<!-- 销售总价{{ row.reconciliation.service_package_sales_price }}-->
<!-- </Tag>-->
<!-- </div>-->
<!-- <div class="mt-2">-->
<!-- <Tag color="warning">-->
<!-- 供货总价{{ row.reconciliation.service_package_supply_price }}-->
<!-- </Tag>-->
<!-- </div>-->
<!-- </template>-->
<!-- <template #other="{ row }">-->
<!-- <div class="mt-2">-->
<!-- <Tag color="green">-->
<!-- 快递费用{{ row.reconciliation.express_price }}-->
<!-- </Tag>-->
<!-- </div>-->
<!-- <div class="mt-2">-->
<!-- <Tag color="green">-->
<!-- 加工费{{ row.reconciliation.process_price }}-->
<!-- </Tag>-->
<!-- </div>-->
<!-- <div class="mt-2">-->
<!-- <Tag color="green">-->
<!-- 治疗费{{ row.reconciliation.treatment_price }}-->
<!-- </Tag>-->
<!-- </div>-->
<!-- <div class="mt-2">-->
<!-- <Tag color="green">-->
<!-- 诊疗费{{ row.reconciliation.outpatient_price }}-->
<!-- </Tag>-->
<!-- </div>-->
<!-- </template>-->
<!-- <template #action="{ row }">-->
<!-- <TableAction-->
<!-- :actions="[-->
<!-- {-->
<!-- label: '查看',-->
<!-- type: 'link',-->
<!-- icon: 'uil:edit',-->
<!-- size: 'small',-->
<!-- onClick: goDrug.bind(null, row, true),-->
<!-- },-->
<!-- ]"-->
<!-- :drop-down-actions="[]"-->
<!-- />-->
<!-- </template>-->
<!-- </Grid>-->
<!-- </template>-->
<!-- <template #drug>-->
<!-- <DrugGrid>-->
<!-- <template #toolbar-buttons></template>-->
<!-- <template #toolbar-tools></template>-->
<!-- <template #action="{ row }">-->
<!-- <TableAction-->
<!-- :actions="[-->
<!-- // {-->
<!-- // label: '编辑',-->
<!-- // type: 'link',-->
<!-- // icon: 'uil:edit',-->
<!-- // size: 'small',-->
<!-- // // auth: ['admin', 'sys:role:detail'],-->
<!-- // onClick: showModal.bind(null, row, true),-->
<!-- // },-->
<!-- ]"-->
<!-- :drop-down-actions="[]"-->
<!-- />-->
<!-- </template>-->
<!-- </DrugGrid>-->
<!-- </template>-->
<!-- </AnalysisChartsTabs>-->
</Page>
</template>

View File

@@ -12,6 +12,7 @@ export * from '@vben-core/popup-ui';
export {
VbenButton,
VbenCountToAnimator,
VbenIcon,
VbenInputPassword,
VbenLoading,
VbenPinInput,

View File

@@ -11,3 +11,5 @@ export const MdiGithub = createIconifyIcon('mdi:github');
export const MdiGoogle = createIconifyIcon('mdi:google');
export const MdiQqchat = createIconifyIcon('mdi:qqchat');
export const MdiCashMultiple = createIconifyIcon('mdi:cash-multiple');