feat: member user

pull/119/head
xingyu4j 2025-05-28 22:57:58 +08:00
parent 868c0f822f
commit 09f26320f7
5 changed files with 456 additions and 3 deletions

View File

@ -12,7 +12,9 @@ import { getSimpleTagList } from '#/api/member/tag';
import { getAreaTree } from '#/api/system/area';
import {
CommonStatusEnum,
convertToInteger,
DICT_TYPE,
formatToFraction,
getDictOptions,
getRangePickerDefaultProps,
} from '#/utils';
@ -265,3 +267,186 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
},
];
}
/** 修改用户等级 */
export function useLeavelFormSchema(): VbenFormSchema[] {
return [
{
component: 'Input',
fieldName: 'id',
label: '用户编号',
componentProps: {
disabled: true,
},
},
{
component: 'Input',
fieldName: 'nickname',
label: '用户昵称',
componentProps: {
disabled: true,
},
},
{
fieldName: 'point',
label: '用户等级',
component: 'ApiSelect',
componentProps: {
api: () => getSimpleLevelList(),
fieldNames: { label: 'name', value: 'id' },
},
},
{
component: 'Textarea',
fieldName: 'reason',
label: '修改原因',
rules: 'required',
},
];
}
/** 修改用户余额 */
export function useBalanceFormSchema(): VbenFormSchema[] {
return [
{
component: 'Input',
fieldName: 'id',
label: '用户编号',
componentProps: {
disabled: true,
},
},
{
component: 'Input',
fieldName: 'nickname',
label: '用户昵称',
componentProps: {
disabled: true,
},
},
{
component: 'Input',
fieldName: 'balance',
label: '变动前余额(元)',
componentProps: {
disabled: true,
},
},
{
component: 'RadioGroup',
fieldName: 'changeType',
label: '变动类型',
componentProps: {
options: [
{ label: '增加', value: 1 },
{ label: '减少', value: -1 },
],
buttonStyle: 'solid',
optionType: 'button',
},
defaultValue: 1,
},
{
component: 'InputNumber',
fieldName: 'changeBalance',
label: '变动余额(元)',
rules: 'required',
componentProps: {
min: 0,
precision: 2,
step: 0.1,
},
defaultValue: 0,
},
{
component: 'Input',
fieldName: 'balanceResult',
label: '变动后余额(元)',
dependencies: {
triggerFields: ['changeBalance', 'changeType'],
disabled: true,
trigger(values, form) {
form.setFieldValue(
'balanceResult',
formatToFraction(
convertToInteger(values.balance) +
convertToInteger(values.changeBalance) * values.changeType,
),
);
},
},
},
];
}
/** 修改用户积分 */
export function usePointFormSchema(): VbenFormSchema[] {
return [
{
component: 'Input',
fieldName: 'id',
label: '用户编号',
componentProps: {
disabled: true,
},
},
{
component: 'Input',
fieldName: 'nickname',
label: '用户昵称',
componentProps: {
disabled: true,
},
},
{
component: 'Input',
fieldName: 'point',
label: '变动前积分',
componentProps: {
disabled: true,
},
},
{
component: 'RadioGroup',
fieldName: 'changeType',
label: '变动类型',
componentProps: {
options: [
{ label: '增加', value: 1 },
{ label: '减少', value: -1 },
],
buttonStyle: 'solid',
optionType: 'button',
},
defaultValue: 1,
},
{
component: 'InputNumber',
fieldName: 'changePoint',
label: '变动积分',
rules: 'required',
componentProps: {
min: 0,
precision: 0,
},
defaultValue: 0,
},
{
component: 'Input',
fieldName: 'pointResult',
label: '变动后积分',
dependencies: {
triggerFields: ['changePoint', 'changeType'],
disabled: true,
trigger(values, form) {
form.setFieldValue(
'pointResult',
values.point + values.changePoint * values.changeType ||
values.point,
);
},
},
rules: z.number().min(0),
},
];
}

View File

