fix: resolve admin acceptance defects

This commit is contained in:
richjohnrambo
2026-10-16 10:14:41 +08:00
parent c6ec45cccf
commit 2b3d855bcc
39 changed files with 1079 additions and 204 deletions
+38 -32
View File
@@ -175,23 +175,32 @@
<n-spin :show="relatedLoading">
<n-form label-placement="left" label-width="110">
<template v-if="relatedType === 'payment'">
<n-alert v-if="paymentForm.id" type="info" :show-icon="false">
当前默认显示脱敏资料;留空保存不会覆盖原敏感字段或收款码。
<n-button
v-if="hasPermission(['/appMember/sensitive'])"
text
type="primary"
@click="openSensitive('payment', paymentForm.id)"
>授权查看明文</n-button>
</n-alert>
<n-form-item label="开户人"
><n-input v-model:value="paymentForm.holder" placeholder="请输入开户人"
><n-input v-model:value="paymentForm.holder" :placeholder="paymentForm.holderMask || '留空不修改'"
/></n-form-item>
<n-form-item label="身份证号"
><n-input v-model:value="paymentForm.identityNo" :placeholder="paymentForm.identityNoMask || '留空不修改'"
/></n-form-item>
<n-form-item label="开户银行"
><n-input v-model:value="paymentForm.bankName" placeholder="请输入开户银行"
/></n-form-item>
<n-form-item label="开户电话"
><n-input v-model:value="paymentForm.mobile" placeholder="请输入开户电话"
><n-input v-model:value="paymentForm.mobile" :placeholder="paymentForm.mobileMask || '留空不修改'"
/></n-form-item>
<n-form-item label="银行卡号"
><n-input v-model:value="paymentForm.bankAccount" placeholder="请输入银行卡号"
><n-input v-model:value="paymentForm.bankAccount" :placeholder="paymentForm.accountNoMask || '留空不修改'"
/></n-form-item>
<n-form-item label="数字钱包"
><n-input v-model:value="paymentForm.digitalWallet" placeholder="请输入数字钱包"
/></n-form-item>
<n-form-item label="云闪付账号"
><n-input v-model:value="paymentForm.unionPayAccount" placeholder="请输入云闪付账号"
><n-input v-model:value="paymentForm.digitalWallet" :placeholder="paymentForm.digitalRmbCodeMask || '留空不修改'"
/></n-form-item>
<n-form-item label="收款码">
<n-grid :cols="2" :x-gap="16">
@@ -743,14 +752,20 @@
memberId,
id: bank?.id || 0,
holder: '',
holderMask: bank?.holderMask || '',
identityNo: '',
identityNoMask: bank?.identityNoMask || '',
bankName: bank?.bankName || '',
mobile: '',
mobileMask: bank?.mobileMask || '',
bankAccount: '',
accountNoMask: bank?.accountNoMask || '',
digitalWallet: '',
unionPayAccount: '',
alipayQrCode: '',
wechatQrCode: '',
records: { 1: bank, 2: byType(2), 3: byType(3), 4: byType(4), 5: byType(5) },
digitalRmbCodeMask: bank?.digitalRmbCodeMask || '',
alipayQrCode: bank?.alipayQrUrl || '',
wechatQrCode: bank?.wechatQrUrl || '',
alipayQrAttachmentId: bank?.alipayQrAttachmentId || 0,
wechatQrAttachmentId: bank?.wechatQrAttachmentId || 0,
});
} else {
const row = relatedRows.value.find((item) => item.isDefault === 1) || relatedRows.value[0];
@@ -811,32 +826,23 @@
relatedSaving.value = true;
try {
if (relatedType.value === 'payment') {
const common = {
await PaymentEdit({
id: paymentForm.id,
memberId: paymentForm.memberId,
accountType: 1,
bankName: paymentForm.bankName,
holder: paymentForm.holder,
identityNo: paymentForm.identityNo,
mobile: paymentForm.mobile,
accountNo: paymentForm.bankAccount,
digitalRmbCode: paymentForm.digitalWallet,
alipayQrUrl: paymentForm.alipayQrCode,
wechatQrUrl: paymentForm.wechatQrCode,
alipayQrAttachmentId: paymentForm.alipayQrAttachmentId,
wechatQrAttachmentId: paymentForm.wechatQrAttachmentId,
verifyStatus: 1,
isDefault: 1,
};
const accounts = [
{ type: 1, value: paymentForm.bankAccount, bankName: paymentForm.bankName },
{ type: 2, value: paymentForm.digitalWallet, bankName: '' },
{ type: 3, value: paymentForm.unionPayAccount, bankName: '' },
{ type: 4, value: paymentForm.alipayQrCode, bankName: '' },
{ type: 5, value: paymentForm.wechatQrCode, bankName: '' },
];
for (const account of accounts) {
const old = paymentForm.records?.[account.type];
if (account.value || old?.id)
await PaymentEdit({
...common,
id: old?.id || 0,
accountType: account.type,
accountNo: account.value,
bankName: account.bankName,
isDefault: account.type === 1 ? 1 : 2,
});
}
});
} else {
const regionChanged = addressForm.region !== addressForm.originalRegion;
await AddressEdit({
+27 -2
View File
@@ -76,7 +76,7 @@
import { usePermission } from '@/hooks/web/usePermission';
import { useDictStore } from '@/store/modules/dict';
import { adaTableScrollX } from '@/utils/hotgo';
import { Delete, Edit, List, View } from '@/api/taskHall';
import { Delete, Edit, List, Status, View } from '@/api/taskHall';
import { columns, loadOptions } from './model';
const message = useMessage();
@@ -100,7 +100,7 @@
};
const actionColumn = reactive({
width: 180,
width: 260,
title: '操作',
key: 'action',
fixed: 'right',
@@ -114,6 +114,12 @@
auth: ['/taskHall/view'],
onClick: () => openEdit(row),
},
{
label: row.publishStatus === 3 ? '下线' : '发布',
type: row.publishStatus === 3 ? 'warning' : 'success',
auth: ['/taskHall/status'],
onClick: () => changeStatus(row),
},
{
label: '删除',
type: 'error',
@@ -237,6 +243,25 @@
});
}
function changeStatus(row) {
const publish = row.publishStatus !== 3;
dialog.warning({
title: publish ? '发布任务大厅' : '下线任务大厅',
content: `确定${publish ? '发布' : '下线'}“${row.name}”吗?`,
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
await Status({
id: row.id,
version: row.version,
publishStatus: publish ? 3 : 4,
});
message.success(`${publish ? '发布' : '下线'}成功`);
reload();
},
});
}
onMounted(loadOptions);
</script>
+14 -1
View File
@@ -1,3 +1,5 @@
import { h } from 'vue';
import { NTag } from 'naive-ui';
import { renderImage } from '@/utils';
import { useDictStore } from '@/store/modules/dict';
@@ -14,7 +16,18 @@ export const columns = [
render: (row) => `${((row.commissionRate || 0) / 100).toFixed(2)}%`,
},
{ title: '简介', key: 'summary', width: 260, ellipsis: { tooltip: true } },
{
title: '状态',
key: 'publishStatus',
width: 100,
render: (row) =>
h(
NTag,
{ type: dict.getType('bizPublishStatus', row.publishStatus), bordered: false },
{ default: () => dict.getLabel('bizPublishStatus', row.publishStatus) || '-' }
),
},
{ title: '创建时间', key: 'createdAt', width: 180 },
];
export const loadOptions = () => dict.loadOptions(['memberLevelOption']);
export const loadOptions = () => dict.loadOptions(['memberLevelOption', 'bizPublishStatus']);
@@ -62,6 +62,11 @@
</template>
</n-result>
</n-spin>
<template #action>
<n-space justify="end">
<n-button :disabled="importing" @click="showImport = false">取消</n-button>
</n-space>
</template>
</n-modal>
<n-modal v-model:show="showDetail" preset="card" title="地址详情" class="detail-modal">
+44 -4
View File
@@ -37,14 +37,16 @@
</template>
<script lang="ts" setup>
import { h, reactive, ref } from 'vue';
import { NImage, useDialog, useMessage } from 'naive-ui';
import { h, onMounted, reactive, ref } from 'vue';
import { NImage, NTag, useDialog, useMessage } from 'naive-ui';
import { BasicTable, TableAction } from '@/components/Table';
import { usePermission } from '@/hooks/web/usePermission';
import { ManualProductDelete, ManualProductList } from '@/api/portal';
import { useDictStore } from '@/store/modules/dict';
import { ManualProductDelete, ManualProductList, ManualProductStatus } from '@/api/portal';
import Edit from './edit.vue';
const { hasPermission } = usePermission();
const dict = useDictStore();
const dialog = useDialog();
const message = useMessage();
const tableRef = ref();
@@ -64,12 +66,23 @@
},
{ title: '产品名称', key: 'name', minWidth: 320 },
{ title: '价格', key: 'price', width: 140 },
{
title: '状态',
key: 'publishStatus',
width: 100,
render: (row) =>
h(
NTag,
{ type: dict.getType('bizPublishStatus', row.publishStatus), bordered: false },
{ default: () => dict.getLabel('bizPublishStatus', row.publishStatus) || '-' }
),
},
{ title: '创建时间', key: 'createdAt', width: 190 },
];
const loadTable = (params) => ManualProductList(params);
const actionColumn = reactive({
width: 180,
width: 260,
title: '操作',
key: 'action',
fixed: 'right',
@@ -82,6 +95,12 @@
auth: ['/manualProduct/edit'],
onClick: () => editRef.value.open(row),
},
{
label: row.publishStatus === 3 ? '下架' : '发布',
type: row.publishStatus === 3 ? 'warning' : 'success',
auth: ['/manualProduct/status'],
onClick: () => changeStatus(row),
},
{
label: '删除',
auth: ['/manualProduct/delete'],
@@ -121,6 +140,27 @@
},
});
}
function changeStatus(row) {
const publish = row.publishStatus !== 3;
dialog.warning({
title: publish ? '发布商品' : '下架商品',
content: `确定${publish ? '发布' : '下架'}商品“${row.name}”吗?`,
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
await ManualProductStatus({
id: row.id,
version: row.version,
publishStatus: publish ? 3 : 4,
});
message.success(`${publish ? '发布' : '下架'}成功`);
reload();
},
});
}
onMounted(() => dict.loadOptions(['bizPublishStatus']));
</script>
<style lang="less" scoped>
+1 -1
View File
@@ -25,7 +25,7 @@
<n-form-item label="交易所需余额(必填)">
<n-input-number v-model:value="form.tradeBalanceAmount" :min="0" />
</n-form-item>
<n-form-item label="未订单的支付等待时长(秒)">
<n-form-item label="未支付订单等待时长(秒)">
<n-input-number v-model:value="form.tradeWaitSeconds" :min="0" />
</n-form-item>
<n-form-item label="当日交易次数限制">
@@ -26,6 +26,10 @@
<n-spin :show="loading">
<n-descriptions v-if="detail.id" bordered :column="2">
<n-descriptions-item label="任务">{{ detail.taskTitle }}</n-descriptions-item>
<n-descriptions-item label="订单">
{{ detail.tradeOrderNo || `#${detail.tradeOrderId || '-'}` }}
</n-descriptions-item>
<n-descriptions-item label="商品">{{ detail.productName || '-' }}</n-descriptions-item>
<n-descriptions-item label="会员">
{{ detail.username }} / {{ detail.mobile }}
</n-descriptions-item>
@@ -35,6 +35,7 @@ export const columns = [
{ title: '用户名', key: 'username', width: 130 },
{ title: '手机号', key: 'mobile', width: 130 },
{ title: '任务标题', key: 'taskTitle', width: 180 },
{ title: '订单号', key: 'tradeOrderNo', width: 180, render: (r) => r.tradeOrderNo || `#${r.tradeOrderId || '-'}` },
{ title: '等级', key: 'levelName', width: 110 },
{
title: '佣金',
+23 -2
View File
@@ -13,6 +13,13 @@
><n-select
v-model:value="form.hallId"
:options="dict.getOptionUnRef('taskHallOption')" /></n-form-item-gi
><n-form-item-gi label="关联商品" path="productId"
><n-select
v-model:value="form.productId"
filterable
:loading="productLoading"
:options="productOptions"
placeholder="请选择关联商品" /></n-form-item-gi
><n-form-item-gi label="会员等级" :span="2" path="levelIds"
><n-select
v-model:value="form.levelIds"
@@ -61,7 +68,7 @@
import Editor from '@/components/Editor/editor.vue';
import UploadImage from '@/components/Upload/uploadImage.vue';
import { useDictStore } from '@/store/modules/dict';
import { TaskEdit, TaskView } from '@/api/productTask';
import { ProductList, TaskEdit, TaskView } from '@/api/productTask';
const emit = defineEmits(['reload']),
message = useMessage(),
dict = useDictStore(),
@@ -70,7 +77,9 @@
saving = ref(false),
formRef = ref(),
dateRange = ref<any>(),
commissionPercent = ref(0);
commissionPercent = ref(0),
productLoading = ref(false),
productOptions = ref<any[]>([]);
const init = () => ({
id: 0,
version: 0,
@@ -80,6 +89,7 @@
coverAttachmentId: 0,
coverUrl: '',
hallId: null,
productId: null,
levelIds: [],
commission: '0.00',
dailyLimit: 0,
@@ -93,6 +103,7 @@
description: { required: true, message: '请输入任务描述' },
coverUrl: { required: true, message: '请上传任务封面' },
hallId: { required: true, type: 'number', message: '请选择任务大厅' },
productId: { required: true, type: 'number', message: '请选择关联商品' },
levelIds: { required: true, type: 'array', min: 1, message: '请选择会员等级' },
commission: { required: true, message: '请输入固定佣金' },
};
@@ -101,6 +112,16 @@
commissionPercent.value = 0;
dateRange.value = null;
show.value = true;
productLoading.value = true;
try {
const result: any = await ProductList({ page: 1, perPage: 500 });
productOptions.value = (result.list || []).map((item) => ({
label: `${item.name}(${item.productNo})`,
value: item.id,
}));
} finally {
productLoading.value = false;
}
if (row) {
loading.value = true;
try {
+27 -1
View File
@@ -1 +1,27 @@
import{computed,h,ref}from'vue';import{NImage,NTag}from'naive-ui';import{FormSchema}from'@/components/Form';import{useDictStore}from'@/store/modules/dict';const dict=useDictStore();export const schemas=ref<FormSchema[]>([{field:'title',component:'NInput',label:'任务标题',componentProps:{clearable:true}},{field:'levelId',component:'NSelect',label:'会员类型',componentProps:{options:computed(()=>dict.getOptionUnRef('memberLevelOption')),clearable:true}},{field:'hallId',component:'NSelect',label:'任务大厅',componentProps:{options:computed(()=>dict.getOptionUnRef('taskHallOption')),clearable:true}},{field:'publishStatus',component:'NSelect',label:'状态',componentProps:{options:computed(()=>dict.getOptionUnRef('bizPublishStatus')),clearable:true}}]);const tag=(k,v)=>h(NTag,{type:dict.getType(k,v),bordered:false},()=>dict.getLabel(k,v)||'-');export const columns=[{title:'任务编号',key:'taskNo',width:180},{title:'封面',key:'coverUrl',width:100,render:r=>h(NImage,{src:r.coverUrl,width:64,height:64,objectFit:'cover'})},{title:'标题',key:'title',width:180},{title:'描述',key:'description',width:220,ellipsis:{tooltip:true}},{title:'任务大厅',key:'hallName',width:130},{title:'会员等级',key:'levelNames',width:180,render:r=>(r.levelNames||[]).join('、')},{title:'固定佣金',key:'commissionAmount',width:100,render:r=>`¥${((r.commissionAmount||0)/100).toFixed(2)}`},{title:'比例佣金',key:'commissionRate',width:100,render:r=>`${((r.commissionRate||0)/100).toFixed(2)}%`},{title:'状态',key:'publishStatus',width:100,render:r=>tag('bizPublishStatus',r.publishStatus)},{title:'创建时间',key:'createdAt',width:180}];export const loadOptions=()=>dict.loadOptions(['memberLevelOption','taskHallOption','bizPublishStatus']);
import { computed, h, ref } from 'vue';
import { NImage, NTag } from 'naive-ui';
import { FormSchema } from '@/components/Form';
import { useDictStore } from '@/store/modules/dict';
const dict = useDictStore();
export const schemas = ref<FormSchema[]>([
{ field: 'title', component: 'NInput', label: '任务标题', componentProps: { clearable: true } },
{ field: 'levelId', component: 'NSelect', label: '会员类型', componentProps: { options: computed(() => dict.getOptionUnRef('memberLevelOption')), clearable: true } },
{ field: 'hallId', component: 'NSelect', label: '任务大厅', componentProps: { options: computed(() => dict.getOptionUnRef('taskHallOption')), clearable: true } },
{ field: 'publishStatus', component: 'NSelect', label: '状态', componentProps: { options: computed(() => dict.getOptionUnRef('bizPublishStatus')), clearable: true } },
]);
const tag = (key, value) => h(NTag, { type: dict.getType(key, value), bordered: false }, () => dict.getLabel(key, value) || '-');
export const columns = [
{ title: '任务编号', key: 'taskNo', width: 180 },
{ title: '封面', key: 'coverUrl', width: 100, render: (row) => h(NImage, { src: row.coverUrl, width: 64, height: 64, objectFit: 'cover' }) },
{ title: '标题', key: 'title', width: 180 },
{ title: '关联商品', key: 'productName', width: 180, render: (row) => row.productName || '待补录' },
{ title: '描述', key: 'description', width: 220, ellipsis: { tooltip: true } },
{ title: '任务大厅', key: 'hallName', width: 130 },
{ title: '会员等级', key: 'levelNames', width: 180, render: (row) => (row.levelNames || []).join('、') },
{ title: '固定佣金', key: 'commissionAmount', width: 100, render: (row) => `¥${((row.commissionAmount || 0) / 100).toFixed(2)}` },
{ title: '比例佣金', key: 'commissionRate', width: 100, render: (row) => `${((row.commissionRate || 0) / 100).toFixed(2)}%` },
{ title: '状态', key: 'publishStatus', width: 100, render: (row) => tag('bizPublishStatus', row.publishStatus) },
{ title: '创建时间', key: 'createdAt', width: 180 },
];
export const loadOptions = () => dict.loadOptions(['memberLevelOption', 'taskHallOption', 'bizPublishStatus']);
+15 -1
View File
@@ -68,8 +68,19 @@
<n-descriptions-item label="订单号">{{ detail.orderNo }}</n-descriptions-item><n-descriptions-item label="会员">{{ detail.username }} / {{ detail.mobile }}</n-descriptions-item>
<n-descriptions-item label="商品/任务">{{ detail.productName || '-' }}</n-descriptions-item><n-descriptions-item label="规则等级ID">{{ detail.memberLevelId }}</n-descriptions-item>
<n-descriptions-item label="订单金额">{{ yuan(detail.totalAmount) }}</n-descriptions-item><n-descriptions-item label="佣金">{{ yuan(detail.commissionAmount) }}</n-descriptions-item>
<n-descriptions-item label="订单状态">{{ dict.getLabel('tradeOrderStatus', detail.orderStatus) || '-' }}</n-descriptions-item><n-descriptions-item label="冻结状态">{{ dict.getLabel('tradeFreezeStatus', detail.freezeStatus) || '-' }}</n-descriptions-item>
<n-descriptions-item label="创建时间">{{ detail.createdAt || '-' }}</n-descriptions-item><n-descriptions-item label="更新时间">{{ detail.updatedAt || '-' }}</n-descriptions-item>
<n-descriptions-item label="规则快照" :span="2"><n-code :code="JSON.stringify(detail.ruleSnapshot || {}, null, 2)" language="json" word-wrap /></n-descriptions-item>
</n-descriptions>
<n-divider title-placement="left">状态时间线</n-divider>
<n-timeline v-if="detail?.timeline?.length"><n-timeline-item v-for="item in detail.timeline" :key="`${item.title}-${item.time}`" :title="item.title" :time="item.time" /></n-timeline>
<n-empty v-else description="暂无状态时间线" />
<n-divider title-placement="left">资金流水</n-divider>
<n-data-table :columns="ledgerColumns" :data="detail?.ledgers || []" :bordered="false" />
<n-empty v-if="!detail?.ledgers?.length" description="暂无关联资金流水" />
<n-divider title-placement="left">审批记录</n-divider>
<n-data-table :columns="approvalColumns" :data="detail?.approvalActions || []" :bordered="false" />
<n-empty v-if="!detail?.approvalActions?.length" description="暂无关联审批记录" />
</n-modal>
</div>
</template>
@@ -77,11 +88,14 @@
import { computed, h, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
import { useRouter } from 'vue-router';
import { NButton, NImage, NTag, useDialog, useMessage } from 'naive-ui';
import { useDictStore } from '@/store/modules/dict';
import { BasicTable, TableAction } from '@/components/Table';import { BasicForm,useForm } from '@/components/Form';import { adaTableScrollX } from '@/utils/hotgo';
import { TradeOrderForcePay,TradeOrderFreeze,TradeOrderList,TradeOrderMatch,TradeOrderMatchProducts,TradeOrderUpdateAmount,TradeOrderView } from '@/api/trade';import { columns,schemas,loadOptions } from './model';
const message=useMessage();const dialog=useDialog();const router=useRouter();const actionRef=ref(),searchFormRef=ref<any>({}),showDetail=ref(false),showMatch=ref(false),showAmount=ref(false),detail=ref<any>(),current=ref<any>(),autoRefresh=ref(false),refreshCountdown=ref(10);let timer:any;
const message=useMessage();const dialog=useDialog();const router=useRouter();const dict=useDictStore();const actionRef=ref(),searchFormRef=ref<any>({}),showDetail=ref(false),showMatch=ref(false),showAmount=ref(false),detail=ref<any>(),current=ref<any>(),autoRefresh=ref(false),refreshCountdown=ref(10);let timer:any;
const [register]=useForm({gridProps:{cols:'1 s:1 m:2 l:3 xl:5'},labelWidth:80,schemas});const loadDataTable=(res)=>TradeOrderList({...searchFormRef.value?.formModel,...res});const reloadTable=()=>actionRef.value?.reload();const yuan=(v)=>`¥${((v||0)/100).toFixed(2)}`;
const yuanPlain=(v)=>((v||0)/100).toFixed(2);
const ledgerColumns=[{title:'流水号',key:'ledgerNo',width:180},{title:'业务',key:'bizType',width:120},{title:'方向',key:'direction',width:80,render:(row)=>row.direction===1?'收入':'支出'},{title:'金额',key:'amount',width:110,render:(row)=>yuan(row.amount)},{title:'变动前',key:'beforeAmount',width:110,render:(row)=>yuan(row.beforeAmount)},{title:'变动后',key:'afterAmount',width:110,render:(row)=>yuan(row.afterAmount)},{title:'说明',key:'remark',minWidth:180},{title:'发生时间',key:'occurredAt',width:180}];
const approvalColumns=[{title:'节点',key:'stepNo',width:70},{title:'操作人ID',key:'actorId',width:100},{title:'动作',key:'action',width:90},{title:'意见',key:'comment',minWidth:220},{title:'操作时间',key:'createdAt',width:180}];
const matchLoading=ref(false),productLoading=ref(false),matchSubmitting=ref(false),products=ref<any[]>([]),selectedProduct=ref<any>();
const matchMember=reactive({id:0,username:'',availableAmount:0,frozenAmount:0});
const matchForm=reactive({id:0,productId:0,quantity:1});
+1
View File
@@ -9,6 +9,7 @@ export const schemas = ref<FormSchema[]>([
{ field: 'orderNo', component: 'NInput', label: '订单号', componentProps: { placeholder: '请输入订单号', clearable: true } },
{ field: 'username', component: 'NInput', label: '用户名', componentProps: { clearable: true } },
{ field: 'mobile', component: 'NInput', label: '手机号', componentProps: { placeholder: '精确查询', clearable: true } },
{ field: 'autoDispatch', component: 'NSelect', label: '派单模式', componentProps: { options: computed(() => dict.getOptionUnRef('tradeAutoDispatch')), clearable: true } },
{ field: 'orderStatus', component: 'NSelect', label: '订单状态', componentProps: { options: computed(() => dict.getOptionUnRef('tradeOrderStatus')), clearable: true } },
{ field: 'createdAt', component: 'NDatePicker', label: '下单时间', componentProps: { type: 'datetimerange', clearable: true, shortcuts: defRangeShortcuts() } },
]);