feat:【mall 商城】分销用户的 list modal 迁移(ele 50%)

This commit is contained in:
YunaiV
2025-10-10 13:28:25 +08:00
parent f77e731357
commit f41e746a81
8 changed files with 256 additions and 318 deletions

View File

@@ -19,7 +19,6 @@ const [Grid] = useVbenVxeGrid({
columns: useGridColumns(),
height: 'auto',
keepSource: true,
showOverflow: 'tooltip',
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {

View File

@@ -1,6 +1,8 @@
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { fenToYuan } from '@vben/utils';
import { getRangePickerDefaultProps } from '#/utils';
@@ -138,3 +140,177 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
},
];
}
/** 用户列表弹窗搜索表单配置 */
export function useUserListFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'level',
label: '用户类型',
component: 'RadioGroup',
// TODO @霖:这里会折行
componentProps: {
options: [
{ label: '全部', value: undefined },
{ label: '一级推广人', value: '1' },
{ label: '二级推广人', value: '2' },
],
buttonStyle: 'solid',
optionType: 'button',
},
},
{
fieldName: 'bindUserTime',
label: '绑定时间',
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
clearable: true,
},
},
];
}
/** 用户列表弹窗表格列配置 */
export function useUserListColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'id',
title: '用户编号',
minWidth: 80,
},
{
field: 'avatar',
title: '头像',
minWidth: 70,
cellRender: {
name: 'CellImage',
props: {
width: 24,
height: 24,
shape: 'circle',
},
},
},
{
field: 'nickname',
title: '昵称',
minWidth: 80,
},
{
field: 'brokerageUserCount',
title: '推广人数',
minWidth: 80,
},
{
field: 'brokerageOrderCount',
title: '推广订单数量',
minWidth: 110,
},
{
field: 'brokerageEnabled',
title: '推广资格',
minWidth: 80,
slots: { default: 'brokerageEnabled' },
},
{
field: 'bindUserTime',
title: '绑定时间',
width: 180,
formatter: 'formatDateTime',
},
];
}
/** 推广订单列表弹窗搜索表单配置 */
export function useOrderListFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'sourceUserLevel',
label: '用户类型',
component: 'Select',
componentProps: {
options: [
{ label: '全部', value: 0 },
{ label: '一级推广人', value: 1 },
{ label: '二级推广人', value: 2 },
],
},
defaultValue: 0,
},
{
fieldName: 'status',
label: '状态',
component: 'Select',
componentProps: {
placeholder: '请选择状态',
clearable: true,
options: getDictOptions(DICT_TYPE.BROKERAGE_RECORD_STATUS, 'number'),
},
},
{
fieldName: 'createTime',
label: '创建时间',
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
clearable: true,
},
},
];
}
/** 推广订单列表弹窗表格列配置 */
export function useOrderListColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'bizId',
title: '订单编号',
minWidth: 80,
},
{
field: 'sourceUserId',
title: '用户编号',
minWidth: 80,
},
{
field: 'sourceUserAvatar',
title: '头像',
minWidth: 70,
cellRender: {
name: 'CellImage',
props: {
width: 24,
height: 24,
},
},
},
{
field: 'sourceUserNickname',
title: '昵称',
minWidth: 80,
},
{
field: 'price',
title: '佣金',
minWidth: 100,
formatter: ({ row }) => `${fenToYuan(row.price)}`,
},
{
field: 'status',
title: '状态',
minWidth: 85,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.BROKERAGE_RECORD_STATUS },
},
},
{
field: 'createTime',
title: '创建时间',
width: 180,
formatter: 'formatDateTime',
},
];
}

View File

