feat: refine member and trade management

This commit is contained in:
richjohnrambo
2026-10-18 18:11:34 +08:00
parent 2b3d855bcc
commit 48f304212e
12 changed files with 1079 additions and 70 deletions
+62 -15
View File
@@ -2,7 +2,7 @@
<div>
<div class="n-layout-page-header">
<n-card :bordered="false" title="会员列表"
>会员资料默认脱敏展示;余额、禁用与奖励均通过审批流程处理。</n-card
>会员资料默认脱敏展示;余额修改会自动保留账变流水。</n-card
>
</div>
<n-card :bordered="false" class="proCard">
@@ -45,15 +45,9 @@
:model="editForm"
:rules="editRules"
label-placement="left"
label-width="100"
label-width="88"
>
<n-form-item label="标记">
<n-checkbox
:checked="editForm.isTestAccount === 1"
@update:checked="editForm.isTestAccount = $event ? 1 : 2"
>标记</n-checkbox
>
</n-form-item>
<div class="edit-form-grid">
<n-form-item label="用户名称" path="username" required
><n-input v-model:value="editForm.username" placeholder="请输入用户名称"
/></n-form-item>
@@ -62,8 +56,13 @@
v-model:value="editForm.mobile"
:placeholder="editForm.currentMobile || '留空不修改'"
/></n-form-item>
<n-form-item label="账户余额"
><n-input :value="money(editForm.availableAmount)" disabled
<n-form-item label="账户余额" path="availableAmountYuan"
><n-input-number
v-model:value="editForm.availableAmountYuan"
:min="0"
:precision="2"
class="form-number"
placeholder="请输入账户余额"
/></n-form-item>
<n-form-item label="冻结金额"
><n-input :value="money(editForm.frozenAmount)" disabled
@@ -108,6 +107,17 @@
:max="1000"
class="form-number"
/></n-form-item>
<n-form-item class="edit-mark-item">
<n-alert type="warning" :show-icon="true" class="edit-mark-alert">
<n-checkbox
:checked="editForm.isTestAccount === 1"
@update:checked="editForm.isTestAccount = $event ? 1 : 2"
>标记为假人账号</n-checkbox
>
<div class="edit-mark-hint">标记后该会员将按模拟账号处理,请确认后再保存。</div>
</n-alert>
</n-form-item>
</div>
</n-form>
</n-spin>
<template #action
@@ -476,12 +486,19 @@
validator: () => true,
trigger: ['input', 'blur'],
},
availableAmountYuan: {
validator: (_rule, value) =>
value !== null && Number.isFinite(Number(value)) && Number(value) >= 0
? true
: new Error('账户余额不能为空或小于0'),
trigger: ['input', 'blur'],
},
password: {
validator: (_rule, value) => {
const text = String(value || '');
if (!text) return true;
if (text.length < 8 || !/[A-Za-z]/.test(text) || !/\d/.test(text))
return new Error('密码至少8位且必须同时包含字母和数字');
if (text.length < 6 || text.length > 64)
return new Error('密码长度为6到64个字符');
return true;
},
trigger: ['input', 'blur'],
@@ -627,6 +644,7 @@
memberCategory: row?.memberCategory || 1,
isTestAccount: row?.isTestAccount || 2,
availableAmount: row?.availableAmount || 0,
availableAmountYuan: (row?.availableAmount || 0) / 100,
frozenAmount: row?.frozenAmount || 0,
});
showEdit.value = true;
@@ -638,6 +656,7 @@
riskStatus: data.riskStatus || 1,
creditScore: data.creditScore ?? 100,
availableAmount: data.availableAmount || 0,
availableAmountYuan: (data.availableAmount || 0) / 100,
frozenAmount: data.frozenAmount || 0,
currentMobile: data.mobile || row.mobile,
});
@@ -654,7 +673,10 @@
saving.value = true;
try {
editForm.memberCategory = editForm.isTestAccount === 1 ? 2 : 1;
await Edit(editForm);
await Edit({
...editForm,
availableAmount: Math.round(Number(editForm.availableAmountYuan) * 100),
});
message.success('保存成功');
showEdit.value = false;
reloadTable();
@@ -1090,7 +1112,7 @@
max-width: 94vw;
}
:global(.edit-modal) {
width: 720px;
width: 700px;
max-width: 92vw;
}
:global(.related-modal) {
@@ -1100,6 +1122,23 @@
.form-number {
width: 100%;
}
.edit-form-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
column-gap: 20px;
}
.edit-mark-item {
grid-column: 1 / -1;
margin-bottom: 0;
}
.edit-mark-alert {
width: 100%;
}
.edit-mark-hint {
margin-top: 4px;
color: #d97706;
font-size: 12px;
}
.upload-title {
margin-bottom: 8px;
color: var(--n-label-text-color);
@@ -1160,4 +1199,12 @@
.action-column-scrollbar::-webkit-scrollbar-thumb:hover {
background: #8c94a1;
}
@media (max-width: 680px) {
.edit-form-grid {
grid-template-columns: minmax(0, 1fr);
}
.edit-mark-item {
grid-column: auto;
}
}
</style>
+1 -1
View File
@@ -114,7 +114,7 @@
show.value = true;
productLoading.value = true;
try {
const result: any = await ProductList({ page: 1, perPage: 500 });
const result: any = await ProductList({ page: 1, perPage: 200 });
productOptions.value = (result.list || []).map((item) => ({
label: `${item.name}(${item.productNo})`,
value: item.id,
+7 -7
View File
@@ -22,6 +22,13 @@ const yuan = (value) => `¥${((value || 0) / 100).toFixed(2)}`;
export const columns = [
{ title: '订单号', key: 'rechargeNo', width: 210 },
{ title: '真实姓名', key: 'realNameMask', width: 110 },
{
title: '备注',
key: 'reviewReason',
width: 180,
ellipsis: { tooltip: true },
render: (row) => row.reviewReason || row.remark || dict.getLabel('rechargeStatus', row.rechargeStatus) || '-',
},
{ title: '手机号', key: 'mobile', width: 130 },
{ title: '用户余额', key: 'availableAmount', width: 120, render: (row) => yuan(row.availableAmount) },
{ title: '交易金额', key: 'requestAmount', width: 120, render: (row) => yuan(row.requestAmount) },
@@ -35,13 +42,6 @@ export const columns = [
{ title: '支付方式', key: 'channelName', width: 120 },
{ title: '类型', key: 'rechargeType', width: 100, render: (row) => tag('rechargeType', row.rechargeType) },
{ title: '处理时间', key: 'reviewedAt', width: 180 },
{
title: '备注',
key: 'reviewReason',
width: 180,
ellipsis: { tooltip: true },
render: (row) => row.reviewReason || row.remark || dict.getLabel('rechargeStatus', row.rechargeStatus) || '-',
},
];
export const loadOptions = () =>