-
-
-
-
-
-
-
-
+
+
+
+ 按单可提比账户余额多 ¥{{ Number(customGap).toFixed(2) }}。点击打款单号可看申请→审核→打款进度。
+
+
+
+
+ 历史自定义提现未按单核销
+
+
+
+
+ 可提现余额 ¥{{ Number(maxAmount || 0).toFixed(2) }}
+
+
+ {{ opt.label }}
+
+
+
+
+ 提现金额
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 已选 {{ selectedRows.length }} 笔,合计 ¥{{ selectedSum.toFixed(2) }}
+
+
+
+
+
+
+
+
+
+ 共 {{ orderItems.length }} 笔,合计 ¥{{ rangeSum.toFixed(2) }}
+
+
+
+
+
diff --git a/apps/web-antd/src/views/finance/withdrawal/components/withdrawal-relation-detail.vue b/apps/web-antd/src/views/finance/withdrawal/components/withdrawal-relation-detail.vue
index f17b4f1c..24dd408a 100644
--- a/apps/web-antd/src/views/finance/withdrawal/components/withdrawal-relation-detail.vue
+++ b/apps/web-antd/src/views/finance/withdrawal/components/withdrawal-relation-detail.vue
@@ -7,7 +7,7 @@ import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
-import { Spin, Tag } from 'ant-design-vue';
+import { Button, Spin, Steps, Table, Tag } from 'ant-design-vue';
import { getChargeCashPayRecordDetailApi } from '#/views/finance/charge-cash-pay-record/api';
import { getWithdrawalApplicationInfo } from '#/views/finance/withdrawal/api';
@@ -19,15 +19,22 @@ defineOptions({
const loading = ref(false);
/** 归一化后的展示数据 */
const view = ref
| null>(null);
+/** 页面级打开订单详情(不嵌套在本弹窗内,避免层级被挡住) */
+const openOrderDetail = ref<(row: Record) => void>();
const [Modal, modalApi] = useVbenModal({
footer: false,
draggable: true,
+ // 点打款单号打开:盖住申请(1000)和差额确认(2000)
+ zIndex: 3000,
onOpenChange(isOpen) {
if (isOpen) {
+ const data = modalApi.getData>() || {};
+ openOrderDetail.value = data.openOrderDetail;
loadDetail();
} else {
view.value = null;
+ openOrderDetail.value = undefined;
}
},
});
@@ -45,6 +52,94 @@ const checkStatusColor = computed(() => {
return 'warning';
});
+/**
+ * 申请 → 审核 → 打款 → 到账
+ * 按 check_status / dakuan_status 标 finish/process/error/wait,点打款单号就能看见走到哪一步
+ */
+const progressItems = computed(() => {
+ const v = view.value;
+ if (!v) return [];
+ return buildWithdrawProgress(v);
+});
+
+const relatedOrders = computed(() => {
+ const list = view.value?.orders;
+ return Array.isArray(list) ? list : [];
+});
+
+const relatedOrderColumns = [
+ { title: '订单号', dataIndex: 'order_no', key: 'order_no', ellipsis: true },
+ { title: '类型', dataIndex: 'order_type_txt', key: 'order_type_txt', width: 90 },
+ { title: '分类', dataIndex: 'category_txt', key: 'category_txt', width: 70 },
+ { title: '金额', dataIndex: 'amount', key: 'amount', width: 110, align: 'right' as const },
+ { title: '操作', key: 'action', width: 80 },
+];
+
+function buildWithdrawProgress(row: Record) {
+ const check = Number(row.check_status ?? 0);
+ const dakuan = Number(row.dakuan_status ?? 0);
+ const apply = {
+ title: '申请',
+ description: row.apply_time || '已提交',
+ status: 'finish' as const,
+ };
+ let audit: Record;
+ if (check === 1) {
+ audit = { title: '审核', description: '待审核', status: 'process' };
+ } else if (check === 2) {
+ audit = {
+ title: '审核',
+ description: row.check_time || row.check_status_txt || '审核通过',
+ status: 'finish',
+ };
+ } else if (check === 3) {
+ audit = {
+ title: '审核',
+ description: row.check_result || '已拒绝',
+ status: 'error',
+ };
+ } else if (check === 4) {
+ audit = {
+ title: '审核',
+ description: row.check_result || '提现失败',
+ status: 'error',
+ };
+ } else {
+ audit = { title: '审核', description: '待审核', status: 'wait' };
+ }
+ let pay: Record;
+ let arrived: Record;
+ if (check === 1) {
+ pay = { title: '打款', description: '待审核通过后打款', status: 'wait' };
+ arrived = { title: '到账', description: '尚未打款', status: 'wait' };
+ } else if (check === 3) {
+ pay = { title: '打款', description: '已拒绝,未打款', status: 'wait' };
+ arrived = { title: '到账', description: '未到账', status: 'wait' };
+ } else if (check === 4 || dakuan === -1) {
+ pay = {
+ title: '打款',
+ description: row.dakuan_time || row.dakuan_status_txt || '打款失败',
+ status: 'error',
+ };
+ arrived = { title: '到账', description: '未到账', status: 'error' };
+ } else if (dakuan === 1) {
+ pay = {
+ title: '打款',
+ description: row.dakuan_time || '打款成功',
+ status: 'finish',
+ };
+ arrived = {
+ title: '到账',
+ description: row.dakuan_time || '已到账',
+ status: 'finish',
+ };
+ } else {
+ pay = { title: '打款', description: '打款处理中', status: 'process' };
+ arrived = { title: '到账', description: '等待到账', status: 'wait' };
+ }
+ return [apply, audit, pay, arrived];
+}
+
/**
* 按入口源加载:apply 走提现详情,charge 走代付详情再归一化
*/
@@ -97,6 +192,10 @@ function normalizeFromApply(res: Record) {
charge_status: res?.charge_status ?? charge?.status,
charge_status_txt: res?.charge_status_txt,
charge_created_at: charge?.created_at,
+ orders: res?.orders || [],
+ tcm_amount: res?.tcm_amount,
+ western_amount: res?.western_amount,
+ other_amount: res?.other_amount,
};
}
@@ -123,6 +222,10 @@ function normalizeFromCharge(res: Record) {
charge_status: res?.status,
charge_status_txt: res?.status_txt,
charge_created_at: res?.created_at,
+ orders: res?.orders || apply?.orders || [],
+ tcm_amount: apply?.tcm_amount,
+ western_amount: apply?.western_amount,
+ other_amount: apply?.other_amount,
};
}
@@ -131,6 +234,14 @@ function formatMoney(val: any) {
return Number.isFinite(n) ? n.toFixed(2) : '0.00';
}
+/** 点订单号/详情:交给页面级弹窗,本弹窗不嵌套订单详情 */
+function onViewOrder(record: Record) {
+ if (typeof openOrderDetail.value === 'function') {
+ openOrderDetail.value(record);
+ return;
+ }
+}
+
function chargeTagColor(status: number) {
if (status === 2) return 'success';
if (status === 3) return 'default';
@@ -139,7 +250,7 @@ function chargeTagColor(status: number) {
-
+
@@ -155,6 +266,11 @@ function chargeTagColor(status: number) {
+
+
提现申请
@@ -176,6 +292,12 @@ function chargeTagColor(status: number) {
打款金额
¥{{ formatMoney(view.true_cash) }}
+
+ 中/西/其他
+
+ ¥{{ formatMoney(view.tcm_amount) }} / ¥{{ formatMoney(view.western_amount) }} / ¥{{ formatMoney(view.other_amount) }}
+
+
申请时间
{{ view.apply_time || '-' }}
@@ -200,6 +322,48 @@ function chargeTagColor(status: number) {
+
+
+ 关联订单
+
+
+
+
+
+
+
+
+ ¥{{ formatMoney(record.amount) }}
+
+
+
+
+
+
+
+ 该记录没有关联业务订单(自定义金额提现不会核销具体订单)
+
+
+
代付手续费
@@ -280,12 +444,20 @@ function chargeTagColor(status: number) {
}
.section-title {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
margin-bottom: 10px;
font-size: 13px;
font-weight: 600;
color: hsl(var(--foreground));
}
+.section-extra {
+ font-weight: 400;
+ color: hsl(var(--muted-foreground));
+}
+
.rows {
display: flex;
flex-direction: column;
@@ -329,4 +501,16 @@ function chargeTagColor(status: number) {
text-align: center;
color: hsl(var(--muted-foreground));
}
+
+.empty-orders {
+ padding: 12px 0;
+ font-size: 13px;
+ text-align: center;
+ color: hsl(var(--muted-foreground));
+}
+
+.order-no-btn {
+ padding-inline: 0;
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
+}
diff --git a/apps/web-antd/src/views/finance/withdrawal/config/table.ts b/apps/web-antd/src/views/finance/withdrawal/config/table.ts
index 3e88e273..b91e44da 100644
--- a/apps/web-antd/src/views/finance/withdrawal/config/table.ts
+++ b/apps/web-antd/src/views/finance/withdrawal/config/table.ts
@@ -30,8 +30,18 @@ export const gridOptions: VxeGridProps
= {
columns: [
{ type: 'checkbox', width: 60 },
{ field: 'id', align: 'left', title: 'ID', width: 100 },
+ {
+ field: 'order_no',
+ title: '打款订单',
+ width: 220,
+ slots: { default: 'order_no' },
+ },
{ field: 'user_id', title: '名称', slots: { default: 'user_id' } },
- { field: 'apply_cash', title: '申请金额' },
+ { field: 'apply_cash', title: '申请金额', width: 110 },
+ { field: 'mode_txt', title: '提现方式', width: 100 },
+ { field: 'tcm_amount', title: '中药', width: 90 },
+ { field: 'western_amount', title: '西药', width: 90 },
+ { field: 'other_amount', title: '其他', width: 90 },
{ field: 'true_cash', title: '实际到账金额' },
{ field: 'display_charge_cash', title: '手续费' },
{ field: 'apply_time', title: '申请时间' },
diff --git a/apps/web-antd/src/views/finance/withdrawal/index.vue b/apps/web-antd/src/views/finance/withdrawal/index.vue
index 01690c1f..7c622f4d 100644
--- a/apps/web-antd/src/views/finance/withdrawal/index.vue
+++ b/apps/web-antd/src/views/finance/withdrawal/index.vue
@@ -5,19 +5,20 @@
* 状态列统一用主题化状态胶囊(StatusPill 样式),金额涨跌用 --success/--destructive 变量,全量适配暗色
*/
import { ref } from 'vue';
-import { useRouter } from 'vue-router';
import { AnalysisChartsTabs, Page, useVbenModal } from '@vben/common-ui';
import { IconifyIcon as VbenIcon } from '@vben/icons';
-import { FloatButton, FloatButtonGroup, message, Popover } from 'ant-design-vue';
+import { Button, FloatButton, FloatButtonGroup, Popover } 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 ApplyOrdersModal from './components/apply-orders-modal.vue';
import SettlementDetailDrawer from './components/settlement-detail-drawer.vue';
import Statistics from './components/statistics.vue';
+import WithdrawableOrderDrawer from './components/withdrawable-order-drawer.vue';
+import WithdrawalRelationDetail from './components/withdrawal-relation-detail.vue';
import { getAccountChangeSearchOptions } from './config/account-change-search';
import { accountChangeGridOptions } from './config/account-change-table';
import { formOptions } from './config/search';
@@ -25,8 +26,10 @@ 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';
+import { useWithdrawOrderDetailModals } from './utils/use-withdraw-order-detail';
-const router = useRouter();
+const { OrderDetailModal, RegisterDetailModal, openOrderDetail } =
+ useWithdrawOrderDetailModals();
const [Grid, GridApi] = useVbenVxeGrid({
formOptions,
@@ -40,6 +43,10 @@ const [Grid2] = useVbenVxeGrid({
const settlementDetailDrawerRef =
ref>();
+const withdrawableOrderDrawerRef =
+ ref>();
+const applyOrdersModalRef = ref>();
+const activeTab = ref('trends');
const [Grid3] = useVbenVxeGrid({
formOptions: formOptions3,
@@ -52,34 +59,68 @@ const [Grid4] = useVbenVxeGrid({
gridOptions: accountChangeGridOptions,
});
-const [OrderDetailModal, OrderDetailModalApi] = useVbenModal({
- connectedComponent: DetailModal,
+const [RelationDetailModal, relationDetailApi] = useVbenModal({
+ connectedComponent: WithdrawalRelationDetail,
});
+/** 点打款单号看申请→审核→打款→到账 */
+function openApplyProgress(id: number) {
+ if (!id) return;
+ relationDetailApi.setData({ id, source: 'apply', openOrderDetail });
+ relationDetailApi.open();
+}
+
function showSettlementDetail(row: Record) {
settlementDetailDrawerRef.value?.open(row);
}
-function openOrderDetail(row: Record) {
- 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();
+function showApplyOrders(row: Record) {
+ const orders = row.orders || [];
+ if (orders.length === 1) {
+ openOrderDetail(orders[0]);
return;
}
+ applyOrdersModalRef.value?.open({
+ title: row.order_no ? `关联订单 · ${row.order_no}` : '关联订单',
+ orders,
+ });
+}
- if (orderType === 2 && orderNo) {
- router.push({
- path: '/business/register/list',
- query: { order_no: orderNo },
- });
+function onAccountChangeOrder(row: Record) {
+ const orders = row.orders || [];
+ if (orders.length > 1 || (row.source_type === 'withdraw' && orders.length > 0)) {
+ showApplyOrders(row);
return;
}
+ openOrderDetail(row);
+}
- message.info('暂不支持查看该类型订单详情');
+function onStatClick(key: string) {
+ const categoryMap: Record = {
+ balance: { category: 0, title: '全部可提订单' },
+ tcm: { category: 1, title: '中药可提订单' },
+ western: { category: 2, title: '西药可提订单' },
+ other: { category: 3, title: '其他可提订单' },
+ };
+ if (categoryMap[key]) {
+ withdrawableOrderDrawerRef.value?.open(categoryMap[key]);
+ return;
+ }
+ if (key === 'reviewing') {
+ activeTab.value = 'trends';
+ GridApi.formApi?.setValues({ check_status: 1 });
+ GridApi.query?.();
+ return;
+ }
+ if (key === 'withdrawn') {
+ activeTab.value = 'trends';
+ GridApi.formApi?.setValues({ check_status: 2 });
+ GridApi.query?.();
+ return;
+ }
+ if (key === 'pending') {
+ activeTab.value = 'visits';
+ }
}
const chartTabs = [
@@ -120,15 +161,39 @@ function dakuanStatusPill(status: number) {
title="提现管理"
>
-
+
+
+
+
+
-
+
+
+
+ -
+
{{ row.store?.name }}
萧康平台
@@ -177,7 +242,23 @@ function dakuanStatusPill(status: number) {
}}
-
+
@@ -261,8 +342,8 @@ function dakuanStatusPill(status: number) {
label: '订单详情',
type: 'link',
size: 'small',
- ifShow: !!row.order_id,
- onClick: openOrderDetail.bind(null, row),
+ ifShow: !!row.order_id || (row.orders && row.orders.length > 0),
+ onClick: onAccountChangeOrder.bind(null, row),
},
]"
:drop-down-actions="[]"
@@ -391,4 +472,9 @@ function dakuanStatusPill(status: number) {
.cell-muted {
color: hsl(var(--muted-foreground));
}
+
+.order-no-btn {
+ padding-inline: 0;
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
+}
diff --git a/apps/web-antd/src/views/finance/withdrawal/utils/use-withdraw-order-detail.ts b/apps/web-antd/src/views/finance/withdrawal/utils/use-withdraw-order-detail.ts
new file mode 100644
index 00000000..a3e4ca42
--- /dev/null
+++ b/apps/web-antd/src/views/finance/withdrawal/utils/use-withdraw-order-detail.ts
@@ -0,0 +1,89 @@
+/**
+ * 提现相关页共用:页面级挂载商品订单 / 挂号订单详情弹窗
+ * 二级弹窗不嵌套在提现详情里,zIndex 4000 盖住提现进度(3000)
+ */
+import { useVbenModal } from '@vben/common-ui';
+
+import { message } from 'ant-design-vue';
+
+import { getRegisterListApi } from '#/views/business/order/register/api';
+import RegisterOrderDetail from '#/views/business/order/register/components/RegisterDetailModal.vue';
+import ProductOrderDetail from '#/views/business/order/product-order/components/detail.vue';
+
+/** 叠在提现详情(3000)之上,避免详情被挡住 */
+const ORDER_DETAIL_Z_INDEX = 4000;
+
+/**
+ * 按关联订单行打开业务订单详情
+ * 商品单(order_type=1)走订单详情接口;挂号单(order_type=2)没有独立详情接口,按订单号拉列表行再开挂号弹窗
+ */
+export async function openWithdrawRelatedOrderDetail(
+ row: Record,
+ apis: {
+ productModalApi: { setData: (data: Record) => void; open: () => void };
+ registerModalApi: { setData: (data: Record) => void; open: () => void };
+ },
+) {
+ 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) {
+ apis.productModalApi.setData({ id: orderId });
+ apis.productModalApi.open();
+ return;
+ }
+
+ if (orderType === 2 && (orderId > 0 || orderNo)) {
+ const hide = message.loading('加载挂号详情...', 0);
+ try {
+ const res = await getRegisterListApi({
+ ...(orderId > 0 ? { id: orderId } : {}),
+ ...(orderNo ? { order_no: orderNo } : {}),
+ page: 1,
+ pageSize: 20,
+ });
+ const items = res?.items || [];
+ const item =
+ items.find((it) => Number(it.id) === orderId) ||
+ items.find((it) => String(it.order_no) === orderNo) ||
+ items[0];
+ if (!item) {
+ message.info('未找到挂号订单');
+ return;
+ }
+ apis.registerModalApi.setData({ row: item });
+ apis.registerModalApi.open();
+ } finally {
+ hide();
+ }
+ return;
+ }
+
+ message.info('暂不支持查看该类型订单详情');
+}
+
+/** 提现 / 审核 / 代付页挂载订单详情弹窗,并把打开方法传给提现详情 */
+export function useWithdrawOrderDetailModals() {
+ const [OrderDetailModal, productModalApi] = useVbenModal({
+ connectedComponent: ProductOrderDetail,
+ zIndex: ORDER_DETAIL_Z_INDEX,
+ });
+ const [RegisterDetailModal, registerModalApi] = useVbenModal({
+ connectedComponent: RegisterOrderDetail,
+ zIndex: ORDER_DETAIL_Z_INDEX,
+ });
+
+ async function openOrderDetail(row: Record) {
+ await openWithdrawRelatedOrderDetail(row, {
+ productModalApi,
+ registerModalApi,
+ });
+ }
+
+ return {
+ OrderDetailModal,
+ RegisterDetailModal,
+ openOrderDetail,
+ };
+}
diff --git a/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-charts-tabs.vue b/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-charts-tabs.vue
index ab264a1e..23718690 100644
--- a/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-charts-tabs.vue
+++ b/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-charts-tabs.vue
@@ -17,14 +17,19 @@ const props = withDefaults(defineProps(), {
tabs: () => [],
});
-const defaultValue = computed(() => {
- return props.tabs?.[0]?.value;
+/** 受控当前 Tab,未传时回落到第一项 */
+const activeTab = defineModel('value');
+const currentTab = computed({
+ get: () => activeTab.value || props.tabs?.[0]?.value || '',
+ set: (val: string) => {
+ activeTab.value = val;
+ },
});
-
+
{{ tab.label }}