@@ -2,11 +2,10 @@
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MallBrokerageUserApi } from '#/api/mall/trade/brokerage/user';
import { useAccess } from '@vben/access';
import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { ElLoading, ElMessage, ElSwitch } from 'element-plus';
import { ElMessage, ElSwitch } from 'element-plus';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
@@ -16,84 +15,85 @@ import {
} from '#/api/mall/trade/brokerage/user';
import { useGridColumns, useGridFormSchema } from './data';
import CreateForm from './modules/create-form.vue';
import BrokerageOrderListModal from './modules/order-list-modal.vue';
import BrokerageUserCreateForm from './modules/user-create-form.vue';
import UpdateForm from './modules/update-form.vue';
import BrokerageUserListModal from './modules/user-list-modal.vue';
import BrokerageUserUpdateForm from './modules/user-update-form.vue';
defineOptions({ name: 'TradeBrokerageUser' });
const { hasAccessByCodes } = useAccess();
const [CreateFormModal, createFormModalApi] = useVbenModal({
connectedComponent: CreateForm,
destroyOnClose: true,
});
const [UpdateFormModal, updateModalApi] = useVbenModal({
connectedComponent: UpdateForm,
destroyOnClose: true,
});
const [OrderListModal, orderListModalApi] = useVbenModal({
connectedComponent: BrokerageOrderListModal,
destroyOnClose: true,
});
const [UserListModal, userListModalApi] = useVbenModal({
connectedComponent: BrokerageUserListModal,
destroyOnClose: true,
});
/** 刷新表格 */
function onRefresh() {
function handleRefresh() {
gridApi.query();
}
const [OrderListModal, OrderListModalApi] = useVbenModal({
connectedComponent: BrokerageOrderListModal,
});
/** 创建分销员 */
function handleCreate() {
createFormModalApi.open();
}
const [UserCreateModal, UserCreateModalApi] = useVbenModal({
connectedComponent: BrokerageUserCreateForm,
});
const [UserListModal, UserListModalApi] = useVbenModal({
connectedComponent: BrokerageUserListModal,
});
const [UserUpdateModal, UserUpdateModalApi] = useVbenModal({
connectedComponent: BrokerageUserUpdateForm,
});
/** 修改分销员 */
function handleUpdateForm(row: MallBrokerageUserApi.BrokerageUser) {
updateModalApi.setData(row).open();
}
/** 打开推广人列表 */
function openBrokerageUserTable(row: MallBrokerageUserApi.BrokerageUser) {
UserListModalApi.setData(row).open();
function handleOpenUserList(row: MallBrokerageUserApi.BrokerageUser) {
userListModalApi.setData(row).open();
}
/** 打开推广订单列表 */
function openBrokerageOrderTable(row: MallBrokerageUserApi.BrokerageUser) {
OrderListModalApi.setData(row).open();
}
/** 打开表单:修改上级推广人 */
function openUpdateBindUserForm(row: MallBrokerageUserApi.BrokerageUser) {
UserUpdateModalApi.setData(row).open();
}
/** 创建分销员 */
function openCreateUserForm() {
UserCreateModalApi.open();
function handleOpenOrderList(row: MallBrokerageUserApi.BrokerageUser) {
orderListModalApi.setData(row).open();
}
/** 清除上级推广人 */
async function handleClearBindUser(row: MallBrokerageUserApi.BrokerageUser) {
await confirm(`确定清除"${row.nickname}"的上级推广人吗?`);
await clearBindUser({ id: row.id as number });
ElMessage.success('清除成功');
onRefresh();
try {
await clearBindUser({ id: row.id as number });
ElMessage.success('清除成功');
handleRefresh();
} catch {
// 清除失败时不做处理
}
}
/** 推广资格:开通/关闭 */
/** 更新推广资格 */
async function handleBrokerageEnabledChange(
row: MallBrokerageUserApi.BrokerageUser,
) {
const text = row.brokerageEnabled ? '开通' : '关闭';
const loadingInstance = ElLoading.service({
text: `正在${text}"${row.nickname}"的推广资格...`,
});
try {
await updateBrokerageEnabled({
id: row.id as number,
enabled: row.brokerageEnabled as boolean,
});
ElMessage.success(`${text}成功`);
onRefresh();
handleRefresh();
} catch {
// 异常时,需要重置回之前的值
row.brokerageEnabled = !row.brokerageEnabled;
} finally {
loadingInstance.close();
}
}
@@ -105,7 +105,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
columns: useGridColumns(),
height: 'auto',
keepSource: true,
showOverflow: 'tooltip',
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
@@ -138,7 +137,15 @@ const [Grid, gridApi] = useVbenVxeGrid({
/>
</template>
<Grid table-title="分销用户列表">
<!-- 创建分销员 -->
<CreateFormModal @success="handleRefresh" />
<!-- 修改分销员 -->
<UpdateFormModal @success="handleRefresh" />
<!-- 推广人列表 -->
<UserListModal />
<!-- 推广订单列表 -->
<OrderListModal />
<Grid>
<template #toolbar-tools>
<TableAction
:actions="[
@@ -147,7 +154,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
type: 'primary',
icon: ACTION_ICON.ADD,
auth: ['trade:brokerage-user:create'],
onClick: openCreateUserForm,
onClick: handleCreate,
},
]"
/>
@@ -155,10 +162,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
<template #brokerageEnabled="{ row }">
<ElSwitch
v-model:checked="row.brokerageEnabled"
:disabled="
!hasAccessByCodes(['trade:brokerage-user:update-bind-user'])
"
v-model="row.brokerageEnabled"
checked-children=""
un-checked-children=""
@change="handleBrokerageEnabledChange(row)"
@@ -173,41 +177,33 @@ const [Grid, gridApi] = useVbenVxeGrid({
type: 'primary',
link: true,
auth: ['trade:brokerage-user:user-query'],
onClick: openBrokerageUserTable.bind(null, row),
onClick: handleOpenUserList.bind(null, row),
},
{
label: '推广订单',
type: 'primary',
link: true,
auth: ['trade:brokerage-user:order-query'],
onClick: openBrokerageOrderTable.bind(null, row),
onClick: handleOpenOrderList.bind(null, row),
},
{
label: '修改上级推广人',
type: 'primary',
link: true,
auth: ['trade:brokerage-user:update-bind-user'],
onClick: openUpdateBindUserForm.bind(null, row),
onClick: handleUpdateForm.bind(null, row),
},
{
label: '清除上级推广人',
type: 'primary',
link: true,
auth: ['trade:brokerage-user:clear-bind-user'],
ifShow: !!row.bindUserId,
onClick: handleClearBindUser.bind(null, row),
},
]"
/>
</template>
</Grid>
<!-- 修改上级推广人表单 -->
<UserUpdateModal @success="onRefresh" />
<!-- 推广人列表 -->
<UserListModal />
<!-- 推广订单列表 -->
<OrderListModal />
<!-- 创建分销员 -->
<UserCreateModal @success="onRefresh" />
</Page>
</template>

View File

@@ -168,7 +168,7 @@ async function handleGetUser(id: any, userType: string) {
{{ userInfo.bindUser?.nickname }}
</ElDescriptionsItem>
<ElDescriptionsItem label="推广资格">
<ElTag v-if="userInfo.bindUser?.brokerageEnabled" color="success">
<ElTag v-if="userInfo.bindUser?.brokerageEnabled" type="success">
</ElTag>
<ElTag v-else></ElTag>
@@ -179,4 +179,4 @@ async function handleGetUser(id: any, userType: string) {
</ElDescriptions>
</div>
</Modal>
</template>
</template>

View File

@@ -1,158 +1,35 @@
<script lang="ts" setup>
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MallBrokerageRecordApi } from '#/api/mall/trade/brokerage/record';
import type { MallBrokerageUserApi } from '#/api/mall/trade/brokerage/user';
import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { BrokerageRecordBizTypeEnum, DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { fenToYuan } from '@vben/utils';
import { BrokerageRecordBizTypeEnum } from '@vben/constants';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getBrokerageRecordPage } from '#/api/mall/trade/brokerage/record';
import { getRangePickerDefaultProps } from '#/utils';
import { useOrderListColumns, useOrderListFormSchema } from '../data';
/** 推广订单列表 */
defineOptions({ name: 'BrokerageOrderListModal' });
const userId = ref<number>();
const [Modal, modalApi] = useVbenModal({});
const [Modal, modalApi] = useVbenModal({
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
userId.value = undefined;
return;
}
// 加载数据
const data = modalApi.getData<MallBrokerageUserApi.BrokerageUser>();
if (!data || !data.id) {
return;
}
modalApi.lock();
try {
userId.value = data.id;
// 等待弹窗打开后再查询
setTimeout(() => {
gridApi.query();
}, 100);
} finally {
modalApi.unlock();
}
},
});
/** 搜索表单配置 */
function useFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'sourceUserLevel',
label: '用户类型',
component: 'Select',
componentProps: {
options: [
{ label: '全部', value: 0 },
{ label: '一级推广人', value: 1 },
{ label: '二级推广人', value: 2 },
],
},
defaultValue: 0,
},
{
fieldName: 'status',
label: '状态',
component: 'Select',
componentProps: {
placeholder: '请选择状态',
clearable: true,
options: getDictOptions(DICT_TYPE.BROKERAGE_RECORD_STATUS, 'number'),
},
},
{
fieldName: 'createTime',
label: '创建时间',
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
clearable: true,
},
},
];
}
/** 表格列配置 */
function useColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'bizId',
title: '订单编号',
minWidth: 80,
},
{
field: 'sourceUserId',
title: '用户编号',
minWidth: 80,
},
{
field: 'sourceUserAvatar',
title: '头像',
minWidth: 70,
cellRender: {
name: 'CellImage',
props: {
width: 24,
height: 24,
},
},
},
{
field: 'sourceUserNickname',
title: '昵称',
minWidth: 80,
},
{
field: 'price',
title: '佣金',
minWidth: 100,
formatter: ({ row }) => `${fenToYuan(row.price)}`,
},
{
field: 'status',
title: '状态',
minWidth: 85,
cellRender: {
name: 'CellDict',
props: { type: DICT_TYPE.BROKERAGE_RECORD_STATUS },
},
},
{
field: 'createTime',
title: '创建时间',
width: 180,
formatter: 'formatDateTime',
},
];
}
const [Grid, gridApi] = useVbenVxeGrid({
const [Grid] = useVbenVxeGrid({
formOptions: {
schema: useFormSchema(),
schema: useOrderListFormSchema(),
},
gridOptions: {
columns: useColumns(),
columns: useOrderListColumns(),
height: '600',
keepSource: true,
showOverflow: 'tooltip',
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
// 处理全部的情况
const params = {
pageNo: page.currentPage,
pageSize: page.pageSize,
userId: userId.value,
userId: modalApi.getData()?.id,
bizType: BrokerageRecordBizTypeEnum.ORDER.type,
sourceUserLevel:
formValues.sourceUserLevel === 0
@@ -179,6 +56,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
<template>
<Modal title="推广订单列表" class="w-3/5">
<Grid table-title="推广订单列表" />
<Grid />
</Modal>
</template>

View File

@@ -125,7 +125,7 @@ async function handleGetUser() {
{{ bindUser.nickname }}
</ElDescriptionsItem>
<ElDescriptionsItem label="推广资格">
<ElTag v-if="bindUser.brokerageEnabled" color="success"></ElTag>
<ElTag v-if="bindUser.brokerageEnabled" type="success"></ElTag>
<ElTag v-else></ElTag>
</ElDescriptionsItem>
<ElDescriptionsItem label="成为推广员的时间">
@@ -133,4 +133,4 @@ async function handleGetUser() {
</ElDescriptionsItem>
</ElDescriptions>
</Modal>
</template>
</template>

View File

@@ -1,137 +1,33 @@
<script lang="ts" setup>
import type { VbenFormSchema } from '#/adapter/form';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MallBrokerageUserApi } from '#/api/mall/trade/brokerage/user';
import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { ElTag } from 'element-plus';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { getBrokerageUserPage } from '#/api/mall/trade/brokerage/user';
import { getRangePickerDefaultProps } from '#/utils';
import { useUserListColumns, useUserListFormSchema } from '../data';
defineOptions({ name: 'BrokerageUserListModal' });
const bindUserId = ref<number>();
const [Modal, modalApi] = useVbenModal({});
const [Modal, modalApi] = useVbenModal({
onOpenChange: async (isOpen: boolean) => {
if (!isOpen) {
bindUserId.value = undefined;
return;
}
const data = modalApi.getData<MallBrokerageUserApi.BrokerageUser>();
if (!data || !data.id) {
return;
}
bindUserId.value = data.id;
// 等待弹窗打开后再查询
setTimeout(() => {
gridApi.query();
}, 100);
},
});
/** 搜索表单配置 */
function useFormSchema(): VbenFormSchema[] {
return [
{
fieldName: 'level',
label: '用户类型',
component: 'RadioGroup',
// TODO @霖:这里会折行
componentProps: {
options: [
{ label: '全部', value: undefined },
{ label: '一级推广人', value: '1' },
{ label: '二级推广人', value: '2' },
],
buttonStyle: 'solid',
optionType: 'button',
},
},
{
fieldName: 'bindUserTime',
label: '绑定时间',
component: 'RangePicker',
componentProps: {
...getRangePickerDefaultProps(),
clearable: true,
},
},
];
}
/** 表格列配置 */
function useColumns(): VxeTableGridOptions['columns'] {
return [
{
field: 'id',
title: '用户编号',
minWidth: 80,
},
{
field: 'avatar',
title: '头像',
minWidth: 70,
cellRender: {
name: 'CellImage',
props: {
width: 24,
height: 24,
shape: 'circle',
},
},
},
{
field: 'nickname',
title: '昵称',
minWidth: 80,
},
{
field: 'brokerageUserCount',
title: '推广人数',
minWidth: 80,
},
{
field: 'brokerageOrderCount',
title: '推广订单数量',
minWidth: 110,
},
{
field: 'brokerageEnabled',
title: '推广资格',
minWidth: 80,
slots: { default: 'brokerageEnabled' },
},
{
field: 'bindUserTime',
title: '绑定时间',
width: 180,
formatter: 'formatDateTime',
},
];
}
const [Grid, gridApi] = useVbenVxeGrid({
const [Grid] = useVbenVxeGrid({
formOptions: {
schema: useFormSchema(),
schema: useUserListFormSchema(),
},
gridOptions: {
columns: useColumns(),
columns: useUserListColumns(),
height: '600',
keepSource: true,
showOverflow: 'tooltip',
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
return await getBrokerageUserPage({
pageNo: page.currentPage,
pageSize: page.pageSize,
bindUserId: bindUserId.value,
bindUserId: modalApi.getData().id,
...formValues,
});
},
@@ -151,11 +47,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
<template>
<Modal title="推广人列表" class="w-3/5">
<Grid table-title="推广人列表">
<template #brokerageEnabled="{ row }">
<ElTag v-if="row.brokerageEnabled" color="success"></ElTag>
<ElTag v-else></ElTag>
</template>
</Grid>
<Grid />
</Modal>
</template>