fix: 更新提现审核相关功能 更新账务报表页面功能
This commit is contained in:
@@ -377,7 +377,7 @@ export default defineComponent({
|
||||
|
||||
try {
|
||||
const file = base64ToFile(dataUrl, `banner_${Date.now()}.jpg`);
|
||||
const result = await uploadFile(file, OssUploadType.Banner as any);
|
||||
const result = await uploadFile(file, OssUploadType.EventCover);
|
||||
|
||||
URL.revokeObjectURL(localUrl);
|
||||
formData.imageUrl = result.url;
|
||||
|
||||
@@ -123,4 +123,10 @@
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
}
|
||||
|
||||
.detailTableWrap {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { defineComponent, ref, nextTick } from 'vue';
|
||||
import { Select, Table } from 'ant-design-vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { useReportsModel } from './model/useReportsModel';
|
||||
import { useEffect } from '@/hooks';
|
||||
import { useEffect, useContainerSize } from '@/hooks';
|
||||
import styles from './index.module.less';
|
||||
|
||||
/**
|
||||
@@ -11,7 +11,7 @@ import styles from './index.module.less';
|
||||
* 页面结构:
|
||||
* 1. 顶部标题 + 时间范围选择
|
||||
* 2. 三张统计卡(总收入 / 总提现 / 平台余额)
|
||||
* 3. 月度收支趋势(近 6 个月,echarts 柱状 + 折线组合图)
|
||||
* 3. 月度收支趋势(echarts 柱状 + 折线组合图)
|
||||
* 4. 月度收支明细(表格)
|
||||
*/
|
||||
export default defineComponent({
|
||||
@@ -33,22 +33,23 @@ export default defineComponent({
|
||||
let chartInstance: any = null;
|
||||
|
||||
const getChartOption = () => {
|
||||
const months = chartData.value.map((d) => d.month);
|
||||
const order = chartData.value.map((d) => d.order);
|
||||
const netIncome = chartData.value.map((d) => d.netIncome);
|
||||
const data = chartData.value;
|
||||
const months = data.map((d) => d.month);
|
||||
const orderAmount = data.map((d) => d.order);
|
||||
const refundAmount = data.map((d) => d.refund);
|
||||
const withdrawAmount = data.map((d) => d.withdraw);
|
||||
const netIncome = data.map((d) => d.netIncome);
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
crossStyle: { color: 'rgba(0, 0, 0, 0.15)' },
|
||||
label: { backgroundColor: '#6a7985' },
|
||||
},
|
||||
axisPointer: { type: 'shadow' },
|
||||
valueFormatter: (v: number) => `¥${formatMoney(v)}`,
|
||||
},
|
||||
legend: {
|
||||
data: [
|
||||
{ name: '订单金额', icon: 'roundRect' },
|
||||
{ name: '退款金额', icon: 'roundRect' },
|
||||
{ name: '提现金额', icon: 'roundRect' },
|
||||
{ name: '净收入', icon: 'circle' },
|
||||
],
|
||||
bottom: 8,
|
||||
@@ -63,7 +64,6 @@ export default defineComponent({
|
||||
axisTick: { show: false },
|
||||
axisLine: { lineStyle: { color: '#e8e8e8' } },
|
||||
axisLabel: { color: 'rgba(0, 0, 0, 0.55)' },
|
||||
axisPointer: { type: 'shadow' },
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
@@ -77,23 +77,28 @@ export default defineComponent({
|
||||
{
|
||||
name: '订单金额',
|
||||
type: 'bar',
|
||||
data: order,
|
||||
barMaxWidth: 48,
|
||||
barCategoryGap: '35%',
|
||||
itemStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{ offset: 0, color: '#4096ff' },
|
||||
{ offset: 1, color: '#1677ff' },
|
||||
],
|
||||
},
|
||||
borderRadius: [4, 4, 0, 0],
|
||||
},
|
||||
stack: 'amount',
|
||||
data: orderAmount,
|
||||
barMaxWidth: 40,
|
||||
itemStyle: { color: '#4096ff' },
|
||||
emphasis: { focus: 'series' },
|
||||
},
|
||||
{
|
||||
name: '退款金额',
|
||||
type: 'bar',
|
||||
stack: 'amount',
|
||||
data: refundAmount,
|
||||
barMaxWidth: 40,
|
||||
itemStyle: { color: '#ff7875' },
|
||||
emphasis: { focus: 'series' },
|
||||
},
|
||||
{
|
||||
name: '提现金额',
|
||||
type: 'bar',
|
||||
stack: 'amount',
|
||||
data: withdrawAmount,
|
||||
barMaxWidth: 40,
|
||||
itemStyle: { color: '#ffa940' },
|
||||
emphasis: { focus: 'series' },
|
||||
},
|
||||
{
|
||||
@@ -154,48 +159,59 @@ export default defineComponent({
|
||||
};
|
||||
}, [() => chartRef.value]);
|
||||
|
||||
// 时间范围切换时刷新图表
|
||||
// 数据更新时刷新图表
|
||||
useEffect(() => {
|
||||
renderChart();
|
||||
}, [range]);
|
||||
}, [chartData]);
|
||||
|
||||
// ===== 明细表容器高度 =====
|
||||
const { containerRef: detailContainerRef, height: detailTableHeight } = useContainerSize({
|
||||
headerOffset: 56,
|
||||
});
|
||||
|
||||
return () => {
|
||||
/** 根据增长率构建箭头 + 颜色(正值 ↑ 绿,负值 ↓ 红,null 时固定文本) */
|
||||
const changeDesc = (rate: number | null) => {
|
||||
if (rate == null) return { desc: '= 总收入 - 提现金额', descColor: 'rgba(0, 0, 0, 0.55)' };
|
||||
const arrow = rate > 0 ? '↑' : '↓';
|
||||
/** 根据增长率构建箭头 + 颜色 */
|
||||
const changeDesc = (rateStr: string | undefined, fallbackRate: number | null) => {
|
||||
if (rateStr === undefined && fallbackRate !== null) rateStr = String(fallbackRate);
|
||||
if (rateStr == null || rateStr === '' || isNaN(parseFloat(rateStr)))
|
||||
return { desc: '= 总收入 - 提现金额', descColor: 'rgba(0, 0, 0, 0.55)' };
|
||||
const rate = parseFloat(rateStr);
|
||||
const arrow = rate > 0 ? '↑' : rate < 0 ? '↓' : '';
|
||||
const sign = rate > 0 ? '+' : '';
|
||||
const absRate = Math.abs(rate).toFixed(2);
|
||||
return {
|
||||
desc: `${arrow} 较上月 ${sign}${rate}%`,
|
||||
descColor: rate > 0 ? '#52c41a' : '#ff4d4f',
|
||||
desc: rate === 0 ? `→ 与上月持平` : `${arrow} 较上月 ${sign}${absRate}%`,
|
||||
descColor: rate > 0 ? '#52c41a' : rate < 0 ? '#ff4d4f' : 'rgba(0, 0, 0, 0.55)',
|
||||
};
|
||||
};
|
||||
|
||||
const s = summary.value;
|
||||
|
||||
/** 顶部 3 张统计卡配置 */
|
||||
const statCards = [
|
||||
{
|
||||
key: 'totalIncome',
|
||||
label: '总收入(扣除退款后)',
|
||||
value: summary.value.totalIncome,
|
||||
value: s.totalIncome,
|
||||
bgColor: '#f6ffed',
|
||||
accentColor: '#52c41a',
|
||||
...changeDesc(12.5),
|
||||
...changeDesc(s.totalIncomeTendency, null),
|
||||
},
|
||||
{
|
||||
key: 'totalWithdraw',
|
||||
label: '总提现金额',
|
||||
value: summary.value.totalWithdraw,
|
||||
value: s.totalWithdraw,
|
||||
bgColor: '#fffbe6',
|
||||
accentColor: '#faad14',
|
||||
...changeDesc(32),
|
||||
...changeDesc(s.totalWithdrawTendency, null),
|
||||
},
|
||||
{
|
||||
key: 'totalBalance',
|
||||
label: '平台余额(所有用户余额总和)',
|
||||
value: summary.value.totalBalance,
|
||||
value: s.totalBalance,
|
||||
bgColor: '#e6f4ff',
|
||||
accentColor: '#1677ff',
|
||||
...changeDesc(null),
|
||||
...changeDesc(undefined, null),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -236,7 +252,7 @@ export default defineComponent({
|
||||
<div class={styles.chartCard}>
|
||||
<div class={styles.cardTitle}>
|
||||
<span class={styles.cardTitleIcon}>📊</span>
|
||||
月度收支趋势(近 6 个月)
|
||||
月度收支趋势(近 {chartData.value.length} 个月)
|
||||
</div>
|
||||
<div
|
||||
ref={(el: any) => {
|
||||
@@ -249,14 +265,20 @@ export default defineComponent({
|
||||
{/* ===== 月度收支明细 ===== */}
|
||||
<div class={styles.detailCard}>
|
||||
<div class={styles.cardTitle}>月度收支明细</div>
|
||||
<Table
|
||||
columns={detailColumns}
|
||||
dataSource={detailData.value}
|
||||
pagination={false}
|
||||
size="middle"
|
||||
bordered={false}
|
||||
rowKey="key"
|
||||
/>
|
||||
<div ref={detailContainerRef} class={styles.detailTableWrap}>
|
||||
<Table
|
||||
columns={detailColumns}
|
||||
dataSource={detailData.value}
|
||||
pagination={false}
|
||||
size="middle"
|
||||
bordered={false}
|
||||
rowKey="key"
|
||||
scroll={{
|
||||
y: detailTableHeight.value,
|
||||
x: 'max-content',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 财务报表 - 接口服务
|
||||
*/
|
||||
import { get } from '@/utils/request';
|
||||
import type {
|
||||
ReportQueryParams,
|
||||
FinanceReportVO,
|
||||
ReportMonthItem,
|
||||
ApiResult,
|
||||
} from '@/api/reports/types';
|
||||
|
||||
export type {
|
||||
ReportQueryParams,
|
||||
FinanceReportVO,
|
||||
ReportMonthItem,
|
||||
ApiResult,
|
||||
} from '@/api/reports/types';
|
||||
|
||||
// ============================================================
|
||||
// URL 常量
|
||||
// ============================================================
|
||||
const reportList = '/admin/finance/report/list';
|
||||
|
||||
// ============================================================
|
||||
// API 函数
|
||||
// ============================================================
|
||||
|
||||
/** 财务报表 — GET /admin/finance/report/list?date=yyyy-MM */
|
||||
export function getFinanceReport(params: ReportQueryParams): Promise<ApiResult<FinanceReportVO>> {
|
||||
return get(reportList, params as Record<string, any>);
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
import { ref } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import type { SelectValue } from 'ant-design-vue/es/select';
|
||||
import { useState } from '@/hooks';
|
||||
import { useRequest } from '@/hooks/useRequest';
|
||||
import { useEffect } from '@/hooks/useEffect';
|
||||
import { getFinanceReport, type FinanceReportVO } from './services';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export type RangeValue = (typeof RANGE_OPTIONS)[number]['value'];
|
||||
|
||||
@@ -10,85 +13,44 @@ export type RangeValue = (typeof RANGE_OPTIONS)[number]['value'];
|
||||
|
||||
/** 时间范围选项 */
|
||||
export const RANGE_OPTIONS = [
|
||||
{ value: 'month', label: '本月' },
|
||||
{ value: '3m', label: '近 3 个月' },
|
||||
{ value: '6m', label: '近 6 个月' },
|
||||
{ value: 'year', label: '本年' },
|
||||
] as const;
|
||||
{ value: 'month' as const, label: '本月' },
|
||||
{ value: '3m' as const, label: '近 3 个月' },
|
||||
{ value: '6m' as const, label: '近 6 个月' },
|
||||
{ value: 'year' as const, label: '本年' },
|
||||
];
|
||||
|
||||
/** 范围对应的月数 */
|
||||
const RANGE_MONTHS: Record<RangeValue, number> = {
|
||||
month: 1,
|
||||
'3m': 3,
|
||||
'6m': 6,
|
||||
year: 12,
|
||||
};
|
||||
|
||||
function isRangeValue(val: SelectValue): val is RangeValue {
|
||||
return typeof val === 'string' && RANGE_OPTIONS.some((item) => item.value === val);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 假数据(数值自洽:总收入 - 总提现 = 平台余额)
|
||||
// ============================================================
|
||||
|
||||
/** 6 个月图表数据(月份标签 + 订单金额 + 净收入) */
|
||||
const CHART_DATA = [
|
||||
{ month: '01月', order: 22500, netIncome: 19000, withdraw: 300 },
|
||||
{ month: '02月', order: 24500, netIncome: 21500, withdraw: 1850 },
|
||||
{ month: '03月', order: 24800, netIncome: 23200, withdraw: 1400 },
|
||||
{ month: '04月', order: 26100, netIncome: 24900, withdraw: 1500 },
|
||||
{ month: '05月', order: 28900, netIncome: 27400, withdraw: 1800 },
|
||||
{ month: '06月', order: 32500, netIncome: 31860, withdraw: 2100 },
|
||||
];
|
||||
|
||||
/** 月度收支明细(最近 4 个月) */
|
||||
const DETAIL_DATA = [
|
||||
{
|
||||
key: '2026-06',
|
||||
month: '2026-06',
|
||||
order: 32500,
|
||||
refund: 640,
|
||||
netIncome: 31860,
|
||||
withdraw: 2100,
|
||||
},
|
||||
{
|
||||
key: '2026-05',
|
||||
month: '2026-05',
|
||||
order: 28900,
|
||||
refund: 1500,
|
||||
netIncome: 27400,
|
||||
withdraw: 1800,
|
||||
},
|
||||
{
|
||||
key: '2026-04',
|
||||
month: '2026-04',
|
||||
order: 26100,
|
||||
refund: 1200,
|
||||
netIncome: 24900,
|
||||
withdraw: 1500,
|
||||
},
|
||||
{
|
||||
key: '2026-03',
|
||||
month: '2026-03',
|
||||
order: 24800,
|
||||
refund: 1600,
|
||||
netIncome: 23200,
|
||||
withdraw: 1400,
|
||||
},
|
||||
];
|
||||
|
||||
/** 顶部 3 张统计卡数据 */
|
||||
const SUMMARY = {
|
||||
totalIncome: 147860, // 总收入(净收入合计)
|
||||
totalWithdraw: 8950, // 总提现
|
||||
totalBalance: 138910, // 平台余额 = 总收入 - 总提现
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// 工具
|
||||
// ============================================================
|
||||
|
||||
/** 千分位 + 保留 2 位小数 */
|
||||
function formatMoney(n: number): string {
|
||||
return (n || 0).toLocaleString('zh-CN', {
|
||||
function formatMoneyStr(value: string): string {
|
||||
const n = parseFloat(value);
|
||||
if (isNaN(n)) return '0.00';
|
||||
return n.toLocaleString('zh-CN', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
});
|
||||
}
|
||||
|
||||
/** 字符串金额转数字 */
|
||||
function toNumber(value: string): number {
|
||||
const n = parseFloat(value);
|
||||
return isNaN(n) ? 0 : n;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Model
|
||||
// ============================================================
|
||||
@@ -98,22 +60,55 @@ function formatMoney(n: number): string {
|
||||
*/
|
||||
export function useReportsModel() {
|
||||
// ===== 时间范围 =====
|
||||
const [range, setRange] = useState<RangeValue>('month');
|
||||
const range = ref<RangeValue>('month');
|
||||
|
||||
/** 切换时间范围(演示用:打日志,不改数据) */
|
||||
const handleRangeChange = (val: SelectValue) => {
|
||||
if (!isRangeValue(val)) return;
|
||||
setRange(val);
|
||||
// TODO: 接入 API 时按 range 重新拉取汇总 / 图表 / 明细
|
||||
// ===== 构建 API 参数(根据 range 推算查询起始月份) =====
|
||||
const buildQueryParams = () => {
|
||||
const months = RANGE_MONTHS[range.value];
|
||||
const date = dayjs()
|
||||
.subtract(months - 1, 'month')
|
||||
.format('YYYY-MM');
|
||||
return { date };
|
||||
};
|
||||
|
||||
// ===== 请求(range 变化时自动重新请求) =====
|
||||
const {
|
||||
data,
|
||||
loading,
|
||||
run: fetchReport,
|
||||
} = useRequest<FinanceReportVO>(() => getFinanceReport(buildQueryParams()), {
|
||||
refreshDeps: [range],
|
||||
formatResult: (res) => (res.code == 200 ? res.data : null),
|
||||
});
|
||||
|
||||
// ===== 根据范围过滤后的月列表 =====
|
||||
const filteredMonthList = computed(() => {
|
||||
const list = data.value?.monthList || [];
|
||||
const count = RANGE_MONTHS[range.value];
|
||||
return list.slice(-count);
|
||||
});
|
||||
|
||||
// ===== 汇总 =====
|
||||
const summary = ref({ ...SUMMARY });
|
||||
const summary = computed(() => ({
|
||||
totalIncome: data.value?.totalIncome || '0.00',
|
||||
totalIncomeTendency: data.value?.totalIncomeTendency || '0',
|
||||
totalWithdraw: data.value?.totalWithdrawIncome || '0.00',
|
||||
totalWithdrawTendency: data.value?.totalWithdrawIncomeTendency || '0',
|
||||
totalBalance: data.value?.totalBalanceAmount || '0.00',
|
||||
}));
|
||||
|
||||
// ===== 图表 =====
|
||||
const chartData = ref([...CHART_DATA]);
|
||||
// ===== 图表数据 =====
|
||||
const chartData = computed(() =>
|
||||
filteredMonthList.value.map((item) => ({
|
||||
month: dayjs(item.month).format('MM月'),
|
||||
order: toNumber(item.orderAmount),
|
||||
refund: toNumber(item.refundAmount),
|
||||
netIncome: toNumber(item.netIncome),
|
||||
withdraw: toNumber(item.withdrawAmount),
|
||||
})),
|
||||
);
|
||||
|
||||
// ===== 明细表 =====
|
||||
// ===== 明细表列 =====
|
||||
const detailColumns = [
|
||||
{
|
||||
title: '月份',
|
||||
@@ -156,10 +151,39 @@ export function useReportsModel() {
|
||||
customCell: (_record: any) => ({ style: { color: '#fa8c16' } }),
|
||||
},
|
||||
];
|
||||
const detailData = ref([...DETAIL_DATA]);
|
||||
|
||||
// ===== 明细表数据 =====
|
||||
const detailData = computed(() =>
|
||||
filteredMonthList.value.map((item) => ({
|
||||
key: item.month,
|
||||
month: item.month,
|
||||
order: toNumber(item.orderAmount),
|
||||
refund: toNumber(item.refundAmount),
|
||||
netIncome: toNumber(item.netIncome),
|
||||
withdraw: toNumber(item.withdrawAmount),
|
||||
})),
|
||||
);
|
||||
|
||||
// ===== 切换时间范围 =====
|
||||
const handleRangeChange = (val: SelectValue) => {
|
||||
if (!isRangeValue(val)) return;
|
||||
range.value = val;
|
||||
};
|
||||
|
||||
// ===== 范围变化时刷新图表(通过 page 中的 useEffect 监听 range) =====
|
||||
|
||||
/** 千分位格式金额(页面渲染用) */
|
||||
function formatMoney(val: string | number): string {
|
||||
if (typeof val === 'string') return formatMoneyStr(val);
|
||||
return val.toLocaleString('zh-CN', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
range,
|
||||
loading,
|
||||
handleRangeChange,
|
||||
summary,
|
||||
chartData,
|
||||
|
||||
@@ -135,6 +135,11 @@
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.required {
|
||||
color: #ff4d4f;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { defineComponent, reactive, ref, watch } from 'vue';
|
||||
import { defineComponent, reactive, ref } from 'vue';
|
||||
import { useEffect } from '@/hooks';
|
||||
import { Modal, Radio, Input, Button, Upload, Image, Spin, message } from 'ant-design-vue';
|
||||
import type { UploadFile } from 'ant-design-vue';
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import { uploadFile, OssUploadType } from '@/utils/oss';
|
||||
import { getWithdrawInfo, postWithdrawAudit, type WithdrawInfoVO } from '../model/services';
|
||||
import { getAuditStatusText, getPayStatusText } from '../model/useWithdrawModel';
|
||||
import styles from './WithdrawDetailModal.module.less';
|
||||
|
||||
const { TextArea } = Input;
|
||||
const MAX_IMAGE_COUNT = 3;
|
||||
const MAX_IMAGE_SIZE = 5 * 1024 * 1024;
|
||||
|
||||
interface WithdrawDetailModalProps {
|
||||
visible: boolean;
|
||||
@@ -30,7 +35,7 @@ export default defineComponent({
|
||||
const detailLoading = ref(false);
|
||||
|
||||
const auditForm = reactive({ pass: true, remark: '' });
|
||||
const imageList = ref<any[]>([]);
|
||||
const imageList = ref<UploadFile[]>([]);
|
||||
const submitting = ref(false);
|
||||
|
||||
// ===== 拉取详情 =====
|
||||
@@ -47,44 +52,91 @@ export default defineComponent({
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
async (val) => {
|
||||
if (val && props.record?.id) {
|
||||
auditForm.pass = true;
|
||||
auditForm.remark = '';
|
||||
imageList.value = [];
|
||||
submitting.value = false;
|
||||
await fetchDetail(props.record.id);
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
// ===== 弹窗打开时拉取详情 =====
|
||||
useEffect(() => {
|
||||
if (props.visible && props.record?.id) {
|
||||
auditForm.pass = true;
|
||||
auditForm.remark = '';
|
||||
imageList.value = [];
|
||||
submitting.value = false;
|
||||
fetchDetail(props.record.id);
|
||||
}
|
||||
}, [() => props.visible]);
|
||||
|
||||
// ===== 上传 =====
|
||||
const beforeUpload = (file: any) => {
|
||||
if (file.size / 1024 / 1024 >= 5) {
|
||||
// ===== 图片上传(OSS) =====
|
||||
const beforeUpload = async (file: UploadFile) => {
|
||||
const rawFile = file as unknown as File;
|
||||
|
||||
if (imageList.value.length >= MAX_IMAGE_COUNT) {
|
||||
message.warning(`最多上传 ${MAX_IMAGE_COUNT} 张图片`);
|
||||
return false;
|
||||
}
|
||||
if (!/^image\//.test(rawFile.type)) {
|
||||
message.error('请选择图片文件');
|
||||
return false;
|
||||
}
|
||||
if (rawFile.size >= MAX_IMAGE_SIZE) {
|
||||
message.error('图片大小不能超过 5MB');
|
||||
return false;
|
||||
}
|
||||
imageList.value = [...imageList.value, file];
|
||||
|
||||
const uid = file.uid || `${Date.now()}-${Math.random()}`;
|
||||
const uploadingItem: UploadFile = {
|
||||
uid,
|
||||
name: rawFile.name,
|
||||
status: 'uploading',
|
||||
};
|
||||
imageList.value = [...imageList.value, uploadingItem];
|
||||
|
||||
try {
|
||||
const result = await uploadFile(rawFile, OssUploadType.WithdrawAudit);
|
||||
imageList.value = imageList.value.map((item) =>
|
||||
item.uid === uid
|
||||
? {
|
||||
...item,
|
||||
status: 'done',
|
||||
url: result.url,
|
||||
thumbUrl: result.url,
|
||||
}
|
||||
: item,
|
||||
);
|
||||
} catch (err: any) {
|
||||
imageList.value = imageList.value.filter((item) => item.uid !== uid);
|
||||
message.error(err?.message || '图片上传失败');
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
const handleRemove = (file: any) => {
|
||||
imageList.value = imageList.value.filter((f) => f.uid !== file.uid);
|
||||
|
||||
const handleRemove = (file: UploadFile) => {
|
||||
imageList.value = imageList.value.filter((item) => item.uid !== file.uid);
|
||||
};
|
||||
|
||||
// ===== 提交审核 =====
|
||||
const handleSubmit = async () => {
|
||||
if (!auditForm.remark.trim()) {
|
||||
message.warning('请输入审核内容');
|
||||
// 驳回:审核内容必填
|
||||
if (!auditForm.pass && !auditForm.remark.trim()) {
|
||||
message.warning('驳回时请填写审核内容');
|
||||
return;
|
||||
}
|
||||
// 通过:必须上传图片
|
||||
if (auditForm.pass) {
|
||||
const hasDoneImage = imageList.value.some((item) => item.status === 'done' && item.url);
|
||||
if (!hasDoneImage) {
|
||||
message.warning('审核通过时必须上传审核图片');
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (imageList.value.some((item) => item.status === 'uploading')) {
|
||||
message.warning('图片仍在上传中,请稍候');
|
||||
return;
|
||||
}
|
||||
|
||||
submitting.value = true;
|
||||
try {
|
||||
const checkImg = imageList.value
|
||||
.map((f) => f.name || f.url || '')
|
||||
.filter(Boolean)
|
||||
.filter((item) => item.status === 'done' && item.url)
|
||||
.map((item) => item.url as string)
|
||||
.join(',');
|
||||
const res = await postWithdrawAudit({
|
||||
id: String(props.record.id),
|
||||
@@ -117,7 +169,7 @@ export default defineComponent({
|
||||
const renderContent = () => {
|
||||
const d = detail.value!;
|
||||
const bankCardTypeLabel = d.bankCardType === 1 ? '个人' : d.bankCardType || '-';
|
||||
const isPending = d.auditStatus === 0;
|
||||
const isPending = d.auditStatus == 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -177,9 +229,12 @@ export default defineComponent({
|
||||
</Radio.Group>
|
||||
</div>
|
||||
<div class={styles.formRow}>
|
||||
<span class={[styles.formLabel, styles.formLabelTop]}>审核内容</span>
|
||||
<span class={[styles.formLabel, styles.formLabelTop]}>
|
||||
{!auditForm.pass && <span class={styles.required}>*</span>}
|
||||
审核内容
|
||||
</span>
|
||||
<TextArea
|
||||
v-model={auditForm.remark}
|
||||
v-model:value={auditForm.remark}
|
||||
placeholder="请输入审核内容"
|
||||
rows={4}
|
||||
maxlength={500}
|
||||
@@ -188,7 +243,10 @@ export default defineComponent({
|
||||
/>
|
||||
</div>
|
||||
<div class={styles.formRow}>
|
||||
<span class={[styles.formLabel, styles.formLabelTop]}>审核图片</span>
|
||||
<span class={[styles.formLabel, styles.formLabelTop]}>
|
||||
{auditForm.pass && <span class={styles.required}>*</span>}
|
||||
审核图片
|
||||
</span>
|
||||
<Upload
|
||||
listType="picture-card"
|
||||
fileList={imageList.value}
|
||||
@@ -197,10 +255,10 @@ export default defineComponent({
|
||||
accept="image/*"
|
||||
class={styles.uploader}
|
||||
>
|
||||
{imageList.value.length >= 3 ? null : (
|
||||
{imageList.value.length >= MAX_IMAGE_COUNT ? null : (
|
||||
<div class={styles.uploadTrigger}>
|
||||
<PlusOutlined />
|
||||
<div class={styles.uploadText}>粘贴图片到此处</div>
|
||||
<div class={styles.uploadText}>请选择图片</div>
|
||||
</div>
|
||||
)}
|
||||
</Upload>
|
||||
|
||||
@@ -81,7 +81,7 @@ export default defineComponent({
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 100,
|
||||
width: 140,
|
||||
fixed: 'right' as const,
|
||||
align: 'center' as const,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user