1. 订单追溯、模拟支付(仅限本地环境)
Some checks failed
Close stale issues / stale (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / CI OK (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
Some checks failed
Close stale issues / stale (push) Has been cancelled
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / CI OK (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
This commit is contained in:
31
apps/web-antd/src/views/business/order/api/order-ops.ts
Normal file
31
apps/web-antd/src/views/business/order/api/order-ops.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
const prefix = 'order/';
|
||||
|
||||
export async function simulatePayApi(data: {
|
||||
order_id: number;
|
||||
order_type: 'product' | 'register';
|
||||
}) {
|
||||
return requestClient.post<any>(`${prefix}simulate-pay`, data);
|
||||
}
|
||||
|
||||
export async function getOrderTraceApi(params: {
|
||||
order_id: number;
|
||||
scene: 'product' | 'register';
|
||||
}) {
|
||||
return requestClient.get<any>(`${prefix}trace`, { params });
|
||||
}
|
||||
|
||||
export async function getOrderLedgerDetailApi(params: {
|
||||
order_id: number;
|
||||
order_type: number;
|
||||
scope?: 'all' | 'platform' | 'store';
|
||||
}) {
|
||||
return requestClient.get<any>(`${prefix}ledger-detail`, { params });
|
||||
}
|
||||
|
||||
export async function getOrderReconciliationDetailApi(params: {
|
||||
product_order_id: number;
|
||||
}) {
|
||||
return requestClient.get<any>(`${prefix}reconciliation-detail`, { params });
|
||||
}
|
||||
@@ -0,0 +1,467 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { useVbenDrawer, useVbenModal } from '@vben/common-ui';
|
||||
import { useUserStore } from '@vben/stores';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Descriptions,
|
||||
Image,
|
||||
Space,
|
||||
Spin,
|
||||
Table,
|
||||
Tabs,
|
||||
Tag,
|
||||
Timeline,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
getOrderLedgerDetailApi,
|
||||
getOrderReconciliationDetailApi,
|
||||
getOrderTraceApi,
|
||||
} from '#/views/business/order/api/order-ops';
|
||||
import PrescriptionDetail from '#/views/doctor/doctor-reception/components/PrescriptionDetail.vue';
|
||||
|
||||
import DetailModal from '../product-order/components/detail.vue';
|
||||
import ReconciliationDetailTable from './reconciliation-detail-table.vue';
|
||||
|
||||
defineOptions({ name: 'OrderTraceDrawer' });
|
||||
|
||||
type LedgerScope = 'all' | 'platform' | 'store';
|
||||
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const loading = ref(false);
|
||||
const ledgerLoading = ref(false);
|
||||
const reconciliationLoading = ref(false);
|
||||
const activeTab = ref('timeline');
|
||||
const ledgerScope = ref<LedgerScope>('all');
|
||||
const ledgerSubTab = ref<'product' | 'register'>('product');
|
||||
const traceData = ref<Record<string, any> | null>(null);
|
||||
const ledgerProduct = ref<Record<string, any> | null>(null);
|
||||
const ledgerRegister = ref<Record<string, any> | null>(null);
|
||||
const reconciliationData = ref<Record<string, any> | null>(null);
|
||||
const loadedTabs = ref(new Set<string>());
|
||||
|
||||
const isPlatformAdmin = computed(
|
||||
() => userStore?.userInfo?.roles?.user_type === 2,
|
||||
);
|
||||
|
||||
const ledgerColumns = [
|
||||
{ title: '费用类型', dataIndex: 'fee_type_txt', key: 'fee_type_txt' },
|
||||
{ title: '药品图片', dataIndex: 'drug_image', key: 'drug_image', width: 80 },
|
||||
{ title: '药品名称', dataIndex: 'drug_name', key: 'drug_name' },
|
||||
{ title: '药品编号', dataIndex: 'drug_number', key: 'drug_number' },
|
||||
{ title: '药品规格', dataIndex: 'specification', key: 'specification' },
|
||||
{ title: '分账对象', dataIndex: 'user_type_txt', key: 'user_type_txt' },
|
||||
{ title: '门店', dataIndex: ['store', 'name'], key: 'store_name' },
|
||||
{ title: '分账金额', dataIndex: 'money', key: 'money' },
|
||||
{ title: '结算状态', dataIndex: 'status_txt', key: 'status_txt' },
|
||||
{ title: '创建时间', dataIndex: 'created_at', key: 'created_at' },
|
||||
];
|
||||
|
||||
const currentLedgerData = computed(() =>
|
||||
ledgerSubTab.value === 'product' ? ledgerProduct.value : ledgerRegister.value,
|
||||
);
|
||||
|
||||
function formatDrugCell(value?: string) {
|
||||
return value?.trim() ? value : '-';
|
||||
}
|
||||
|
||||
function resetDrawerState() {
|
||||
traceData.value = null;
|
||||
ledgerProduct.value = null;
|
||||
ledgerRegister.value = null;
|
||||
reconciliationData.value = null;
|
||||
loadedTabs.value = new Set<string>();
|
||||
activeTab.value = 'timeline';
|
||||
ledgerScope.value = isPlatformAdmin.value ? 'all' : 'store';
|
||||
ledgerSubTab.value = 'product';
|
||||
}
|
||||
|
||||
const [Drawer, drawerApi] = useVbenDrawer({
|
||||
class: 'w-[80%]',
|
||||
placement: 'right',
|
||||
showConfirmButton: false,
|
||||
showCancelButton: false,
|
||||
destroyOnClose: true,
|
||||
onOpenChange(isOpen) {
|
||||
if (isOpen) {
|
||||
ledgerScope.value = isPlatformAdmin.value ? 'all' : 'store';
|
||||
void loadTrace();
|
||||
} else {
|
||||
resetDrawerState();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const [PrescriptionDetailModal, PrescriptionDetailModalApi] = useVbenModal({
|
||||
connectedComponent: PrescriptionDetail,
|
||||
});
|
||||
|
||||
const [OrderDetailModal, OrderDetailModalApi] = useVbenModal({
|
||||
connectedComponent: DetailModal,
|
||||
});
|
||||
|
||||
async function loadTrace() {
|
||||
const data = drawerApi.getData<{
|
||||
order_id: number;
|
||||
order_no?: string;
|
||||
scene: 'product' | 'register';
|
||||
}>();
|
||||
if (!data?.order_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
drawerApi.setState({ loading: true });
|
||||
try {
|
||||
traceData.value = await getOrderTraceApi({
|
||||
scene: data.scene,
|
||||
order_id: data.order_id,
|
||||
});
|
||||
if (traceData.value?.meta?.is_platform_admin === false) {
|
||||
ledgerScope.value = 'store';
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
drawerApi.setState({ loading: false });
|
||||
}
|
||||
}
|
||||
|
||||
async function loadLedgerDetail(force = false) {
|
||||
if (!force && loadedTabs.value.has(`ledger:${ledgerScope.value}`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const productOrderId = traceData.value?.links?.product_order_id;
|
||||
const registerId = traceData.value?.links?.register_id;
|
||||
|
||||
ledgerLoading.value = true;
|
||||
try {
|
||||
const requests: Promise<any>[] = [];
|
||||
if (productOrderId) {
|
||||
requests.push(
|
||||
getOrderLedgerDetailApi({
|
||||
order_id: productOrderId,
|
||||
order_type: 1,
|
||||
scope: ledgerScope.value,
|
||||
}).then((res) => {
|
||||
ledgerProduct.value = res;
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
ledgerProduct.value = null;
|
||||
}
|
||||
|
||||
if (registerId) {
|
||||
requests.push(
|
||||
getOrderLedgerDetailApi({
|
||||
order_id: registerId,
|
||||
order_type: 2,
|
||||
scope: ledgerScope.value,
|
||||
}).then((res) => {
|
||||
ledgerRegister.value = res;
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
ledgerRegister.value = null;
|
||||
}
|
||||
|
||||
await Promise.all(requests);
|
||||
loadedTabs.value.add(`ledger:${ledgerScope.value}`);
|
||||
} finally {
|
||||
ledgerLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadReconciliationDetail() {
|
||||
if (loadedTabs.value.has('reconciliation')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const productOrderId = traceData.value?.links?.product_order_id;
|
||||
if (!productOrderId) {
|
||||
reconciliationData.value = {
|
||||
items: [],
|
||||
sum_sales_price: '0',
|
||||
sum_supply_price: '0',
|
||||
reconciliation_hint:
|
||||
traceData.value?.summary?.reconciliation_hint || '暂无关联商品订单',
|
||||
};
|
||||
loadedTabs.value.add('reconciliation');
|
||||
return;
|
||||
}
|
||||
|
||||
reconciliationLoading.value = true;
|
||||
try {
|
||||
reconciliationData.value = await getOrderReconciliationDetailApi({
|
||||
product_order_id: productOrderId,
|
||||
});
|
||||
loadedTabs.value.add('reconciliation');
|
||||
} finally {
|
||||
reconciliationLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleMainTabChange(key: string) {
|
||||
activeTab.value = key;
|
||||
if (key === 'ledger') {
|
||||
await loadLedgerDetail();
|
||||
} else if (key === 'reconciliation') {
|
||||
await loadReconciliationDetail();
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLedgerScopeChange(scope: LedgerScope) {
|
||||
ledgerScope.value = scope;
|
||||
loadedTabs.value.delete(`ledger:${scope}`);
|
||||
await loadLedgerDetail(true);
|
||||
}
|
||||
|
||||
function openPrescriptionDetail() {
|
||||
const id = traceData.value?.links?.prescription_id;
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
PrescriptionDetailModalApi.setData({ values: id });
|
||||
PrescriptionDetailModalApi.open();
|
||||
}
|
||||
|
||||
function openOrderDetail() {
|
||||
const id = traceData.value?.links?.product_order_id;
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
OrderDetailModalApi.setData({ id });
|
||||
OrderDetailModalApi.open();
|
||||
}
|
||||
|
||||
function goAuditPrescription() {
|
||||
const id = traceData.value?.links?.prescription_id;
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
void router.push({
|
||||
path: '/pharmacist/review-prescription',
|
||||
query: { prescription_id: String(id) },
|
||||
});
|
||||
}
|
||||
|
||||
function prescriptionStatusTag() {
|
||||
const summary = traceData.value?.summary;
|
||||
if (!summary?.prescription_id) {
|
||||
return null;
|
||||
}
|
||||
const auditNode = traceData.value?.timeline?.find(
|
||||
(n: Record<string, any>) =>
|
||||
n.key === 'prescription_audited' || n.key === 'prescription_rejected',
|
||||
);
|
||||
if (auditNode) {
|
||||
return auditNode.status;
|
||||
}
|
||||
return '待审核';
|
||||
}
|
||||
|
||||
const reconciliationHint = computed(
|
||||
() =>
|
||||
reconciliationData.value?.reconciliation_hint ||
|
||||
traceData.value?.summary?.reconciliation_hint ||
|
||||
'',
|
||||
);
|
||||
|
||||
watch(isPlatformAdmin, (val) => {
|
||||
if (!val) {
|
||||
ledgerScope.value = 'store';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Drawer title="订单溯源">
|
||||
<Spin :spinning="loading">
|
||||
<div v-if="traceData" class="flex flex-col gap-4">
|
||||
<Descriptions bordered :column="2" size="small">
|
||||
<Descriptions.Item label="商品订单号">
|
||||
{{ traceData.summary?.product_order_no || '—' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="挂号订单号">
|
||||
{{ traceData.summary?.register_order_no || '—' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="处方号">
|
||||
{{ traceData.summary?.prescription_no || '—' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="结算状态">
|
||||
{{ traceData.summary?.is_settled === 1 ? '已结算' : '未结算' }}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
<Space wrap>
|
||||
<Button
|
||||
v-if="traceData.links?.prescription_id"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="openPrescriptionDetail"
|
||||
>
|
||||
处方详情
|
||||
</Button>
|
||||
<Button
|
||||
v-if="traceData.links?.product_order_id"
|
||||
size="small"
|
||||
@click="openOrderDetail"
|
||||
>
|
||||
订单详情
|
||||
</Button>
|
||||
<Button
|
||||
v-if="traceData.links?.prescription_id && prescriptionStatusTag() === '待审核'"
|
||||
size="small"
|
||||
danger
|
||||
@click="goAuditPrescription"
|
||||
>
|
||||
去审方
|
||||
</Button>
|
||||
<Tag v-else-if="prescriptionStatusTag()" color="green">
|
||||
审方:{{ prescriptionStatusTag() }}
|
||||
</Tag>
|
||||
</Space>
|
||||
|
||||
<Tabs v-model:active-key="activeTab" @change="(key) => handleMainTabChange(String(key))">
|
||||
<Tabs.TabPane key="timeline" tab="溯源时间线">
|
||||
<Timeline v-if="traceData.timeline?.length">
|
||||
<Timeline.Item
|
||||
v-for="node in traceData.timeline"
|
||||
:key="node.key"
|
||||
>
|
||||
<div class="font-medium">{{ node.title }}</div>
|
||||
<div class="text-sm text-gray-500">{{ node.time }}</div>
|
||||
<div v-if="node.status" class="text-sm">
|
||||
<Tag>{{ node.status }}</Tag>
|
||||
</div>
|
||||
<div v-if="node.extra" class="mt-1 text-sm text-gray-600">
|
||||
{{ node.extra }}
|
||||
</div>
|
||||
</Timeline.Item>
|
||||
</Timeline>
|
||||
<div v-else class="py-6 text-center text-gray-500">暂无溯源记录</div>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane key="ledger" tab="分账明细">
|
||||
<Spin :spinning="ledgerLoading">
|
||||
<Tabs
|
||||
v-if="isPlatformAdmin"
|
||||
v-model:active-key="ledgerScope"
|
||||
type="card"
|
||||
class="mb-3"
|
||||
@change="(key) => handleLedgerScopeChange(key as LedgerScope)"
|
||||
>
|
||||
<Tabs.TabPane key="all" tab="全部" />
|
||||
<Tabs.TabPane key="store" tab="门店" />
|
||||
<Tabs.TabPane key="platform" tab="平台" />
|
||||
</Tabs>
|
||||
|
||||
<Tabs v-model:active-key="ledgerSubTab" type="card">
|
||||
<Tabs.TabPane key="product" tab="商品订单分账">
|
||||
<div class="mb-2 text-sm">
|
||||
合计分账:{{ ledgerProduct?.sum_money ?? '0' }}
|
||||
</div>
|
||||
<Table
|
||||
:columns="ledgerColumns"
|
||||
:data-source="ledgerProduct?.items ?? []"
|
||||
:pagination="false"
|
||||
row-key="id"
|
||||
size="small"
|
||||
>
|
||||
<template #bodyCell="{ column, record, text }">
|
||||
<template v-if="column.key === 'drug_image'">
|
||||
<Image
|
||||
v-if="record.drug_image"
|
||||
:src="record.drug_image"
|
||||
:width="48"
|
||||
:height="48"
|
||||
class="object-cover"
|
||||
/>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<template
|
||||
v-else-if="
|
||||
column.key === 'drug_name' ||
|
||||
column.key === 'drug_number' ||
|
||||
column.key === 'specification'
|
||||
"
|
||||
>
|
||||
{{ formatDrugCell(text) }}
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
<div
|
||||
v-if="!ledgerProduct?.items?.length && !ledgerLoading"
|
||||
class="py-6 text-center text-gray-500"
|
||||
>
|
||||
暂无分账记录
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key="register" tab="挂号订单分账">
|
||||
<div class="mb-2 text-sm">
|
||||
合计分账:{{ ledgerRegister?.sum_money ?? '0' }}
|
||||
</div>
|
||||
<Table
|
||||
:columns="ledgerColumns"
|
||||
:data-source="ledgerRegister?.items ?? []"
|
||||
:pagination="false"
|
||||
row-key="id"
|
||||
size="small"
|
||||
>
|
||||
<template #bodyCell="{ column, record, text }">
|
||||
<template v-if="column.key === 'drug_image'">
|
||||
<Image
|
||||
v-if="record.drug_image"
|
||||
:src="record.drug_image"
|
||||
:width="48"
|
||||
:height="48"
|
||||
class="object-cover"
|
||||
/>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<template
|
||||
v-else-if="
|
||||
column.key === 'drug_name' ||
|
||||
column.key === 'drug_number' ||
|
||||
column.key === 'specification'
|
||||
"
|
||||
>
|
||||
{{ formatDrugCell(text) }}
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
<div
|
||||
v-if="!ledgerRegister?.items?.length && !ledgerLoading"
|
||||
class="py-6 text-center text-gray-500"
|
||||
>
|
||||
暂无分账记录
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Spin>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane key="reconciliation" tab="对账明细">
|
||||
<Spin :spinning="reconciliationLoading">
|
||||
<ReconciliationDetailTable
|
||||
:items="reconciliationData?.items ?? []"
|
||||
:sum-sales-price="reconciliationData?.sum_sales_price"
|
||||
:sum-supply-price="reconciliationData?.sum_supply_price"
|
||||
:summary="reconciliationData?.summary"
|
||||
:hint="reconciliationHint"
|
||||
/>
|
||||
</Spin>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
</Spin>
|
||||
<PrescriptionDetailModal />
|
||||
<OrderDetailModal />
|
||||
</Drawer>
|
||||
</template>
|
||||
@@ -0,0 +1,154 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { Table, Tag } from 'ant-design-vue';
|
||||
|
||||
defineOptions({ name: 'ReconciliationDetailTable' });
|
||||
|
||||
const props = defineProps<{
|
||||
hint?: string;
|
||||
items: Record<string, any>[];
|
||||
sumSalesPrice?: string;
|
||||
sumSupplyPrice?: string;
|
||||
summary?: Record<string, any> | null;
|
||||
}>();
|
||||
|
||||
const columns = [
|
||||
{ title: '药品名称', dataIndex: 'drug_name', key: 'drug_name' },
|
||||
{ title: '药品编号', dataIndex: 'drug_number', key: 'drug_number' },
|
||||
{ title: '数量', dataIndex: 'number', key: 'number', width: 80 },
|
||||
{ title: '销售总价', dataIndex: 'total_price', key: 'total_price' },
|
||||
{ title: '供货总价', dataIndex: 'total_buy_price', key: 'total_buy_price' },
|
||||
{ title: '状态', dataIndex: 'status_txt', key: 'status_txt', width: 90 },
|
||||
{ title: '对账时间', dataIndex: 'created_at', key: 'created_at', width: 170 },
|
||||
];
|
||||
|
||||
function hasValue(v: unknown): boolean {
|
||||
if (v === null || v === undefined || v === '') return false;
|
||||
const n = Number(v);
|
||||
return !Number.isNaN(n) && n !== 0;
|
||||
}
|
||||
|
||||
const displaySummary = computed(() => props.summary ?? null);
|
||||
|
||||
const totalSales = computed(
|
||||
() => displaySummary.value?.total_sales_price ?? props.sumSalesPrice,
|
||||
);
|
||||
const totalSupply = computed(
|
||||
() => displaySummary.value?.total_supply_price ?? props.sumSupplyPrice,
|
||||
);
|
||||
|
||||
const showHerbal = computed(() => {
|
||||
const s = displaySummary.value;
|
||||
if (!s) return false;
|
||||
return hasValue(s.herbal_sales) || hasValue(s.herbal_sales_price) || hasValue(s.herbal_supply_price);
|
||||
});
|
||||
|
||||
const showMedicine = computed(() => {
|
||||
const s = displaySummary.value;
|
||||
if (!s) return false;
|
||||
return hasValue(s.medicine_sales) || hasValue(s.medicine_sales_price) || hasValue(s.medicine_supply_price);
|
||||
});
|
||||
|
||||
const showServicePackage = computed(() => {
|
||||
const s = displaySummary.value;
|
||||
if (!s) return false;
|
||||
return (
|
||||
hasValue(s.service_package_sales)
|
||||
|| hasValue(s.service_package_sales_price)
|
||||
|| hasValue(s.service_package_supply_price)
|
||||
);
|
||||
});
|
||||
|
||||
const showOtherFees = computed(() => {
|
||||
const s = displaySummary.value;
|
||||
if (!s) return false;
|
||||
return hasValue(s.express_price) || hasValue(s.process_price) || hasValue(s.treatment_price);
|
||||
});
|
||||
|
||||
const showRegistration = computed(() => hasValue(displaySummary.value?.registration_price));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="displaySummary" class="mb-4">
|
||||
<div class="mb-3 flex flex-wrap gap-4 text-sm font-medium">
|
||||
<span>总销售额:{{ totalSales }}</span>
|
||||
<span>总供货额:{{ totalSupply }}</span>
|
||||
<span v-if="showRegistration">
|
||||
挂号费:{{ displaySummary?.registration_price }}
|
||||
<template v-if="displaySummary?.register_order_no">
|
||||
({{ displaySummary.register_order_no }})
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 grid grid-cols-1 gap-3 md:grid-cols-2">
|
||||
<div v-if="showHerbal" class="rounded border border-gray-200 p-3 dark:border-gray-700">
|
||||
<div class="mb-2 text-sm font-medium">草药</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<Tag color="green">销售数量:{{ displaySummary?.herbal_sales }}</Tag>
|
||||
<Tag color="orange">销售总价:{{ displaySummary?.herbal_sales_price }}</Tag>
|
||||
<Tag color="red">供货总价:{{ displaySummary?.herbal_supply_price }}</Tag>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="showMedicine" class="rounded border border-gray-200 p-3 dark:border-gray-700">
|
||||
<div class="mb-2 text-sm font-medium">西药</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<Tag color="green">销售数量:{{ displaySummary?.medicine_sales }}</Tag>
|
||||
<Tag color="orange">销售总价:{{ displaySummary?.medicine_sales_price }}</Tag>
|
||||
<Tag color="red">供货总价:{{ displaySummary?.medicine_supply_price }}</Tag>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="showServicePackage" class="rounded border border-gray-200 p-3 dark:border-gray-700">
|
||||
<div class="mb-2 text-sm font-medium">服务包</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<Tag color="green">销售数量:{{ displaySummary?.service_package_sales }}</Tag>
|
||||
<Tag color="orange">销售总价:{{ displaySummary?.service_package_sales_price }}</Tag>
|
||||
<Tag color="red">供货总价:{{ displaySummary?.service_package_supply_price }}</Tag>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="showOtherFees" class="rounded border border-gray-200 p-3 dark:border-gray-700">
|
||||
<div class="mb-2 text-sm font-medium">其他费用</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<Tag v-if="hasValue(displaySummary?.express_price)" color="green">
|
||||
快递费:{{ displaySummary?.express_price }}
|
||||
</Tag>
|
||||
<Tag v-if="hasValue(displaySummary?.process_price)" color="green">
|
||||
加工费:{{ displaySummary?.process_price }}
|
||||
</Tag>
|
||||
<Tag v-if="hasValue(displaySummary?.treatment_price)" color="green">
|
||||
诊疗费:{{ displaySummary?.treatment_price }}
|
||||
</Tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="sumSalesPrice != null" class="mb-3 flex gap-4 text-sm">
|
||||
<span>销售总价合计:{{ sumSalesPrice }}</span>
|
||||
<span>供货总价合计:{{ sumSupplyPrice }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="items.length" class="mb-2 text-sm font-medium">药品明细</div>
|
||||
<Table
|
||||
:columns="columns"
|
||||
:data-source="items"
|
||||
:pagination="false"
|
||||
row-key="id"
|
||||
size="small"
|
||||
/>
|
||||
<div
|
||||
v-if="!items.length && !displaySummary"
|
||||
class="py-8 text-center text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{ hint || '订单尚未结算或无对账记录' }}
|
||||
</div>
|
||||
<div
|
||||
v-else-if="!items.length && displaySummary && !showHerbal && !showMedicine && !showServicePackage && !showOtherFees && !showRegistration"
|
||||
class="py-4 text-center text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{ hint || '暂无药品对账明细' }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,11 +1,13 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { useVbenDrawer, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Card, Descriptions, Image, Tag, Timeline } from 'ant-design-vue';
|
||||
import { Button, Card, Descriptions, Image, Space, Tag, Timeline } from 'ant-design-vue';
|
||||
|
||||
import {expressDetailByOrderId, getOrderInfo} from '../api';
|
||||
import OrderTraceDrawer from '#/views/business/order/components/order-trace-drawer.vue';
|
||||
|
||||
import { expressDetailByOrderId, getOrderInfo } from '../api';
|
||||
|
||||
defineOptions({
|
||||
name: 'DetailModal',
|
||||
@@ -19,6 +21,22 @@ const expressDetail = ref({});
|
||||
// 订单发货方式
|
||||
const deliveryMethod = ref(-1);
|
||||
|
||||
const [TraceDrawer, traceDrawerApi] = useVbenDrawer({
|
||||
connectedComponent: OrderTraceDrawer,
|
||||
});
|
||||
|
||||
function openOrderTrace() {
|
||||
if (!data.value?.id) {
|
||||
return;
|
||||
}
|
||||
traceDrawerApi.setData({
|
||||
scene: 'product',
|
||||
order_id: data.value.id,
|
||||
order_no: data.value.order_no,
|
||||
});
|
||||
traceDrawerApi.open();
|
||||
}
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
@@ -87,6 +105,11 @@ const orderTypeMap = {
|
||||
<template>
|
||||
<Modal class="w-[80%]" title="订单详情">
|
||||
<div v-if="data" class="flex flex-col gap-4">
|
||||
<Space>
|
||||
<Button type="primary" size="small" @click="openOrderTrace">
|
||||
查看溯源
|
||||
</Button>
|
||||
</Space>
|
||||
<h3 class="mt-4">订单信息</h3>
|
||||
<Descriptions
|
||||
:column="{ xxl: 2, xl: 2, lg: 2, md: 1, sm: 1, xs: 1 }"
|
||||
@@ -319,6 +342,7 @@ const orderTypeMap = {
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<TraceDrawer />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
AnalysisOverview,
|
||||
type AnalysisOverviewItem,
|
||||
Page,
|
||||
useVbenDrawer,
|
||||
useVbenModal,
|
||||
} from '@vben/common-ui';
|
||||
import { SvgCakeIcon } from '@vben/icons';
|
||||
@@ -24,6 +25,8 @@ import {
|
||||
saleAmountApi,
|
||||
updateFreeShipping,
|
||||
} from '#/views/business/order/product-order/api';
|
||||
import { simulatePayApi } from '#/views/business/order/api/order-ops';
|
||||
import OrderTraceDrawer from '#/views/business/order/components/order-trace-drawer.vue';
|
||||
import PrescriptionDetail from '#/views/doctor/doctor-reception/components/PrescriptionDetail.vue';
|
||||
|
||||
import DetailModal from './components/detail.vue';
|
||||
@@ -179,6 +182,34 @@ const saleAmount = () => {
|
||||
const [PrescriptionDetailModal, PrescriptionDetailModalApi] = useVbenModal({
|
||||
connectedComponent: PrescriptionDetail,
|
||||
});
|
||||
|
||||
const [TraceDrawer, traceDrawerApi] = useVbenDrawer({
|
||||
connectedComponent: OrderTraceDrawer,
|
||||
});
|
||||
|
||||
function openOrderTrace(row: Record<string, any>) {
|
||||
traceDrawerApi.setData({
|
||||
scene: 'product',
|
||||
order_id: row.id,
|
||||
order_no: row.order_no,
|
||||
});
|
||||
traceDrawerApi.open();
|
||||
}
|
||||
|
||||
function handleSimulatePay(row: Record<string, any>) {
|
||||
AntdModal.confirm({
|
||||
title: '模拟支付',
|
||||
content: `确认为订单 ${row.order_no} 模拟易票联支付回调?`,
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
await simulatePayApi({ order_type: 'product', order_id: row.id });
|
||||
message.success('模拟支付成功');
|
||||
await gridApi.query();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const openPrescriptionDetail = (values) => {
|
||||
// 打开西药处方模态框逻辑
|
||||
PrescriptionDetailModalApi.setData({
|
||||
@@ -319,6 +350,7 @@ const openOrderAmountVerify = () => {
|
||||
</AntdModal>
|
||||
<RefundModal />
|
||||
<PrescriptionDetailModal />
|
||||
<TraceDrawer />
|
||||
<AnalysisOverview :items="overviewItems" :my-card="false" />
|
||||
<Grid>
|
||||
<template #toolbar-buttons>
|
||||
@@ -475,6 +507,13 @@ const openOrderAmountVerify = () => {
|
||||
// auth: ['order', 'sys:role:detail'],
|
||||
onClick: infoModal.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: '溯源',
|
||||
type: 'link',
|
||||
icon: 'mdi:timeline-text',
|
||||
size: 'small',
|
||||
onClick: openOrderTrace.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: '处方',
|
||||
type: 'link',
|
||||
@@ -483,19 +522,27 @@ const openOrderAmountVerify = () => {
|
||||
// auth: ['超级订单', 'sys:user:save'],
|
||||
onClick: openPrescriptionDetail.bind(null, row.p_id),
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[
|
||||
{
|
||||
label: '模拟支付',
|
||||
type: 'link',
|
||||
icon: 'mdi:cash-check',
|
||||
auth: ['Super Admin', 'sys:user:save'],
|
||||
ifShow: row.is_pay === 0,
|
||||
onClick: handleSimulatePay.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: '发货',
|
||||
type: 'link',
|
||||
icon: 'ri:send-plane-fill',
|
||||
// auth: ['超级订单', 'sys:user:save'],
|
||||
auth: ['Super Admin','Admin', 'sys:user:save'],
|
||||
onClick: wareSend.bind(null, row),
|
||||
// popConfirm: {
|
||||
// title: '确定发货吗?',
|
||||
// confirm: wareSend.bind(null, row),
|
||||
// },
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[
|
||||
{
|
||||
label: '退款',
|
||||
type: 'link',
|
||||
|
||||
@@ -22,13 +22,13 @@ export const gridOptions: VxeGridProps<RegisterOrderItem> = {
|
||||
{ field: 'is_pay', title: '是否支付', width: 100, slots: { default: 'is_pay' } },
|
||||
{ field: 'status', title: '状态', slots: { default: 'status' } },
|
||||
{ field: 'created_at', title: '创建时间' },
|
||||
// {
|
||||
// type: 'html',
|
||||
// title: '操作',
|
||||
// align: 'right',
|
||||
// slots: { default: 'action' },
|
||||
// width: 200,
|
||||
// },
|
||||
{
|
||||
type: 'html',
|
||||
title: '操作',
|
||||
align: 'right',
|
||||
slots: { default: 'action' },
|
||||
width: 200,
|
||||
},
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
|
||||
@@ -4,15 +4,14 @@ import type { VxeGridListeners } from '#/adapter/vxe-table';
|
||||
import { nextTick, onMounted, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import {
|
||||
Page,
|
||||
useVbenModal,
|
||||
} from '@vben/common-ui';
|
||||
import { Page, useVbenDrawer, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, Tag } from 'ant-design-vue';
|
||||
import { Button, message, Modal as AntdModal, Tag } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
import { simulatePayApi } from '#/views/business/order/api/order-ops';
|
||||
import OrderTraceDrawer from '#/views/business/order/components/order-trace-drawer.vue';
|
||||
import PrescriptionDetail from '#/views/doctor/doctor-reception/components/PrescriptionDetail.vue';
|
||||
|
||||
import { formOptions } from './config/search';
|
||||
@@ -56,13 +55,40 @@ onMounted(() => {
|
||||
const [PrescriptionDetailModal, PrescriptionDetailModalApi] = useVbenModal({
|
||||
connectedComponent: PrescriptionDetail,
|
||||
});
|
||||
const openPrescriptionDetail = (values) => {
|
||||
// 打开西药处方模态框逻辑
|
||||
|
||||
const [TraceDrawer, traceDrawerApi] = useVbenDrawer({
|
||||
connectedComponent: OrderTraceDrawer,
|
||||
});
|
||||
|
||||
function openPrescriptionDetail(id: number) {
|
||||
PrescriptionDetailModalApi.setData({
|
||||
values,
|
||||
values: { id },
|
||||
});
|
||||
PrescriptionDetailModalApi.open();
|
||||
};
|
||||
}
|
||||
|
||||
function openOrderTrace(row: Record<string, any>) {
|
||||
traceDrawerApi.setData({
|
||||
scene: 'register',
|
||||
order_id: row.id,
|
||||
order_no: row.order_no,
|
||||
});
|
||||
traceDrawerApi.open();
|
||||
}
|
||||
|
||||
function handleSimulatePay(row: Record<string, any>) {
|
||||
AntdModal.confirm({
|
||||
title: '模拟支付',
|
||||
content: `确认为挂号订单 ${row.order_no} 模拟易票联支付回调?`,
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
await simulatePayApi({ order_type: 'register', order_id: row.id });
|
||||
message.success('模拟支付成功');
|
||||
await gridApi.query();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function formatRegisterPrice(price: unknown) {
|
||||
const n = Number(price);
|
||||
@@ -76,12 +102,10 @@ function formatRegisterPrice(price: unknown) {
|
||||
<template>
|
||||
<Page auto-content-height title="订单管理">
|
||||
<PrescriptionDetailModal />
|
||||
<TraceDrawer />
|
||||
<Grid>
|
||||
<template #toolbar-buttons>
|
||||
<TableAction
|
||||
:actions="[]"
|
||||
:drop-down-actions="[]"
|
||||
>
|
||||
<TableAction :actions="[]" :drop-down-actions="[]">
|
||||
<template #more>
|
||||
<Button style="margin-left: 16px">
|
||||
批量操作
|
||||
@@ -91,10 +115,11 @@ function formatRegisterPrice(price: unknown) {
|
||||
</TableAction>
|
||||
</template>
|
||||
<template #prescription="{ row }">
|
||||
<div v-for="item in row.prescription">
|
||||
<Button type="link" @click="openPrescriptionDetail(item.id)">{{ item.prescription_no }}</Button>
|
||||
<div v-for="item in row.prescription" :key="item.id">
|
||||
<Button type="link" @click="openPrescriptionDetail(item.id)">
|
||||
{{ item.prescription_no }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<template #price="{ row }">
|
||||
<span>{{ formatRegisterPrice(row.price) }}</span>
|
||||
@@ -114,7 +139,6 @@ function formatRegisterPrice(price: unknown) {
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<div class="mt-3">
|
||||
<!-- 状态:0待支付1已支付待接诊2接诊中3已结束4已取消5待评价6已评价7已拒诊-->
|
||||
<Tag v-if="row.status === 0" color="red">代支付</Tag>
|
||||
<Tag v-else-if="row.status === 1" color="green">待接诊</Tag>
|
||||
<Tag v-else-if="row.status === 2" color="red">接诊中</Tag>
|
||||
@@ -123,7 +147,6 @@ function formatRegisterPrice(price: unknown) {
|
||||
<Tag v-else-if="row.status === 5" color="purple">待评价</Tag>
|
||||
<Tag v-else-if="row.status === 6" color="purple">已评价</Tag>
|
||||
<Tag v-else-if="row.status === 7" color="red">已拒诊</Tag>
|
||||
<!-- <p>{{ row.created_at }}</p>-->
|
||||
</div>
|
||||
</template>
|
||||
<template #toolbar-tools></template>
|
||||
@@ -131,20 +154,28 @@ function formatRegisterPrice(price: unknown) {
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '查看处方',
|
||||
label: '溯源',
|
||||
type: 'link',
|
||||
icon: 'marketeq:eye',
|
||||
// auth: ['超级订单', 'sys:user:save'],
|
||||
onClick: openPrescriptionDetail.bind(null, row.id),
|
||||
icon: 'mdi:timeline-text',
|
||||
onClick: openOrderTrace.bind(null, row),
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[
|
||||
{
|
||||
label: '模拟支付',
|
||||
type: 'link',
|
||||
icon: 'mdi:cash-check',
|
||||
auth: ['Super Admin', 'sys:user:save'],
|
||||
ifShow: row.is_pay === 0,
|
||||
onClick: handleSimulatePay.bind(null, row),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.custom-list {
|
||||
list-style-type: none;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
const prefix = 'account-able-change-log/';
|
||||
|
||||
export async function getAccountAbleChangeLogList(data: any) {
|
||||
return requestClient.get<any>(`${prefix}list`, { params: data });
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenDrawer } from '@vben/common-ui';
|
||||
|
||||
import { Descriptions, Spin, Table } from 'ant-design-vue';
|
||||
|
||||
import { getSettlementLedgerDetail } from '#/views/finance/withdrawal/api/settlement';
|
||||
|
||||
const loading = ref(false);
|
||||
const detail = ref<{
|
||||
items: Record<string, any>[];
|
||||
ledger_log: Record<string, any> | null;
|
||||
order_id: number;
|
||||
order_no: string;
|
||||
order_type: number;
|
||||
sum_money: string;
|
||||
}>({
|
||||
items: [],
|
||||
ledger_log: null,
|
||||
order_id: 0,
|
||||
order_no: '',
|
||||
order_type: 0,
|
||||
sum_money: '0',
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{ title: '费用类型', dataIndex: 'fee_type_txt', key: 'fee_type_txt' },
|
||||
{ title: '分账金额', dataIndex: 'money', key: 'money' },
|
||||
{ title: '结算状态', dataIndex: 'status_txt', key: 'status_txt' },
|
||||
{ title: '创建时间', dataIndex: 'created_at', key: 'created_at' },
|
||||
];
|
||||
|
||||
const [Drawer, drawerApi] = useVbenDrawer({
|
||||
onOpenChange(isOpen) {
|
||||
if (isOpen) {
|
||||
loadDetail();
|
||||
} else {
|
||||
detail.value = {
|
||||
items: [],
|
||||
ledger_log: null,
|
||||
order_id: 0,
|
||||
order_no: '',
|
||||
order_type: 0,
|
||||
sum_money: '0',
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
async function loadDetail() {
|
||||
const data = drawerApi.getData<{
|
||||
ledger_log_id?: number;
|
||||
merged?: boolean;
|
||||
order_id?: number;
|
||||
order_type?: number;
|
||||
}>();
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const params =
|
||||
data.merged && data.order_id && data.order_type
|
||||
? { order_id: data.order_id, order_type: data.order_type }
|
||||
: { ledger_log_id: data.ledger_log_id };
|
||||
const res = await getSettlementLedgerDetail(params);
|
||||
detail.value = res || detail.value;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open(row: Record<string, any>) {
|
||||
drawerApi.setData({
|
||||
ledger_log_id: row.id,
|
||||
merged: row.merged,
|
||||
order_id: row.order_id,
|
||||
order_type: row.order_type,
|
||||
});
|
||||
drawerApi.open();
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Drawer class="w-[720px]" title="结算明细">
|
||||
<Spin :spinning="loading">
|
||||
<Descriptions bordered :column="1" size="small" class="mb-4">
|
||||
<Descriptions.Item label="订单号">
|
||||
{{ detail.order_no || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="合计分账">
|
||||
{{ detail.sum_money }}
|
||||
</Descriptions.Item>
|
||||
<template v-if="detail.ledger_log">
|
||||
<Descriptions.Item label="费用类型">
|
||||
{{ detail.ledger_log.fee_type_txt }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="结算类型">
|
||||
{{ detail.ledger_log.type_txt }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="说明">
|
||||
{{ detail.ledger_log.content || '-' }}
|
||||
</Descriptions.Item>
|
||||
</template>
|
||||
</Descriptions>
|
||||
<Table
|
||||
:columns="columns"
|
||||
:data-source="detail.items"
|
||||
:pagination="false"
|
||||
row-key="id"
|
||||
size="small"
|
||||
/>
|
||||
</Spin>
|
||||
</Drawer>
|
||||
</template>
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
export const accountChangeSearchOptions: VbenFormProps = {
|
||||
collapsed: false,
|
||||
schema: [
|
||||
{
|
||||
component: 'VbenSelect',
|
||||
componentProps: {
|
||||
placeholder: '来源类型',
|
||||
allowClear: true,
|
||||
options: [
|
||||
{ label: '分账', value: 'settle' },
|
||||
{ label: '提现', value: 'withdraw' },
|
||||
{ label: '退款', value: 'refund' },
|
||||
],
|
||||
},
|
||||
defaultValue: '',
|
||||
fieldName: 'source_type',
|
||||
label: '来源类型',
|
||||
},
|
||||
{
|
||||
component: 'VbenInput',
|
||||
componentProps: {
|
||||
placeholder: '输入订单号',
|
||||
},
|
||||
defaultValue: '',
|
||||
fieldName: 'order_no',
|
||||
label: '订单号',
|
||||
},
|
||||
],
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
submitOnChange: true,
|
||||
submitOnEnter: false,
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import { getAccountAbleChangeLogList } from '#/views/finance/withdrawal/api/account-change';
|
||||
|
||||
export const accountChangeGridOptions: VxeGridProps = {
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ field: 'created_at', title: '变动时间', width: 170 },
|
||||
{ field: 'source_type_txt', title: '来源类型', width: 100 },
|
||||
{ field: 'field_name_txt', title: '变更字段', width: 120 },
|
||||
{ field: 'before_amount', title: '变动前', width: 110 },
|
||||
{ field: 'after_amount', title: '变动后', width: 110 },
|
||||
{ field: 'change_amount', title: '变动值', width: 110 },
|
||||
{ field: 'order_no', title: '订单号', minWidth: 160 },
|
||||
{ field: 'source_table', title: '来源表', width: 140 },
|
||||
{ field: 'remark', title: '备注', minWidth: 180 },
|
||||
{ type: 'html', title: '操作', slots: { default: 'action' }, width: 100 },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getAccountAbleChangeLogList({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
search: true,
|
||||
refresh: true,
|
||||
print: false,
|
||||
export: false,
|
||||
zoom: true,
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
},
|
||||
},
|
||||
showOverflow: false,
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
export const formOptions3: VbenFormProps = {
|
||||
collapsed: false,
|
||||
schema: [
|
||||
{
|
||||
component: 'Switch',
|
||||
componentProps: {
|
||||
class: 'w-auto',
|
||||
},
|
||||
defaultValue: true,
|
||||
fieldName: 'merge_by_order',
|
||||
label: '同订单合并',
|
||||
},
|
||||
{
|
||||
component: 'VbenInput',
|
||||
componentProps: {
|
||||
placeholder: '输入订单号',
|
||||
},
|
||||
defaultValue: '',
|
||||
fieldName: 'order_no',
|
||||
label: '订单号',
|
||||
},
|
||||
{
|
||||
component: 'VbenSelect',
|
||||
componentProps: {
|
||||
placeholder: '选择费用类型',
|
||||
options: [
|
||||
{ label: '药品费用', value: 1 },
|
||||
{ label: '挂号费用', value: 2 },
|
||||
{ label: '快递费用', value: 3 },
|
||||
{ label: '代煎费用', value: 4 },
|
||||
{ label: '加工费用', value: 5 },
|
||||
{ label: '诊疗费用', value: 6 },
|
||||
],
|
||||
},
|
||||
defaultValue: '',
|
||||
fieldName: 'fee_type',
|
||||
label: '费用类型',
|
||||
},
|
||||
],
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
submitOnChange: true,
|
||||
submitOnEnter: false,
|
||||
};
|
||||
@@ -77,6 +77,13 @@ export const gridOptions2: VxeGridProps<RowType> = {
|
||||
showOverflow: false,
|
||||
};
|
||||
|
||||
function normalizeMergeByOrder(value: unknown): 0 | 1 {
|
||||
if (value === false || value === 0 || value === '0') {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
export const gridOptions3: VxeGridProps<RowType> = {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
@@ -86,29 +93,31 @@ export const gridOptions3: VxeGridProps<RowType> = {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
// { type: 'checkbox', width: 60 },
|
||||
// { field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
{ field: 'order_no', title: '订单号' },
|
||||
{ field: 'order_type_txt', title: '订单类型' },
|
||||
{ field: 'fee_type_txt', title: '费用类型' },
|
||||
{ field: 'amount', title: '金额' },
|
||||
{ field: 'type_txt', title: '类型' },
|
||||
{ field: 'content', title: '说明' },
|
||||
{ field: 'created_at', title: '创建时间' },
|
||||
{ field: 'created_at', title: '结算时间' },
|
||||
{ type: 'html', title: '操作', slots: { default: 'action' } },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
// 请求后端接口方法
|
||||
query: async ({ page }, formValues) => {
|
||||
const { merge_by_order, ...rest } = formValues || {};
|
||||
return await getSettlementLedgerLogList({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
merge_by_order: normalizeMergeByOrder(merge_by_order),
|
||||
exclude_zero_amount: 1,
|
||||
...rest,
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { AnalysisChartsTabs, Page } from '@vben/common-ui';
|
||||
import { AnalysisChartsTabs, Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { FloatButton, FloatButtonGroup, Popover, Tag } from 'ant-design-vue';
|
||||
import { FloatButton, FloatButtonGroup, message, Popover, Tag } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
import DetailModal from '#/views/business/order/product-order/components/detail.vue';
|
||||
|
||||
import SettlementDetailDrawer from './components/settlement-detail-drawer.vue';
|
||||
import Statistics from './components/statistics.vue';
|
||||
import { accountChangeSearchOptions } from './config/account-change-search';
|
||||
import { accountChangeGridOptions } from './config/account-change-table';
|
||||
import { formOptions } from './config/search';
|
||||
import { formOptions3 } from './config/settlement-log-search';
|
||||
import { formOptions2 } from './config/settlement-search';
|
||||
import { gridOptions2, gridOptions3 } from './config/settlement-table';
|
||||
import { gridOptions } from './config/table';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const [Grid, GridApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
@@ -24,24 +32,51 @@ const [Grid2] = useVbenVxeGrid({
|
||||
gridOptions: gridOptions2,
|
||||
});
|
||||
|
||||
const settlementDetailDrawerRef =
|
||||
ref<InstanceType<typeof SettlementDetailDrawer>>();
|
||||
|
||||
const [Grid3] = useVbenVxeGrid({
|
||||
formOptions: formOptions2,
|
||||
formOptions: formOptions3,
|
||||
gridOptions: gridOptions3,
|
||||
});
|
||||
|
||||
const [Grid4] = useVbenVxeGrid({
|
||||
formOptions: accountChangeSearchOptions,
|
||||
gridOptions: accountChangeGridOptions,
|
||||
});
|
||||
|
||||
const [OrderDetailModal, OrderDetailModalApi] = useVbenModal({
|
||||
connectedComponent: DetailModal,
|
||||
});
|
||||
|
||||
function showSettlementDetail(row: Record<string, any>) {
|
||||
settlementDetailDrawerRef.value?.open(row);
|
||||
}
|
||||
|
||||
function openOrderDetail(row: Record<string, any>) {
|
||||
const orderType = Number(row.order_type || 0);
|
||||
const orderId = Number(row.order_id || 0);
|
||||
const orderNo = String(row.order_no || '');
|
||||
|
||||
if (orderType === 1 && orderId > 0) {
|
||||
OrderDetailModalApi.setData({ id: orderId });
|
||||
OrderDetailModalApi.open();
|
||||
return;
|
||||
}
|
||||
|
||||
if (orderType === 2 && orderNo) {
|
||||
router.push({ path: '/business/register/list', query: { order_no: orderNo } });
|
||||
return;
|
||||
}
|
||||
|
||||
message.info('暂不支持查看该类型订单详情');
|
||||
}
|
||||
|
||||
const chartTabs = [
|
||||
{
|
||||
label: '提现记录',
|
||||
value: 'trends',
|
||||
},
|
||||
{
|
||||
label: '结算记录',
|
||||
value: 'visits',
|
||||
},
|
||||
{
|
||||
label: '结算明细',
|
||||
value: 'visitsItems',
|
||||
},
|
||||
{ label: '提现记录', value: 'trends' },
|
||||
{ label: '结算记录', value: 'visits' },
|
||||
{ label: '结算明细', value: 'visitsItems' },
|
||||
{ label: '资金变动记录', value: 'accountChange' },
|
||||
];
|
||||
|
||||
const isShow = ref(true);
|
||||
@@ -57,63 +92,73 @@ function updateShowStatus() {
|
||||
description="提现前请确认好您的打款账户"
|
||||
title="提现管理"
|
||||
>
|
||||
<OrderDetailModal />
|
||||
<Statistics v-show="isShow" :grid-api="GridApi" />
|
||||
<AnalysisChartsTabs :tabs="chartTabs" class="mt-5">
|
||||
<template #trends>
|
||||
<!-- 提现记录 --->
|
||||
<div style="min-height: 500px;">
|
||||
<Grid>
|
||||
<template #toolbar-buttons></template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #user_id="{ row }">
|
||||
<span v-if="row.user_type === 1">{{ row.store.name }}</span>
|
||||
<span v-else-if="row.user_type === 2">萧康平台</span>
|
||||
<span v-else-if="row.user_type === 3">{{ row.supplier.name }}</span>
|
||||
</template>
|
||||
<template #check_status="{ row }">
|
||||
<Tag v-if="row.check_status === 1" color="blue">待审核</Tag>
|
||||
<Tag v-else-if="row.check_status === 2" color="green">审核成功</Tag>
|
||||
<Tag v-else-if="row.check_status === 3" color="red">拒绝</Tag>
|
||||
</template>
|
||||
<template #check_result="{ row }">
|
||||
<Tag v-if="row.check_status === 2" color="green">
|
||||
{{ row.check_result }}
|
||||
</Tag>
|
||||
<Tag v-else-if="row.check_status === 3" color="red">
|
||||
{{ row.check_result }}
|
||||
</Tag>
|
||||
</template>
|
||||
<template #dakuan_status="{ row }">
|
||||
<span v-if="row.dakuan_status === -1">打款失败</span>
|
||||
<span v-else-if="row.dakuan_status === 0">处理中</span>
|
||||
<span v-else-if="row.dakuan_status === 1">打款成功</span>
|
||||
</template>
|
||||
<template #check_id="{ row }">
|
||||
<span>{{
|
||||
row.check_admin?.username || row.check_admin?.nick_name || '暂无'
|
||||
}}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
// {
|
||||
// label: '编辑',
|
||||
// type: 'link',
|
||||
// icon: 'uil:edit',
|
||||
// size: 'small',
|
||||
// onClick: showModal.bind(null, row, true),
|
||||
// },
|
||||
]"
|
||||
:drop-down-actions="[]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
<template #toolbar-buttons></template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #user_id="{ row }">
|
||||
<span v-if="row.user_type === 1">{{ row.store.name }}</span>
|
||||
<span v-else-if="row.user_type === 2">萧康平台</span>
|
||||
<span v-else-if="row.user_type === 3">{{ row.supplier.name }}</span>
|
||||
</template>
|
||||
<template #check_status="{ row }">
|
||||
<Tag v-if="row.check_status === 1" color="blue">待审核</Tag>
|
||||
<Tag v-else-if="row.check_status === 2" color="green">审核成功</Tag>
|
||||
<Tag v-else-if="row.check_status === 3" color="red">拒绝</Tag>
|
||||
</template>
|
||||
<template #check_result="{ row }">
|
||||
<Tag v-if="row.check_status === 2" color="green">{{ row.check_result }}</Tag>
|
||||
<Tag v-else-if="row.check_status === 3" color="red">{{ row.check_result }}</Tag>
|
||||
</template>
|
||||
<template #dakuan_status="{ row }">
|
||||
<span v-if="row.dakuan_status === -1">打款失败</span>
|
||||
<span v-else-if="row.dakuan_status === 0">处理中</span>
|
||||
<span v-else-if="row.dakuan_status === 1">打款成功</span>
|
||||
</template>
|
||||
<template #check_id="{ row }">
|
||||
<span>{{ row.check_admin?.username || row.check_admin?.nick_name || '暂无' }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<TableAction :actions="[]" :drop-down-actions="[]" />
|
||||
</template>
|
||||
</Grid>
|
||||
</div>
|
||||
</template>
|
||||
<template #visits>
|
||||
<!-- 结算记录 --->
|
||||
<div style="min-height: 500px;">
|
||||
<Grid3>
|
||||
<template #toolbar-buttons></template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #action="{ row }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '查看明细',
|
||||
type: 'link',
|
||||
size: 'small',
|
||||
onClick: showSettlementDetail.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: '订单详情',
|
||||
type: 'link',
|
||||
size: 'small',
|
||||
onClick: openOrderDetail.bind(null, row),
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[]"
|
||||
/>
|
||||
</template>
|
||||
</Grid3>
|
||||
<SettlementDetailDrawer ref="settlementDetailDrawerRef" />
|
||||
</div>
|
||||
</template>
|
||||
<template #visitsItems>
|
||||
<div style="min-height: 500px;">
|
||||
<Grid2>
|
||||
<template #toolbar-buttons></template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #user_id="{ row }">
|
||||
@@ -121,57 +166,36 @@ function updateShowStatus() {
|
||||
<span v-else-if="row.user_type === 2">萧康平台</span>
|
||||
<span v-else-if="row.user_type === 3">{{ row.supplier.name }}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<TableAction :actions="[]" :drop-down-actions="[]" />
|
||||
</template>
|
||||
</Grid2>
|
||||
</div>
|
||||
</template>
|
||||
<template #accountChange>
|
||||
<div style="min-height: 500px;">
|
||||
<Grid4>
|
||||
<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),
|
||||
// },
|
||||
]"
|
||||
{
|
||||
label: '订单详情',
|
||||
type: 'link',
|
||||
size: 'small',
|
||||
ifShow: !!row.order_id,
|
||||
onClick: openOrderDetail.bind(null, row),
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[]"
|
||||
/>
|
||||
</template>
|
||||
</Grid3>
|
||||
</div>
|
||||
</template>
|
||||
<template #visitsItems>
|
||||
<!-- 结算明细 --->
|
||||
<div style="min-height: 500px;">
|
||||
<Grid2>
|
||||
<template #toolbar-buttons></template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #user_id="{ row }">
|
||||
<span v-if="row.user_type === 1">{{ row.store.name }}</span>
|
||||
<span v-else-if="row.user_type === 2">萧康平台</span>
|
||||
<span v-else-if="row.user_type === 3">{{ row.supplier.name }}</span>
|
||||
</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>
|
||||
</Grid2>
|
||||
</Grid4>
|
||||
</div>
|
||||
</template>
|
||||
</AnalysisChartsTabs>
|
||||
<!-- 浮动按钮组 -->
|
||||
<FloatButtonGroup :style="{ right: '24px', bottom: '84px' }" shape="circle">
|
||||
<!-- 打开显示余额卡片的按钮 -->
|
||||
<Popover placement="left" title="显示余额卡片">
|
||||
<template #content>
|
||||
<p>点击展开卡片信息,再次点击收起</p>
|
||||
|
||||
Reference in New Issue
Block a user