@ -13,7 +13,10 @@ import { DocAlert } from '#/components/doc-alert';
import { $t } from '#/locales';
import { useGridColumns, useGridFormSchema } from './data';
import BalanceForm from './modules/balance-form.vue';
import Form from './modules/form.vue';
import LeavelForm from './modules/leavel-form.vue';
import PointForm from './modules/point-form.vue';
const router = useRouter();
@ -22,6 +25,21 @@ const [FormModal, formModalApi] = useVbenModal({
destroyOnClose: true,
});
const [PointFormModal, pointFormModalApi] = useVbenModal({
connectedComponent: PointForm,
destroyOnClose: true,
});
const [BalanceFormModal, balanceFormModalApi] = useVbenModal({
connectedComponent: BalanceForm,
destroyOnClose: true,
});
const [LeavelFormModal, leavelFormModalApi] = useVbenModal({
connectedComponent: LeavelForm,
destroyOnClose: true,
});
/** 刷新表格数据 */
function onRefresh() {
gridApi.query();
@ -45,17 +63,17 @@ function handleEdit(row: MemberUserApi.User) {
/** 修改会员等级 */
function handleUpdateLevel(row: MemberUserApi.User) {
formModalApi.setData(row).open();
leavelFormModalApi.setData(row).open();
}
/** 修改会员积分 */
function handleUpdatePoint(row: MemberUserApi.User) {
formModalApi.setData(row).open();
pointFormModalApi.setData(row).open();
}
/** 修改会员余额 */
function handleUpdateBalance(row: MemberUserApi.User) {
formModalApi.setData(row).open();
balanceFormModalApi.setData(row).open();
}
/** 查看会员详情 */
@ -112,6 +130,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
</template>
<FormModal @success="onRefresh" />
<PointFormModal @success="onRefresh" />
<BalanceFormModal @success="onRefresh" />
<LeavelFormModal @success="onRefresh" />
<Grid table-title="">
<template #toolbar-tools>
<TableAction

View File

@ -0,0 +1,93 @@
<script lang="ts" setup>
import type { MemberUserApi } from '#/api/member/user';
import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { getUser, updateUser } from '#/api/member/user';
import { getWallet } from '#/api/pay/wallet/balance';
import { $t } from '#/locales';
import { formatToFraction } from '#/utils';
import { useBalanceFormSchema } from '../data';
const emit = defineEmits(['success']);
const formData = ref({
id: 0,
nickname: '',
balance: '0',
changeBalance: 0,
changeType: 1,
});
const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
formItemClass: 'col-span-2',
labelWidth: 100,
},
layout: 'horizontal',
schema: useBalanceFormSchema(),
showDefaultActions: false,
});
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
const { valid } = await formApi.validate();
if (!valid) {
return;
}
modalApi.lock();
//
const data = (await formApi.getValues()) as MemberUserApi.User;
try {
await updateUser(data);
//
await modalApi.close();
emit('success');
message.success($t('ui.actionMessage.operationSuccess'));
} finally {
modalApi.unlock();
}
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
return;
}
//
const data = modalApi.getData<MemberUserApi.User>();
if (!data || !data.id) {
return;
}
modalApi.lock();
try {
const user = await getUser(data.id as number);
if (!user || !user.id) {
return;
}
const wallet = await getWallet({ userId: user.id });
formData.value.id = user.id;
formData.value.nickname = user.nickname || '';
formData.value.balance = formatToFraction(wallet.balance);
formData.value.changeType = 1; //
formData.value.changeBalance = 0; // 0
// values
await formApi.setValues(formData.value);
} finally {
modalApi.unlock();
}
},
});
</script>
<template>
<Modal class="w-[600px]" :title="$t('ui.actionTitle.edit', ['用户余额'])">
<Form class="mx-4" />
</Modal>
</template>

View File

@ -0,0 +1,77 @@
<script lang="ts" setup>
import type { MemberUserApi } from '#/api/member/user';
import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { getUser, updateUser } from '#/api/member/user';
import { $t } from '#/locales';
import { useLeavelFormSchema } from '../data';
const emit = defineEmits(['success']);
const formData = ref<MemberUserApi.User>();
const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
formItemClass: 'col-span-2',
labelWidth: 80,
},
layout: 'horizontal',
schema: useLeavelFormSchema(),
showDefaultActions: false,
});
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
const { valid } = await formApi.validate();
if (!valid) {
return;
}
modalApi.lock();
//
const data = (await formApi.getValues()) as MemberUserApi.User;
try {
await updateUser(data);
//
await modalApi.close();
emit('success');
message.success($t('ui.actionMessage.operationSuccess'));
} finally {
modalApi.unlock();
}
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
formData.value = undefined;
return;
}
//
const data = modalApi.getData<MemberUserApi.User>();
if (!data || !data.id) {
return;
}
modalApi.lock();
try {
formData.value = await getUser(data.id as number);
// values
await formApi.setValues(formData.value);
} finally {
modalApi.unlock();
}
},
});
</script>
<template>
<Modal class="w-[600px]" :title="$t('ui.actionTitle.edit', ['用户等级'])">
<Form class="mx-4" />
</Modal>
</template>

View File

@ -0,0 +1,77 @@
<script lang="ts" setup>
import type { MemberUserApi } from '#/api/member/user';
import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { getUser, updateUser } from '#/api/member/user';
import { $t } from '#/locales';
import { usePointFormSchema } from '../data';
const emit = defineEmits(['success']);
const formData = ref<MemberUserApi.User>();
const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
formItemClass: 'col-span-2',
labelWidth: 80,
},
layout: 'horizontal',
schema: usePointFormSchema(),
showDefaultActions: false,
});
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
const { valid } = await formApi.validate();
if (!valid) {
return;
}
modalApi.lock();
//
const data = (await formApi.getValues()) as MemberUserApi.User;
try {
await updateUser(data);
//
await modalApi.close();
emit('success');
message.success($t('ui.actionMessage.operationSuccess'));
} finally {
modalApi.unlock();
}
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
formData.value = undefined;
return;
}
//
const data = modalApi.getData<MemberUserApi.User>();
if (!data || !data.id) {
return;
}
modalApi.lock();
try {
formData.value = await getUser(data.id as number);
// values
await formApi.setValues(formData.value);
} finally {
modalApi.unlock();
}
},
});
</script>
<template>
<Modal class="w-[600px]" :title="$t('ui.actionTitle.edit', ['用户积分'])">
<Form class="mx-4" />
</Modal>
</template>