Merge remote-tracking branch 'origin/feature/0804-CZ' into feature/0723/ZR
This commit is contained in:
Generated
+21
@@ -9,8 +9,10 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons-vue": "^7.0.1",
|
"@ant-design/icons-vue": "^7.0.1",
|
||||||
|
"@types/big.js": "^7.0.0",
|
||||||
"@yp-component/root": "^0.0.48",
|
"@yp-component/root": "^0.0.48",
|
||||||
"ant-design-vue": "^4.0.0",
|
"ant-design-vue": "^4.0.0",
|
||||||
|
"big.js": "^7.0.1",
|
||||||
"china-area-data": "^5.0.1",
|
"china-area-data": "^5.0.1",
|
||||||
"dayjs": "^1.11.21",
|
"dayjs": "^1.11.21",
|
||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
@@ -1050,6 +1052,12 @@
|
|||||||
"url": "https://ko-fi.com/dangreen"
|
"url": "https://ko-fi.com/dangreen"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/big.js": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/big.js/-/big.js-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-WfAGp7IbJvyB8EmWK4tJD24rJRAL6uVbw3LV/hJntFNam+os9KWKj0PzXo8rRRpjupYK8U0M8FoBB8dBhWF2dg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.9",
|
"version": "1.0.9",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -1677,6 +1685,19 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/big.js": {
|
||||||
|
"version": "7.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/big.js/-/big.js-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-iFgV784tD8kq4ccF1xtNMZnXeZzVuXWWM+ERFzKQjv+A5G9HC8CY3DuV45vgzFFcW+u2tIvmF95+AzWgs6BjCg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/bigjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/body-parser": {
|
"node_modules/body-parser": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -18,8 +18,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons-vue": "^7.0.1",
|
"@ant-design/icons-vue": "^7.0.1",
|
||||||
|
"@types/big.js": "^7.0.0",
|
||||||
"@yp-component/root": "^0.0.48",
|
"@yp-component/root": "^0.0.48",
|
||||||
"ant-design-vue": "^4.0.0",
|
"ant-design-vue": "^4.0.0",
|
||||||
|
"big.js": "^7.0.1",
|
||||||
"china-area-data": "^5.0.1",
|
"china-area-data": "^5.0.1",
|
||||||
"dayjs": "^1.11.21",
|
"dayjs": "^1.11.21",
|
||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { defineComponent, computed, onMounted } from 'vue';
|
import { defineComponent, computed } from 'vue';
|
||||||
import { Modal, Table, Button, Pagination, Image, Space, Spin } from 'ant-design-vue';
|
import { Modal, Table, Button, Pagination, Image, Space, Spin } from 'ant-design-vue';
|
||||||
import { StatusTag, type StatusTagTone } from '@/components';
|
import { StatusTag, type StatusTagTone } from '@/components';
|
||||||
import { useState } from '@/hooks';
|
import { useState, useEffect } from '@/hooks';
|
||||||
import {
|
import {
|
||||||
getOrderDetail,
|
getOrderDetail,
|
||||||
getOrderPlayerList,
|
getOrderPlayerList,
|
||||||
@@ -19,7 +19,6 @@ interface OrderDetailModalProps {
|
|||||||
onReRefund: () => void;
|
onReRefund: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 选手表格列(对齐 TournamentAdminOrderInfoPageVO) =====
|
|
||||||
const GENDER_MAP: Record<string, string> = { '1': '男', '2': '女' };
|
const GENDER_MAP: Record<string, string> = { '1': '男', '2': '女' };
|
||||||
const formatGender = (v: string) => GENDER_MAP[v] ?? v;
|
const formatGender = (v: string) => GENDER_MAP[v] ?? v;
|
||||||
const formatMoney = (v: string) => {
|
const formatMoney = (v: string) => {
|
||||||
@@ -117,19 +116,18 @@ export default defineComponent({
|
|||||||
visible: { type: Boolean, default: false },
|
visible: { type: Boolean, default: false },
|
||||||
orderNo: { type: String, default: '' },
|
orderNo: { type: String, default: '' },
|
||||||
uniqueId: { type: String, default: '' },
|
uniqueId: { type: String, default: '' },
|
||||||
onClose: { type: Function, required: true },
|
onClose: { type: Function, default: null },
|
||||||
onReRefund: { type: Function, required: true },
|
onReRefund: { type: Function, default: null },
|
||||||
},
|
},
|
||||||
setup(props: OrderDetailModalProps) {
|
setup(props: OrderDetailModalProps) {
|
||||||
// ===== 数据状态 =====
|
|
||||||
const [detail, setDetail] = useState<TournamentAdminOrderInfoVO | null>(null);
|
const [detail, setDetail] = useState<TournamentAdminOrderInfoVO | null>(null);
|
||||||
const [detailLoading, setDetailLoading] = useState(true);
|
const [detailLoading, setDetailLoading] = useState(true);
|
||||||
|
|
||||||
// ===== 选手列表分页状态 =====
|
|
||||||
const [players, setPlayers] = useState<TournamentAdminOrderInfoPageVO[]>([]);
|
const [players, setPlayers] = useState<TournamentAdminOrderInfoPageVO[]>([]);
|
||||||
const [playersLoading, setPlayersLoading] = useState(true);
|
const [playersLoading, setPlayersLoading] = useState(true);
|
||||||
const [playerPage, setPlayerPage] = useState(1);
|
const [playerPage, setPlayerPage] = useState(1);
|
||||||
const [playerTotal, setPlayerTotal] = useState(0);
|
const [playerTotal, setPlayerTotal] = useState(0);
|
||||||
|
const [refundPage, setRefundPage] = useState(1);
|
||||||
|
const [refundPageSize] = useState(2);
|
||||||
const playerPageSize = 5;
|
const playerPageSize = 5;
|
||||||
|
|
||||||
/** 获取选手列表(分页) */
|
/** 获取选手列表(分页) */
|
||||||
@@ -152,12 +150,18 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ===== 退款记录分页 =====
|
|
||||||
const [refundPage, setRefundPage] = useState(1);
|
|
||||||
const [refundPageSize] = useState(2);
|
|
||||||
|
|
||||||
const refundRecords = computed<InnerRefundInfo[]>(() => detail.value?.refundInfoList || []);
|
const refundRecords = computed<InnerRefundInfo[]>(() => detail.value?.refundInfoList || []);
|
||||||
const hasRefund = computed(() => refundRecords.value.length > 0);
|
const hasRefund = computed(() => refundRecords.value.length > 0);
|
||||||
|
const hasRefundFailed = computed(() => {
|
||||||
|
const records = refundRecords.value;
|
||||||
|
if (!records || !records.length) return false;
|
||||||
|
return records.some((r) => {
|
||||||
|
if (!r) return false;
|
||||||
|
const hasRefundAmount = parseFloat(r.refundAmount || '0') > 0;
|
||||||
|
const hasRefundTime = r.refundTime && r.refundTime !== '-';
|
||||||
|
return hasRefundAmount && !hasRefundTime;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const pagedRefunds = computed(() => {
|
const pagedRefunds = computed(() => {
|
||||||
const start = (refundPage.value - 1) * refundPageSize.value;
|
const start = (refundPage.value - 1) * refundPageSize.value;
|
||||||
@@ -170,8 +174,7 @@ export default defineComponent({
|
|||||||
return records.reduce((sum, r) => sum + parseFloat(r.refundAmount || '0'), 0);
|
return records.reduce((sum, r) => sum + parseFloat(r.refundAmount || '0'), 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===== 初始化:获取详情 + 选手列表第一页 =====
|
useEffect(async () => {
|
||||||
onMounted(async () => {
|
|
||||||
const params = { orderNo: props.orderNo, uniqueId: props.uniqueId };
|
const params = { orderNo: props.orderNo, uniqueId: props.uniqueId };
|
||||||
|
|
||||||
const [detailRes] = await Promise.all([getOrderDetail(params), fetchPlayers(1)]);
|
const [detailRes] = await Promise.all([getOrderDetail(params), fetchPlayers(1)]);
|
||||||
@@ -180,7 +183,7 @@ export default defineComponent({
|
|||||||
setDetail(detailRes.data);
|
setDetail(detailRes.data);
|
||||||
}
|
}
|
||||||
setDetailLoading(false);
|
setDetailLoading(false);
|
||||||
});
|
}, []);
|
||||||
|
|
||||||
const handlePlayerPageChange = (page: number) => {
|
const handlePlayerPageChange = (page: number) => {
|
||||||
fetchPlayers(page);
|
fetchPlayers(page);
|
||||||
@@ -202,7 +205,6 @@ export default defineComponent({
|
|||||||
wrapClassName={styles.orderModalWrap}
|
wrapClassName={styles.orderModalWrap}
|
||||||
>
|
>
|
||||||
<div class={styles.orderDetailModalMain}>
|
<div class={styles.orderDetailModalMain}>
|
||||||
{/* ===== 自定义标题栏 ===== */}
|
|
||||||
<div class={styles.modalHeader}>
|
<div class={styles.modalHeader}>
|
||||||
<span class={styles.modalTitle}>订单信息</span>
|
<span class={styles.modalTitle}>订单信息</span>
|
||||||
<button class={styles.closeBtn} onClick={props.onClose} aria-label="关闭">
|
<button class={styles.closeBtn} onClick={props.onClose} aria-label="关闭">
|
||||||
@@ -216,7 +218,6 @@ export default defineComponent({
|
|||||||
</div>
|
</div>
|
||||||
) : detail.value ? (
|
) : detail.value ? (
|
||||||
<div class={styles.modalBody}>
|
<div class={styles.modalBody}>
|
||||||
{/* ===== 订单信息 ===== */}
|
|
||||||
<div class={styles.infoGrid}>
|
<div class={styles.infoGrid}>
|
||||||
<div class={styles.infoItem}>
|
<div class={styles.infoItem}>
|
||||||
<span class={styles.infoLabel}>赛事名称:</span>
|
<span class={styles.infoLabel}>赛事名称:</span>
|
||||||
@@ -252,7 +253,6 @@ export default defineComponent({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ===== 选手信息 ===== */}
|
|
||||||
<div class={styles.section}>
|
<div class={styles.section}>
|
||||||
<div class={styles.sectionTitle}>选手信息</div>
|
<div class={styles.sectionTitle}>选手信息</div>
|
||||||
<Table
|
<Table
|
||||||
@@ -276,7 +276,6 @@ export default defineComponent({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ===== 退款记录 ===== */}
|
|
||||||
<div class={styles.section}>
|
<div class={styles.section}>
|
||||||
<div class={styles.refundHeader}>
|
<div class={styles.refundHeader}>
|
||||||
<span class={styles.sectionTitle}>退款记录</span>
|
<span class={styles.sectionTitle}>退款记录</span>
|
||||||
@@ -354,10 +353,10 @@ export default defineComponent({
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{/* ===== 底部按钮 ===== */}
|
|
||||||
<div class={styles.modalFooter}>
|
<div class={styles.modalFooter}>
|
||||||
<Button onClick={props.onClose}>取消</Button>
|
<Button onClick={props.onClose}>取消</Button>
|
||||||
{hasRefund.value ? (
|
{/* 只有存在退款失败记录时才显示重新退款按钮 */}
|
||||||
|
{hasRefundFailed.value ? (
|
||||||
<Button danger onClick={props.onReRefund}>
|
<Button danger onClick={props.onReRefund}>
|
||||||
重新退款
|
重新退款
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -80,6 +80,8 @@ function renderBodyCell({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (column.key === 'action') {
|
if (column.key === 'action') {
|
||||||
|
// 退款状态:1=无退款,2=退款成功,3=退款失败,-1=其他
|
||||||
|
// 只有退款失败(3)时显示重新退款按钮
|
||||||
const showReRefund = record.refundStatus === 3;
|
const showReRefund = record.refundStatus === 3;
|
||||||
return (
|
return (
|
||||||
<Space>
|
<Space>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { computed, reactive, toRef, Ref, h } from 'vue';
|
import { computed, reactive, toRef, Ref, h } from 'vue';
|
||||||
|
import Big from 'big.js';
|
||||||
import { StatusTag, type StatusTagTone } from '@/components';
|
import { StatusTag, type StatusTagTone } from '@/components';
|
||||||
import { useState, useDebounce } from '@/hooks';
|
import { useState, useDebounce } from '@/hooks';
|
||||||
import { useRequest } from '@/hooks/useRequest';
|
import { useRequest } from '@/hooks/useRequest';
|
||||||
@@ -76,7 +77,6 @@ export function useOrderModel() {
|
|||||||
return { page: String(page), limit: String(pageSize), ...params } as OrderListQueryParams;
|
return { page: String(page), limit: String(pageSize), ...params } as OrderListQueryParams;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ===== 列表数据请求 =====
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
loading,
|
loading,
|
||||||
@@ -86,7 +86,6 @@ export function useOrderModel() {
|
|||||||
{ refreshDeps: [], formatResult: (res) => res },
|
{ refreshDeps: [], formatResult: (res) => res },
|
||||||
);
|
);
|
||||||
|
|
||||||
// 提取列表数据
|
|
||||||
const listData = computed<PageData<TournamentAdminOrderPageVO> | undefined>(() => {
|
const listData = computed<PageData<TournamentAdminOrderPageVO> | undefined>(() => {
|
||||||
const res = data.value;
|
const res = data.value;
|
||||||
return res ? (res as any).data : undefined;
|
return res ? (res as any).data : undefined;
|
||||||
@@ -94,20 +93,30 @@ export function useOrderModel() {
|
|||||||
|
|
||||||
const dataSource = computed(() => listData.value?.list || []);
|
const dataSource = computed(() => listData.value?.list || []);
|
||||||
|
|
||||||
// 同步分页总条数
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const total = (data.value as any)?.data?.total;
|
const total = (data.value as any)?.data?.total;
|
||||||
if (total !== undefined) pagination.setTotal(total);
|
if (total !== undefined) pagination.setTotal(total);
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
// ===== 金额汇总(TODO: 汇总应来自独立接口) =====
|
const summary = computed(() => {
|
||||||
const [summary] = useState({
|
const list = listData.value?.list || [];
|
||||||
totalOrderAmount: 0,
|
let totalOrder = new Big(0);
|
||||||
totalPaidAmount: 0,
|
let totalPaid = new Big(0);
|
||||||
totalRefundAmount: 0,
|
let totalRefund = new Big(0);
|
||||||
|
|
||||||
|
for (const item of list) {
|
||||||
|
totalOrder = totalOrder.plus(new Big(item.creatorSignupFee || 0));
|
||||||
|
totalPaid = totalPaid.plus(new Big(item.creatorSignupFeeActual || 0));
|
||||||
|
totalRefund = totalRefund.plus(new Big(item.creatorSignupFeeRefund || 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalOrderAmount: totalOrder.toNumber(),
|
||||||
|
totalPaidAmount: totalPaid.toNumber(),
|
||||||
|
totalRefundAmount: totalRefund.toNumber(),
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===== 计算属性 =====
|
|
||||||
const hasFilter = computed(() => {
|
const hasFilter = computed(() => {
|
||||||
const f = filterForm;
|
const f = filterForm;
|
||||||
return (
|
return (
|
||||||
@@ -120,7 +129,6 @@ export function useOrderModel() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ===== 表格列配置 =====
|
|
||||||
const formatMoney = (text: string) => {
|
const formatMoney = (text: string) => {
|
||||||
const n = parseFloat(text);
|
const n = parseFloat(text);
|
||||||
return isNaN(n) ? '0.00元' : `${n.toFixed(2)}元`;
|
return isNaN(n) ? '0.00元' : `${n.toFixed(2)}元`;
|
||||||
@@ -202,7 +210,6 @@ export function useOrderModel() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// ===== 列表操作 =====
|
|
||||||
const handleSearch = () => fetchList();
|
const handleSearch = () => fetchList();
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
@@ -220,7 +227,6 @@ export function useOrderModel() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// 数据
|
|
||||||
filterForm,
|
filterForm,
|
||||||
loading,
|
loading,
|
||||||
dataSource,
|
dataSource,
|
||||||
@@ -228,15 +234,12 @@ export function useOrderModel() {
|
|||||||
summary,
|
summary,
|
||||||
pagination,
|
pagination,
|
||||||
hasFilter,
|
hasFilter,
|
||||||
// 方法
|
|
||||||
handleSearch,
|
handleSearch,
|
||||||
handleReset,
|
handleReset,
|
||||||
handlePageChange,
|
handlePageChange,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 工具函数 =====
|
|
||||||
|
|
||||||
function hasValue(v: any): boolean {
|
function hasValue(v: any): boolean {
|
||||||
if (v == null) return false;
|
if (v == null) return false;
|
||||||
if (Array.isArray(v)) return v.length > 0 && v.some((item) => item != null);
|
if (Array.isArray(v)) return v.length > 0 && v.some((item) => item != null);
|
||||||
|
|||||||
Reference in New Issue
Block a user