feat: 提现支持按单申请并查看订单详情
账户岛可下钻可提订单,申请支持勾选/时间范围/自定义金额及差额确认;提现详情可打开商品单和挂号单弹窗。
This commit is contained in:
@@ -10,15 +10,22 @@ import { IconifyIcon as VbenIcon } from '@vben/icons';
|
||||
|
||||
import type { StatIslandItem } from './typing';
|
||||
|
||||
withDefaults(
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
items: StatIslandItem[];
|
||||
/** 每行列数(桌面端),默认 4;窄屏自动降为 2 列 */
|
||||
columns?: number;
|
||||
/** 点击统计岛时调用(只处理 clickable 或全部可点项) */
|
||||
onItemClick?: (item: StatIslandItem) => void;
|
||||
}>(),
|
||||
{ columns: 4 },
|
||||
);
|
||||
|
||||
function handleItemClick(item: StatIslandItem) {
|
||||
if (!item.clickable || !props.onItemClick) return;
|
||||
props.onItemClick(item);
|
||||
}
|
||||
|
||||
/** 金额展示为两位小数;非数字原样透出(如「—」占位) */
|
||||
function formatValue(val: number | string) {
|
||||
const n = Number(val);
|
||||
@@ -32,8 +39,12 @@ function formatValue(val: number | string) {
|
||||
v-for="item in items"
|
||||
:key="item.key"
|
||||
class="stat-island"
|
||||
:class="{ 'stat-island--emphasize': item.emphasize }"
|
||||
:class="{
|
||||
'stat-island--emphasize': item.emphasize,
|
||||
'stat-island--clickable': item.clickable,
|
||||
}"
|
||||
:aria-label="item.label"
|
||||
@click="handleItemClick(item)"
|
||||
>
|
||||
<div class="stat-island-glow" />
|
||||
<VbenIcon class="stat-icon" :icon="item.icon" />
|
||||
@@ -79,6 +90,10 @@ function formatValue(val: number | string) {
|
||||
border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.stat-island--clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.stat-island:hover {
|
||||
border-color: hsl(var(--primary) / 40%);
|
||||
box-shadow:
|
||||
|
||||
@@ -16,4 +16,6 @@ export interface StatIslandItem {
|
||||
emphasize?: boolean;
|
||||
/** 值前缀,默认 ¥;传空串则不显示前缀 */
|
||||
prefix?: string;
|
||||
/** 是否可点击下钻;为 true 时用 pointer 并触发 onItemClick */
|
||||
clickable?: boolean;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { TableAction } from '#/components/table-action';
|
||||
import { getChargeCashPayRecordListApi } from '#/views/finance/charge-cash-pay-record/api';
|
||||
import StatisticsReconciliation from '#/views/finance/reconciliation/components/statistics.vue';
|
||||
import WithdrawalRelationDetail from '#/views/finance/withdrawal/components/withdrawal-relation-detail.vue';
|
||||
import { useWithdrawOrderDetailModals } from '#/views/finance/withdrawal/utils/use-withdraw-order-detail';
|
||||
|
||||
import { formOptions } from './config/search';
|
||||
import { gridOptions } from './config/table';
|
||||
@@ -24,6 +25,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
|
||||
const statistics = ref();
|
||||
|
||||
const { OrderDetailModal, RegisterDetailModal, openOrderDetail } =
|
||||
useWithdrawOrderDetailModals();
|
||||
|
||||
const [RelationDetailModal, relationDetailApi] = useVbenModal({
|
||||
connectedComponent: WithdrawalRelationDetail,
|
||||
});
|
||||
@@ -59,13 +63,15 @@ initTableAjax();
|
||||
|
||||
/** 打开提现+代付互通详情 */
|
||||
const openDetail = (id: number) => {
|
||||
relationDetailApi.setData({ id, source: 'charge' });
|
||||
relationDetailApi.setData({ id, source: 'charge', openOrderDetail });
|
||||
relationDetailApi.open();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="代支付手续费记录">
|
||||
<OrderDetailModal />
|
||||
<RegisterDetailModal />
|
||||
<RelationDetailModal />
|
||||
<StatisticsReconciliation
|
||||
v-if="statistics"
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message, Spin, Tag } from 'ant-design-vue';
|
||||
import { Button, message, Spin, Tag } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {
|
||||
@@ -23,6 +23,8 @@ const gridApi = ref();
|
||||
const type = ref(0);
|
||||
const loading = ref(false);
|
||||
const preview = ref<Record<string, any> | null>(null);
|
||||
/** 页面级打开关联订单详情,审核弹窗不嵌套订单弹窗 */
|
||||
const openOrderDetail = ref<(row: Record<string, any>) => void>();
|
||||
|
||||
const [WithdrawalAuditForm, WithdrawalAuditFormApi] =
|
||||
useVbenForm(withdrawalAudit);
|
||||
@@ -54,10 +56,12 @@ const [Modal, modalApi] = useVbenModal({
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
if (!isOpen) {
|
||||
preview.value = null;
|
||||
openOrderDetail.value = undefined;
|
||||
return;
|
||||
}
|
||||
const { id, modalType } = modalApi.getData<Record<string, any>>();
|
||||
type.value = modalType;
|
||||
openOrderDetail.value = modalApi.getData()?.openOrderDetail;
|
||||
WithdrawalAuditFormApi.setValues({ id, reason: '' });
|
||||
loading.value = true;
|
||||
try {
|
||||
@@ -73,6 +77,10 @@ function formatMoney(val: any) {
|
||||
const n = Number(val || 0);
|
||||
return Number.isFinite(n) ? n.toFixed(2) : '0.00';
|
||||
}
|
||||
|
||||
function viewOrder(od: Record<string, any>) {
|
||||
openOrderDetail.value?.(od);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -116,6 +124,24 @@ function formatMoney(val: any) {
|
||||
</Tag>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="preview.orders && preview.orders.length" class="preview-orders">
|
||||
<div class="preview-orders-title">关联订单 · 每单金额</div>
|
||||
<div
|
||||
v-for="od in preview.orders"
|
||||
:key="od.id || od.order_id"
|
||||
class="preview-row"
|
||||
>
|
||||
<span class="label">
|
||||
<Button type="link" size="small" class="order-no-btn" @click="viewOrder(od)">
|
||||
{{ od.order_no || '-' }}
|
||||
</Button>
|
||||
</span>
|
||||
<span class="value">
|
||||
{{ od.category_txt || od.order_type_txt || '' }}
|
||||
¥{{ formatMoney(od.amount) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<WithdrawalAuditForm />
|
||||
</Spin>
|
||||
@@ -163,6 +189,30 @@ function formatMoney(val: any) {
|
||||
color: hsl(var(--primary));
|
||||
}
|
||||
|
||||
.label.mono {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
|
||||
.order-no-btn {
|
||||
padding-inline: 0;
|
||||
height: auto;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
|
||||
.preview-orders {
|
||||
max-height: 180px;
|
||||
margin-top: 8px;
|
||||
padding-top: 8px;
|
||||
overflow: auto;
|
||||
border-top: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.preview-orders-title {
|
||||
margin-bottom: 8px;
|
||||
font-size: 12px;
|
||||
color: hsl(var(--muted-foreground));
|
||||
}
|
||||
|
||||
.ml-2 {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
import StatisticsReconciliation from '#/views/finance/reconciliation/components/statistics.vue';
|
||||
import WithdrawalRelationDetail from '#/views/finance/withdrawal/components/withdrawal-relation-detail.vue';
|
||||
import { useWithdrawOrderDetailModals } from '#/views/finance/withdrawal/utils/use-withdraw-order-detail';
|
||||
|
||||
import WithdrawalAudit from './components/WithdrawalAudit.vue';
|
||||
import { formOptions } from './config/search';
|
||||
@@ -24,6 +25,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
|
||||
const statistics = ref();
|
||||
|
||||
const { OrderDetailModal, RegisterDetailModal, openOrderDetail } =
|
||||
useWithdrawOrderDetailModals();
|
||||
|
||||
const [WithdrawalAuditModal, WithdrawalAuditModalApi] = useVbenModal({
|
||||
connectedComponent: WithdrawalAudit,
|
||||
});
|
||||
@@ -42,13 +46,14 @@ const infoModal = (id: number, modalType = 0) => {
|
||||
id,
|
||||
modalType,
|
||||
gridApi,
|
||||
openOrderDetail,
|
||||
});
|
||||
WithdrawalAuditModalApi.open();
|
||||
};
|
||||
|
||||
/** 打开提现+代付互通详情 */
|
||||
const openDetail = (id: number) => {
|
||||
relationDetailApi.setData({ id, source: 'apply' });
|
||||
relationDetailApi.setData({ id, source: 'apply', openOrderDetail });
|
||||
relationDetailApi.open();
|
||||
};
|
||||
</script>
|
||||
@@ -56,6 +61,8 @@ const openDetail = (id: number) => {
|
||||
<template>
|
||||
<Page auto-content-height title="提现审核">
|
||||
<WithdrawalAuditModal />
|
||||
<OrderDetailModal />
|
||||
<RegisterDetailModal />
|
||||
<RelationDetailModal />
|
||||
<StatisticsReconciliation v-if="statistics" :statistics="statistics" />
|
||||
<Grid>
|
||||
|
||||
@@ -59,3 +59,8 @@ export async function resetPassword(id: number) {
|
||||
id,
|
||||
});
|
||||
}
|
||||
|
||||
/** 可提订单明细分页 xk-api GET /withdrawal-application/withdrawable-orders */
|
||||
export async function getWithdrawableOrders(data: Record<string, any>) {
|
||||
return requestClient.get<any>(`${prefix}withdrawable-orders`, { params: data });
|
||||
}
|
||||
|
||||
@@ -31,14 +31,29 @@ const props = defineProps<{
|
||||
totalEarnings: number;
|
||||
withdrawnAmount: number;
|
||||
withdrawnFrozenAmount: number;
|
||||
tcmBalance?: number;
|
||||
westernBalance?: number;
|
||||
otherBalance?: number;
|
||||
customGap?: number;
|
||||
/** 造成差额的历史自定义提现单 */
|
||||
customGapApplies?: Record<string, any>[];
|
||||
/** 点击统计岛 */
|
||||
onStatClick?: (key: string) => void;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
apply: [];
|
||||
editCard: [];
|
||||
refresh: [];
|
||||
statClick: [key: string];
|
||||
viewApply: [id: number];
|
||||
}>();
|
||||
|
||||
function handleStatClick(key: string) {
|
||||
props.onStatClick?.(key);
|
||||
emit('statClick', key);
|
||||
}
|
||||
|
||||
/** 鼠标在银行卡上的相对位置(用于光泽跟随),-1 表示未进入 */
|
||||
const glowX = ref(-1);
|
||||
const glowY = ref(-1);
|
||||
@@ -108,6 +123,24 @@ const statItems = computed(() => {
|
||||
value: Number(props.balance || 0),
|
||||
icon: 'lucide:scale',
|
||||
},
|
||||
{
|
||||
key: 'tcm',
|
||||
label: '中药可提',
|
||||
value: Number(props.tcmBalance || 0),
|
||||
icon: 'lucide:leaf',
|
||||
},
|
||||
{
|
||||
key: 'western',
|
||||
label: '西药可提',
|
||||
value: Number(props.westernBalance || 0),
|
||||
icon: 'lucide:pill',
|
||||
},
|
||||
{
|
||||
key: 'other',
|
||||
label: '其他可提',
|
||||
value: Number(props.otherBalance || 0),
|
||||
icon: 'lucide:layers',
|
||||
},
|
||||
];
|
||||
if (!props.isStoreUser) {
|
||||
items.push({
|
||||
@@ -247,9 +280,10 @@ function formatMoney(val: number) {
|
||||
<article
|
||||
v-for="item in statItems"
|
||||
:key="item.key"
|
||||
class="stat-island"
|
||||
class="stat-island stat-island--clickable"
|
||||
:class="{ 'stat-island--fee': item.emphasize }"
|
||||
:aria-label="item.label"
|
||||
@click="handleStatClick(item.key)"
|
||||
>
|
||||
<div class="stat-island-glow"></div>
|
||||
<!-- 右上角图标(规范:SVG 图标,固定尺寸) -->
|
||||
@@ -261,6 +295,20 @@ function formatMoney(val: number) {
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="Number(customGap || 0) > 0" class="gap-tip">
|
||||
按单可提合计比账户余额多 ¥{{ formatMoney(Number(customGap || 0)) }},多半是历史自定义提现未按单核销。点击打款单号可看申请→审核→打款进度。
|
||||
<div v-if="customGapApplies && customGapApplies.length" class="gap-applies">
|
||||
<button
|
||||
v-for="row in customGapApplies"
|
||||
:key="row.id"
|
||||
type="button"
|
||||
class="gap-order"
|
||||
@click.stop="emit('viewApply', Number(row.id))"
|
||||
>
|
||||
{{ row.order_no }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -610,11 +658,41 @@ function formatMoney(val: number) {
|
||||
}
|
||||
|
||||
/* ===== 统计岛:玻璃质感 + 右上角图标 + 淡荧光 ===== */
|
||||
.gap-tip {
|
||||
margin-top: 10px;
|
||||
padding: 8px 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
color: hsl(var(--warning));
|
||||
background: hsl(var(--warning) / 0.12);
|
||||
border: 1px solid hsl(var(--warning) / 30%);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.gap-applies {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px 10px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.gap-order {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: none;
|
||||
color: hsl(var(--primary));
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gap-order:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.stat-island {
|
||||
position: relative;
|
||||
padding: 14px;
|
||||
overflow: hidden;
|
||||
cursor: default;
|
||||
cursor: pointer;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
hsl(var(--primary) / 4%),
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* 提现单 / 资金变动关联订单列表,再下钻业务订单
|
||||
*/
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, Empty, Table } from 'ant-design-vue';
|
||||
|
||||
const emit = defineEmits<{
|
||||
viewOrder: [row: Record<string, any>];
|
||||
}>();
|
||||
|
||||
const orders = ref<Record<string, any>[]>([]);
|
||||
const title = ref('关联订单');
|
||||
|
||||
const columns = [
|
||||
{ title: '订单号', dataIndex: 'order_no', key: 'order_no' },
|
||||
{ title: '订单类型', dataIndex: 'order_type_txt', key: 'order_type_txt', width: 110 },
|
||||
{ title: '分类', dataIndex: 'category_txt', key: 'category_txt', width: 80 },
|
||||
{ title: '占用金额', dataIndex: 'amount', key: 'amount', width: 110 },
|
||||
{ title: '操作', key: 'action', width: 90 },
|
||||
];
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
footer: false,
|
||||
onOpenChange(isOpen) {
|
||||
if (isOpen) {
|
||||
const data = modalApi.getData<Record<string, any>>() || {};
|
||||
orders.value = data.orders || [];
|
||||
title.value = data.title || '关联订单';
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
open(payload: { orders?: Record<string, any>[]; title?: string }) {
|
||||
modalApi.setData(payload);
|
||||
modalApi.open();
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-[720px]" :title="title">
|
||||
<Table
|
||||
v-if="orders.length > 0"
|
||||
:columns="columns"
|
||||
:data-source="orders"
|
||||
:pagination="false"
|
||||
row-key="id"
|
||||
size="small"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<Button type="link" size="small" @click="emit('viewOrder', record)">
|
||||
订单详情
|
||||
</Button>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
<Empty v-else description="该记录没有关联业务订单(自定义金额提现不会核销具体订单)" />
|
||||
</Modal>
|
||||
</template>
|
||||
@@ -0,0 +1,133 @@
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* 按单合计超过余额的确认弹窗(一级申请的二级弹窗)
|
||||
* 与申请弹窗同级挂载,zIndex 1500 叠在申请之上;点打款单号再开页面级进度弹窗(2000)
|
||||
*/
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
const requested = ref(0);
|
||||
const balance = ref(0);
|
||||
const gap = ref(0);
|
||||
const applies = ref<Record<string, any>[]>([]);
|
||||
const openApplyProgress = ref<(id: number) => void>();
|
||||
const onAccept = ref<() => void>();
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
title: '按单金额超过可提现余额',
|
||||
class: 'w-[560px]',
|
||||
draggable: true,
|
||||
confirmText: '按余额申请',
|
||||
cancelText: '暂时不提现',
|
||||
centered: true,
|
||||
// 必须高于申请提现(1000),否则会像截图那样被挡在后面;进度详情用 3000
|
||||
zIndex: 2000,
|
||||
onConfirm() {
|
||||
onAccept.value?.();
|
||||
modalApi.close();
|
||||
},
|
||||
onOpenChange(isOpen) {
|
||||
if (!isOpen) return;
|
||||
const data = modalApi.getData<Record<string, any>>() || {};
|
||||
requested.value = Number(data.requested || 0);
|
||||
balance.value = Number(data.balance || 0);
|
||||
gap.value = Number(data.gap || requested.value - balance.value);
|
||||
applies.value = data.applies || [];
|
||||
openApplyProgress.value = data.openApplyProgress;
|
||||
onAccept.value = data.onAccept;
|
||||
},
|
||||
});
|
||||
|
||||
function formatMoney(val: number | string) {
|
||||
return Number(val || 0).toFixed(2);
|
||||
}
|
||||
|
||||
function openApply(row: Record<string, any>) {
|
||||
const id = Number(row.id || 0);
|
||||
if (id > 0) {
|
||||
openApplyProgress.value?.(id);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal>
|
||||
<div class="gap-confirm">
|
||||
<p>
|
||||
按单可提合计 <b>¥{{ formatMoney(requested) }}</b>
|
||||
超过可提现余额 <b>¥{{ formatMoney(balance) }}</b>,差额
|
||||
<b>¥{{ formatMoney(gap) }}</b>。
|
||||
</p>
|
||||
<p>
|
||||
确认后将按可提现余额 <b>¥{{ formatMoney(balance) }}</b> 直接申请;取消则暂时不提现。
|
||||
</p>
|
||||
<p v-if="applies.length > 0" class="hint">
|
||||
可能原因是这些提现申请使用了自定义金额(点击打款单号可看进度):
|
||||
</p>
|
||||
<p v-else class="hint">可能原因是历史自定义提现未按单核销。</p>
|
||||
<ul v-if="applies.length > 0" class="apply-list">
|
||||
<li v-for="row in applies" :key="row.id">
|
||||
<button type="button" class="order-btn" @click="openApply(row)">
|
||||
{{ row.order_no }}
|
||||
</button>
|
||||
<span class="meta">
|
||||
¥{{ formatMoney(row.apply_cash) }} · {{ row.apply_time || '-' }}
|
||||
· {{ row.check_status_txt || '-' }}
|
||||
· {{ row.dakuan_status_txt || '-' }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.gap-confirm {
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
.gap-confirm p {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: hsl(var(--muted-foreground));
|
||||
}
|
||||
|
||||
.apply-list {
|
||||
max-height: 220px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.apply-list li {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px 10px;
|
||||
align-items: baseline;
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
.order-btn {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: none;
|
||||
color: hsl(var(--primary));
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.order-btn:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.meta {
|
||||
color: hsl(var(--muted-foreground));
|
||||
}
|
||||
</style>
|
||||
@@ -13,6 +13,7 @@ import { getAdminAccountBalance, getMyCard } from '#/views/system/admin/api';
|
||||
|
||||
import AccountInfoCard from './account-info-card.vue';
|
||||
import SaveMyCard from './card-modal.vue';
|
||||
import GapConfirmModal from './gap-confirm-modal.vue';
|
||||
import FormModalDemo from './withdrawal-modal.vue';
|
||||
|
||||
defineOptions({
|
||||
@@ -34,6 +35,11 @@ const isStoreUser = computed(() => userType.value === 1);
|
||||
/** 平台账号需展示累计代付手续费 */
|
||||
const isPlatformUser = computed(() => userType.value === 2);
|
||||
|
||||
const emit = defineEmits<{
|
||||
statClick: [key: string];
|
||||
viewApply: [id: number];
|
||||
}>();
|
||||
|
||||
const balance = ref(0);
|
||||
const pendingEarnings = ref(0);
|
||||
const settledEarnings = ref(0);
|
||||
@@ -42,6 +48,11 @@ const withdrawnAmount = ref(0);
|
||||
const withdrawnFrozenAmount = ref(0);
|
||||
const frozenAmount = ref(0);
|
||||
const fee = ref(0);
|
||||
const tcmBalance = ref(0);
|
||||
const westernBalance = ref(0);
|
||||
const otherBalance = ref(0);
|
||||
const customGap = ref(0);
|
||||
const customGapApplies = ref<Record<string, any>[]>([]);
|
||||
const myCard = ref<Record<string, any> | null>(null);
|
||||
|
||||
/**
|
||||
@@ -59,6 +70,11 @@ function showCard() {
|
||||
withdrawnFrozenAmount.value = res.withdrawn_frozen;
|
||||
frozenAmount.value = res.frozen;
|
||||
fee.value = res.charge;
|
||||
tcmBalance.value = Number(res.tcm_balance || 0);
|
||||
westernBalance.value = Number(res.western_balance || 0);
|
||||
otherBalance.value = Number(res.other_balance || 0);
|
||||
customGap.value = Number(res.custom_gap || 0);
|
||||
customGapApplies.value = res.custom_gap_applies || [];
|
||||
});
|
||||
}
|
||||
|
||||
@@ -74,10 +90,28 @@ showCard();
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: FormModalDemo,
|
||||
zIndex: 1000,
|
||||
});
|
||||
const [saveMyCardModal, saveMyCardApi] = useVbenModal({
|
||||
connectedComponent: SaveMyCard,
|
||||
});
|
||||
/** 差额确认与申请弹窗同级挂载;zIndex 内外都写,避免 connectedComponent 吃掉内部层级 */
|
||||
const [GapConfirmModalComp, gapConfirmModalApi] = useVbenModal({
|
||||
connectedComponent: GapConfirmModal,
|
||||
zIndex: 2000,
|
||||
});
|
||||
|
||||
function openApplyProgress(id: number) {
|
||||
emit('viewApply', id);
|
||||
}
|
||||
|
||||
function openGapConfirm(data: Record<string, any>) {
|
||||
gapConfirmModalApi.setData({
|
||||
...data,
|
||||
openApplyProgress,
|
||||
});
|
||||
gapConfirmModalApi.open();
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
message.success('刷新成功');
|
||||
@@ -87,9 +121,13 @@ function refresh() {
|
||||
const showModal = (data = {}) => {
|
||||
formModalApi.setData({
|
||||
values: data,
|
||||
balance,
|
||||
balance: balance.value,
|
||||
customGap: customGap.value,
|
||||
customGapApplies: customGapApplies.value,
|
||||
gridApi: props.gridApi,
|
||||
showCard,
|
||||
openGapConfirm,
|
||||
openApplyProgress,
|
||||
});
|
||||
formModalApi.open();
|
||||
};
|
||||
@@ -108,6 +146,7 @@ const showCardModal = () => {
|
||||
<template>
|
||||
<div>
|
||||
<FormModal />
|
||||
<GapConfirmModalComp />
|
||||
<saveMyCardModal />
|
||||
<AccountInfoCard
|
||||
:my-card="myCard"
|
||||
@@ -120,9 +159,16 @@ const showCardModal = () => {
|
||||
:withdrawn-frozen-amount="withdrawnFrozenAmount"
|
||||
:frozen-amount="frozenAmount"
|
||||
:fee="fee"
|
||||
:tcm-balance="tcmBalance"
|
||||
:western-balance="westernBalance"
|
||||
:other-balance="otherBalance"
|
||||
:custom-gap="customGap"
|
||||
:custom-gap-applies="customGapApplies"
|
||||
@apply="showModal"
|
||||
@edit-card="showCardModal"
|
||||
@refresh="refresh"
|
||||
@stat-click="emit('statClick', $event)"
|
||||
@view-apply="emit('viewApply', $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* 可提订单明细抽屉:按中药/西药/其他下钻,并可查看业务订单
|
||||
*/
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { useVbenDrawer } from '@vben/common-ui';
|
||||
|
||||
import { Button, Empty, Spin, Table } from 'ant-design-vue';
|
||||
|
||||
import { getWithdrawableOrders } from '#/views/finance/withdrawal/api';
|
||||
|
||||
const emit = defineEmits<{
|
||||
viewOrder: [row: Record<string, any>];
|
||||
}>();
|
||||
|
||||
const loading = ref(false);
|
||||
const items = ref<Record<string, any>[]>([]);
|
||||
const total = ref(0);
|
||||
const page = ref(1);
|
||||
const pageSize = ref(15);
|
||||
const category = ref(0);
|
||||
const title = ref('可提订单');
|
||||
|
||||
const columns = [
|
||||
{ title: '订单号', dataIndex: 'order_no', key: 'order_no', ellipsis: true },
|
||||
{ title: '订单类型', dataIndex: 'order_type_txt', key: 'order_type_txt', width: 100 },
|
||||
{ title: '分类', dataIndex: 'category_txt', key: 'category_txt', width: 80 },
|
||||
{ title: '可提金额', dataIndex: 'remain', key: 'remain', width: 110 },
|
||||
{ title: '结算时间', dataIndex: 'settled_at_txt', key: 'settled_at_txt', width: 170 },
|
||||
{ title: '操作', key: 'action', width: 90 },
|
||||
];
|
||||
|
||||
const [Drawer, drawerApi] = useVbenDrawer({
|
||||
onOpenChange(isOpen) {
|
||||
if (isOpen) {
|
||||
const data = drawerApi.getData<Record<string, any>>() || {};
|
||||
category.value = Number(data.category || 0);
|
||||
title.value = data.title || '可提订单';
|
||||
page.value = 1;
|
||||
loadList();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
async function loadList() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getWithdrawableOrders({
|
||||
page: page.value,
|
||||
pageSize: pageSize.value,
|
||||
category: category.value,
|
||||
});
|
||||
items.value = res?.items || [];
|
||||
total.value = Number(res?.total || 0);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onPageChange(current: number, size: number) {
|
||||
page.value = current;
|
||||
pageSize.value = size;
|
||||
loadList();
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open(payload: { category?: number; title?: string }) {
|
||||
drawerApi.setData(payload);
|
||||
drawerApi.open();
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Drawer class="w-[860px]" :title="title">
|
||||
<Spin :spinning="loading">
|
||||
<Table
|
||||
v-if="items.length > 0 || loading"
|
||||
:columns="columns"
|
||||
:data-source="items"
|
||||
:pagination="{
|
||||
current: page,
|
||||
pageSize,
|
||||
total,
|
||||
showSizeChanger: true,
|
||||
onChange: onPageChange,
|
||||
}"
|
||||
row-key="order_id"
|
||||
size="small"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<Button type="link" size="small" @click="emit('viewOrder', record)">
|
||||
订单详情
|
||||
</Button>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
<Empty v-else description="暂无可提订单" />
|
||||
</Spin>
|
||||
</Drawer>
|
||||
</template>
|
||||
@@ -1,79 +1,323 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
/**
|
||||
* 申请提现:按订单 / 按时间范围 / 自定义金额
|
||||
* 差额确认、打款进度都走页面级二级弹窗(connectedComponent + zIndex),不嵌套、不用 confirm()
|
||||
*/
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
import { Alert, Button, DatePicker, Input, message, Radio, RadioGroup, Select, Table } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { createWithdrawalApplicationWithdrawal } from '#/views/finance/withdrawal/api';
|
||||
|
||||
import { modalFormProps } from '../config/form';
|
||||
import { createWithdrawalApplicationWithdrawal, getWithdrawableOrders } from '#/views/finance/withdrawal/api';
|
||||
|
||||
const gridApi = ref();
|
||||
const showCard = ref();
|
||||
const openGapConfirm = ref<(data: Record<string, any>) => void>();
|
||||
const openApplyProgress = ref<(id: number) => void>();
|
||||
const maxAmount = ref(0);
|
||||
const customGap = ref(0);
|
||||
const customGapApplies = ref<Record<string, any>[]>([]);
|
||||
const mode = ref(2);
|
||||
const amount = ref('');
|
||||
const category = ref(0);
|
||||
const dateRange = ref<any[]>([]);
|
||||
const orderLoading = ref(false);
|
||||
const orderItems = ref<Record<string, any>[]>([]);
|
||||
const selectedRowKeys = ref<string[]>([]);
|
||||
const selectedRows = ref<Record<string, any>[]>([]);
|
||||
|
||||
const [Form, formApi] = useVbenForm(modalFormProps);
|
||||
const modeOptions = [
|
||||
{ label: '按订单', value: 2 },
|
||||
{ label: '按时间范围', value: 3 },
|
||||
{ label: '自定义金额', value: 1 },
|
||||
];
|
||||
const categoryOptions = [
|
||||
{ label: '全部类型', value: 0 },
|
||||
{ label: '中药', value: 1 },
|
||||
{ label: '西药', value: 2 },
|
||||
{ label: '其他', value: 3 },
|
||||
];
|
||||
const orderColumns = [
|
||||
{ title: '订单号', dataIndex: 'order_no', key: 'order_no', ellipsis: true },
|
||||
{ title: '分类', dataIndex: 'category_txt', key: 'category_txt', width: 80 },
|
||||
{ title: '可提金额', dataIndex: 'remain', key: 'remain', width: 110 },
|
||||
{ title: '结算时间', dataIndex: 'settled_at_txt', key: 'settled_at_txt', width: 170 },
|
||||
];
|
||||
|
||||
const selectedSum = computed(() => {
|
||||
return selectedRows.value.reduce((sum, row) => sum + Number(row.remain || 0), 0);
|
||||
});
|
||||
|
||||
const rangeSum = computed(() => {
|
||||
return orderItems.value.reduce((sum, row) => sum + Number(row.remain || 0), 0);
|
||||
});
|
||||
|
||||
/** 当前模式即将申请的按单合计,用来判断要不要弹出差额确认 */
|
||||
const requestedSum = computed(() => {
|
||||
if (mode.value === 2) return selectedSum.value;
|
||||
if (mode.value === 3) return rangeSum.value;
|
||||
return 0;
|
||||
});
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
draggable: true,
|
||||
zIndex: 1000,
|
||||
onCancel() {
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
formApi.validate().then(async (e: any) => {
|
||||
if (e.valid) {
|
||||
const values = await formApi.getValues();
|
||||
modalApi.setState({ loading: true, confirmLoading: true });
|
||||
const submitApi = createWithdrawalApplicationWithdrawal;
|
||||
submitApi(values)
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
gridApi.value?.reload();
|
||||
showCard.value();
|
||||
modalApi.close();
|
||||
})
|
||||
.finally(() => {
|
||||
modalApi.setState({ loading: false, confirmLoading: false });
|
||||
});
|
||||
}
|
||||
});
|
||||
await formApi.validateAndSubmitForm();
|
||||
const payload = buildPayload();
|
||||
if (!payload) return;
|
||||
if (mode.value !== 1 && requestedSum.value > Number(maxAmount.value) + 0.0001) {
|
||||
openGapConfirm.value?.({
|
||||
requested: requestedSum.value,
|
||||
balance: Number(maxAmount.value || 0),
|
||||
gap: requestedSum.value - Number(maxAmount.value || 0),
|
||||
applies: customGapApplies.value,
|
||||
onAccept: () => submitApply({ ...payload, accept_gap: 1 }),
|
||||
});
|
||||
return;
|
||||
}
|
||||
await submitApply(payload);
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
showCard.value = isOpen ? modalApi.getData()?.showCard : null;
|
||||
const data = isOpen ? modalApi.getData<Record<string, any>>() || {} : {};
|
||||
gridApi.value = isOpen ? data.gridApi : null;
|
||||
showCard.value = isOpen ? data.showCard : null;
|
||||
openGapConfirm.value = isOpen ? data.openGapConfirm : undefined;
|
||||
openApplyProgress.value = isOpen ? data.openApplyProgress : undefined;
|
||||
if (isOpen) {
|
||||
const { values, balance } = modalApi.getData<Record<string, any>>();
|
||||
if (values) {
|
||||
maxAmount.value = balance;
|
||||
formApi.setValues(values);
|
||||
}
|
||||
maxAmount.value = Number(data.balance || 0);
|
||||
customGap.value = Number(data.customGap || 0);
|
||||
customGapApplies.value = data.customGapApplies || [];
|
||||
mode.value = 2;
|
||||
amount.value = '';
|
||||
category.value = 0;
|
||||
dateRange.value = [];
|
||||
selectedRowKeys.value = [];
|
||||
selectedRows.value = [];
|
||||
loadOrders({});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const setAmount = (amount: number) => {
|
||||
formApi.setValues({ amount });
|
||||
};
|
||||
function rowKeyOf(row: Record<string, any>) {
|
||||
return `${row.order_id}_${row.order_type}`;
|
||||
}
|
||||
|
||||
async function loadOrders(extra: Record<string, any>) {
|
||||
orderLoading.value = true;
|
||||
try {
|
||||
const res = await getWithdrawableOrders({
|
||||
page: 1,
|
||||
pageSize: 100,
|
||||
category: category.value,
|
||||
...extra,
|
||||
});
|
||||
orderItems.value = res?.items || [];
|
||||
} finally {
|
||||
orderLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onSelectChange(keys: (string | number)[], rows: Record<string, any>[]) {
|
||||
selectedRowKeys.value = keys.map(String);
|
||||
selectedRows.value = rows;
|
||||
}
|
||||
|
||||
function previewRange() {
|
||||
if (!dateRange.value || dateRange.value.length !== 2) {
|
||||
message.warning('请选择时间范围');
|
||||
return;
|
||||
}
|
||||
const start = dateRange.value[0];
|
||||
const end = dateRange.value[1];
|
||||
loadOrders({
|
||||
start_at: start?.startOf?.('day')?.unix?.() || start?.unix?.() || 0,
|
||||
end_at: end?.endOf?.('day')?.unix?.() || end?.unix?.() || 0,
|
||||
});
|
||||
}
|
||||
|
||||
function setAmount(val: number) {
|
||||
amount.value = String(val);
|
||||
}
|
||||
|
||||
function viewApplyProgress(id: number) {
|
||||
openApplyProgress.value?.(id);
|
||||
}
|
||||
|
||||
async function submitApply(payload: Record<string, any>) {
|
||||
modalApi.setState({ loading: true, confirmLoading: true });
|
||||
try {
|
||||
await createWithdrawalApplicationWithdrawal(payload);
|
||||
message.success('提交成功');
|
||||
gridApi.value?.reload();
|
||||
showCard.value?.();
|
||||
modalApi.close();
|
||||
} finally {
|
||||
modalApi.setState({ loading: false, confirmLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
function buildPayload(): Record<string, any> | null {
|
||||
if (mode.value === 1) {
|
||||
const amt = Number(amount.value);
|
||||
if (!amt || amt < 1) {
|
||||
message.warning('请输入不小于 1 元的提现金额');
|
||||
return null;
|
||||
}
|
||||
if (amt > maxAmount.value) {
|
||||
message.warning('不能超过可提现余额');
|
||||
return null;
|
||||
}
|
||||
return { mode: 1, amount: amt, is_custom: 1 };
|
||||
}
|
||||
if (mode.value === 2) {
|
||||
if (selectedRows.value.length === 0) {
|
||||
message.warning('请勾选要提现的订单');
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
mode: 2,
|
||||
order_ids: selectedRows.value.map((row) => ({
|
||||
order_id: row.order_id,
|
||||
order_type: row.order_type,
|
||||
})),
|
||||
};
|
||||
}
|
||||
if (!dateRange.value || dateRange.value.length !== 2) {
|
||||
message.warning('请选择时间范围');
|
||||
return null;
|
||||
}
|
||||
const start = dateRange.value[0];
|
||||
const end = dateRange.value[1];
|
||||
return {
|
||||
mode: 3,
|
||||
category: category.value,
|
||||
start_at: start?.startOf?.('day')?.unix?.() || start?.unix?.() || 0,
|
||||
end_at: end?.endOf?.('day')?.unix?.() || end?.unix?.() || 0,
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-[80%] h-[80%]" title="申请提现">
|
||||
<Form />
|
||||
<div class="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||
<Button type="primary" @click="setAmount(1000)"> 1000 </Button>
|
||||
<Button type="primary" @click="setAmount(3000)"> 3000 </Button>
|
||||
<Button type="primary" @click="setAmount(5000)"> 5000 </Button>
|
||||
<Button type="primary" @click="setAmount(10000)"> 10000 </Button>
|
||||
<Button type="primary" @click="setAmount(30000)"> 30000 </Button>
|
||||
<Button type="primary" @click="setAmount(50000)"> 50000 </Button>
|
||||
<Button type="primary" @click="setAmount(100000)"> 100000 </Button>
|
||||
<Button type="primary" @click="setAmount(maxAmount.value)">
|
||||
全部提现
|
||||
</Button>
|
||||
<Modal class="w-[860px]" title="申请提现">
|
||||
<Alert
|
||||
v-if="Number(customGap) > 0"
|
||||
type="warning"
|
||||
show-icon
|
||||
class="mb-3"
|
||||
>
|
||||
<template #message>
|
||||
按单可提比账户余额多 ¥{{ Number(customGap).toFixed(2) }}。点击打款单号可看申请→审核→打款进度。
|
||||
</template>
|
||||
<template #description>
|
||||
<div class="gap-applies">
|
||||
<button
|
||||
v-for="row in customGapApplies"
|
||||
:key="row.id"
|
||||
type="button"
|
||||
class="gap-order"
|
||||
@click="viewApplyProgress(Number(row.id))"
|
||||
>
|
||||
{{ row.order_no }}({{ row.apply_cash }}元)
|
||||
</button>
|
||||
<span v-if="customGapApplies.length === 0">历史自定义提现未按单核销</span>
|
||||
</div>
|
||||
</template>
|
||||
</Alert>
|
||||
<div class="mb-3 text-sm text-muted-foreground">
|
||||
可提现余额 ¥{{ Number(maxAmount || 0).toFixed(2) }}
|
||||
</div>
|
||||
<RadioGroup v-model:value="mode" class="mb-4">
|
||||
<Radio v-for="opt in modeOptions" :key="opt.value" :value="opt.value">
|
||||
{{ opt.label }}
|
||||
</Radio>
|
||||
</RadioGroup>
|
||||
|
||||
<template v-if="mode === 1">
|
||||
<div class="mb-2 text-sm">提现金额</div>
|
||||
<Input
|
||||
v-model:value="amount"
|
||||
type="number"
|
||||
min="1"
|
||||
placeholder="请输入提现金额"
|
||||
/>
|
||||
<div class="mt-4 grid grid-cols-2 gap-3 md:grid-cols-4">
|
||||
<Button type="primary" @click="setAmount(1000)">1000</Button>
|
||||
<Button type="primary" @click="setAmount(3000)">3000</Button>
|
||||
<Button type="primary" @click="setAmount(5000)">5000</Button>
|
||||
<Button type="primary" @click="setAmount(10000)">10000</Button>
|
||||
<Button type="primary" @click="setAmount(30000)">30000</Button>
|
||||
<Button type="primary" @click="setAmount(50000)">50000</Button>
|
||||
<Button type="primary" @click="setAmount(100000)">100000</Button>
|
||||
<Button type="primary" @click="setAmount(maxAmount)">全部提现</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else-if="mode === 2">
|
||||
<div class="mb-2 flex items-center justify-between">
|
||||
<Select
|
||||
v-model:value="category"
|
||||
class="w-40"
|
||||
:options="categoryOptions"
|
||||
@change="loadOrders({})"
|
||||
/>
|
||||
<span class="text-sm">已选 {{ selectedRows.length }} 笔,合计 ¥{{ selectedSum.toFixed(2) }}</span>
|
||||
</div>
|
||||
<Table
|
||||
:columns="orderColumns"
|
||||
:data-source="orderItems"
|
||||
:loading="orderLoading"
|
||||
:pagination="false"
|
||||
:row-key="rowKeyOf"
|
||||
:row-selection="{ selectedRowKeys, onChange: onSelectChange }"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div class="mb-3 flex flex-wrap items-center gap-2">
|
||||
<DatePicker.RangePicker v-model:value="dateRange" />
|
||||
<Select v-model:value="category" class="w-32" :options="categoryOptions" />
|
||||
<Button type="primary" @click="previewRange">预览订单</Button>
|
||||
<span class="text-sm">共 {{ orderItems.length }} 笔,合计 ¥{{ rangeSum.toFixed(2) }}</span>
|
||||
</div>
|
||||
<Table
|
||||
:columns="orderColumns"
|
||||
:data-source="orderItems"
|
||||
:loading="orderLoading"
|
||||
:pagination="false"
|
||||
:row-key="rowKeyOf"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.text-muted-foreground {
|
||||
color: hsl(var(--muted-foreground));
|
||||
}
|
||||
|
||||
.gap-applies {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px 10px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.gap-order {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: none;
|
||||
color: hsl(var(--primary));
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gap-order:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -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<Record<string, any> | null>(null);
|
||||
/** 页面级打开订单详情(不嵌套在本弹窗内,避免层级被挡住) */
|
||||
const openOrderDetail = ref<(row: Record<string, any>) => void>();
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
footer: false,
|
||||
draggable: true,
|
||||
// 点打款单号打开:盖住申请(1000)和差额确认(2000)
|
||||
zIndex: 3000,
|
||||
onOpenChange(isOpen) {
|
||||
if (isOpen) {
|
||||
const data = modalApi.getData<Record<string, any>>() || {};
|
||||
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<string, any>) {
|
||||
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<string, string>;
|
||||
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<string, string>;
|
||||
let arrived: Record<string, string>;
|
||||
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<string, any>) {
|
||||
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<string, any>) {
|
||||
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<string, any>) {
|
||||
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) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal class="w-[640px]" title="提现与代付详情">
|
||||
<Modal class="w-[720px]" title="提现与代付详情">
|
||||
<Spin :spinning="loading">
|
||||
<div v-if="view" class="relation-detail">
|
||||
<div class="hero">
|
||||
@@ -155,6 +266,11 @@ function chargeTagColor(status: number) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">提现进度</div>
|
||||
<Steps size="small" :items="progressItems" />
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">提现申请</div>
|
||||
<div class="rows">
|
||||
@@ -176,6 +292,12 @@ function chargeTagColor(status: number) {
|
||||
<span class="label">打款金额</span>
|
||||
<span class="value">¥{{ formatMoney(view.true_cash) }}</span>
|
||||
</div>
|
||||
<div v-if="Number(view.tcm_amount || 0) || Number(view.western_amount || 0) || Number(view.other_amount || 0)" class="row">
|
||||
<span class="label">中/西/其他</span>
|
||||
<span class="value">
|
||||
¥{{ formatMoney(view.tcm_amount) }} / ¥{{ formatMoney(view.western_amount) }} / ¥{{ formatMoney(view.other_amount) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">申请时间</span>
|
||||
<span class="value">{{ view.apply_time || '-' }}</span>
|
||||
@@ -200,6 +322,48 @@ function chargeTagColor(status: number) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">
|
||||
关联订单
|
||||
<span v-if="relatedOrders.length > 0" class="section-extra">
|
||||
{{ relatedOrders.length }} 笔
|
||||
</span>
|
||||
</div>
|
||||
<Table
|
||||
v-if="relatedOrders.length > 0"
|
||||
:columns="relatedOrderColumns"
|
||||
:data-source="relatedOrders"
|
||||
:pagination="false"
|
||||
:scroll="{ y: 240 }"
|
||||
row-key="id"
|
||||
size="small"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'order_no'">
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
class="order-no-btn"
|
||||
@click="onViewOrder(record)"
|
||||
>
|
||||
{{ record.order_no || '-' }}
|
||||
</Button>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'amount'">
|
||||
<span class="value fee">¥{{ formatMoney(record.amount) }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<Button type="link" size="small" @click="onViewOrder(record)">
|
||||
详情
|
||||
</Button>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
<div v-else class="empty-orders">
|
||||
该记录没有关联业务订单(自定义金额提现不会核销具体订单)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section section--fee">
|
||||
<div class="section-title">代付手续费</div>
|
||||
<div class="rows">
|
||||
@@ -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;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -30,8 +30,18 @@ export const gridOptions: VxeGridProps<RowType> = {
|
||||
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: '申请时间' },
|
||||
|
||||
@@ -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<InstanceType<typeof SettlementDetailDrawer>>();
|
||||
const withdrawableOrderDrawerRef =
|
||||
ref<InstanceType<typeof WithdrawableOrderDrawer>>();
|
||||
const applyOrdersModalRef = ref<InstanceType<typeof ApplyOrdersModal>>();
|
||||
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<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();
|
||||
function showApplyOrders(row: Record<string, any>) {
|
||||
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<string, any>) {
|
||||
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<string, { category: number; title: string }> = {
|
||||
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="提现管理"
|
||||
>
|
||||
<OrderDetailModal />
|
||||
<Statistics v-show="isShow" :grid-api="GridApi" />
|
||||
<RegisterDetailModal />
|
||||
<RelationDetailModal />
|
||||
<WithdrawableOrderDrawer
|
||||
ref="withdrawableOrderDrawerRef"
|
||||
@view-order="openOrderDetail"
|
||||
/>
|
||||
<ApplyOrdersModal ref="applyOrdersModalRef" @view-order="openOrderDetail" />
|
||||
<Statistics
|
||||
v-show="isShow"
|
||||
:grid-api="GridApi"
|
||||
@stat-click="onStatClick"
|
||||
@view-apply="openApplyProgress"
|
||||
/>
|
||||
|
||||
<div class="withdrawal-lists" style="min-height: 500px">
|
||||
<AnalysisChartsTabs :tabs="chartTabs" class="mt-3">
|
||||
<AnalysisChartsTabs v-model:value="activeTab" :tabs="chartTabs" class="mt-3">
|
||||
<template #trends>
|
||||
<div style="min-height: 500px">
|
||||
<Grid>
|
||||
<template #toolbar-actions></template>
|
||||
<template #toolbar-tools></template>
|
||||
<template #order_no="{ row }">
|
||||
<Button
|
||||
v-if="row.order_no"
|
||||
type="link"
|
||||
size="small"
|
||||
class="order-no-btn"
|
||||
@click="openApplyProgress(Number(row.id))"
|
||||
>
|
||||
{{ row.order_no }}
|
||||
</Button>
|
||||
<span v-else class="cell-muted">-</span>
|
||||
</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>
|
||||
@@ -177,7 +242,23 @@ function dakuanStatusPill(status: number) {
|
||||
}}</span>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<TableAction :actions="[]" :drop-down-actions="[]" />
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '提现进度',
|
||||
type: 'link',
|
||||
size: 'small',
|
||||
onClick: openApplyProgress.bind(null, Number(row.id)),
|
||||
},
|
||||
{
|
||||
label: '关联订单',
|
||||
type: 'link',
|
||||
size: 'small',
|
||||
onClick: showApplyOrders.bind(null, row),
|
||||
},
|
||||
]"
|
||||
:drop-down-actions="[]"
|
||||
/>
|
||||
</template>
|
||||
</Grid>
|
||||
</div>
|
||||
@@ -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;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -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<string, any>,
|
||||
apis: {
|
||||
productModalApi: { setData: (data: Record<string, any>) => void; open: () => void };
|
||||
registerModalApi: { setData: (data: Record<string, any>) => 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<string, any>) {
|
||||
await openWithdrawRelatedOrderDetail(row, {
|
||||
productModalApi,
|
||||
registerModalApi,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
OrderDetailModal,
|
||||
RegisterDetailModal,
|
||||
openOrderDetail,
|
||||
};
|
||||
}
|
||||
@@ -17,14 +17,19 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
tabs: () => [],
|
||||
});
|
||||
|
||||
const defaultValue = computed(() => {
|
||||
return props.tabs?.[0]?.value;
|
||||
/** 受控当前 Tab,未传时回落到第一项 */
|
||||
const activeTab = defineModel<string>('value');
|
||||
const currentTab = computed({
|
||||
get: () => activeTab.value || props.tabs?.[0]?.value || '',
|
||||
set: (val: string) => {
|
||||
activeTab.value = val;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card-box w-full px-4 pt-3 pb-5">
|
||||
<Tabs :default-value="defaultValue">
|
||||
<Tabs :model-value="currentTab" @update:model-value="currentTab = String($event)">
|
||||
<TabsList>
|
||||
<template v-for="tab in tabs" :key="tab.label">
|
||||
<TabsTrigger :value="tab.value"> {{ tab.label }} </TabsTrigger>
|
||||
|
||||
Reference in New Issue
Block a user