feat(财务): 新增天医币订单和用户余额模块
- 添加天医币订单相关API和页面 - 添加用户余额相关API和页面 - 在系统字典中新增来源和订单类型配置 - 调整部分UI样式和表单配置
This commit is contained in:
@@ -1,2 +1,4 @@
|
||||
export * from './import';
|
||||
export * from './reconciliate';
|
||||
export * from './surplus';
|
||||
export * from './tianyibi';
|
||||
|
||||
10
apps/finance/src/api/posting/surplus.ts
Normal file
10
apps/finance/src/api/posting/surplus.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export const balanceApi = {
|
||||
/**
|
||||
* 获取用户余额列表
|
||||
*/
|
||||
getUserBalanceList: (data: { limit: number; page: number; tel?: string }) => {
|
||||
return requestClient.post('/common/user/getUserList', data);
|
||||
},
|
||||
};
|
||||
37
apps/finance/src/api/posting/tianyibi.ts
Normal file
37
apps/finance/src/api/posting/tianyibi.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export const tianyibiApi = {
|
||||
/**
|
||||
* 获取天医币订单列表
|
||||
*/
|
||||
getPointOrdersList: (data: {
|
||||
endTime?: string;
|
||||
limit: number;
|
||||
page: number;
|
||||
startTime?: string;
|
||||
tel?: string;
|
||||
}) => {
|
||||
return requestClient.post('/common/orders/getPointOrdersListNoUse', data);
|
||||
},
|
||||
|
||||
/**
|
||||
* 手动确认消耗
|
||||
*/
|
||||
manualConsumeTianyibi: (data: { orderId: number }) => {
|
||||
return requestClient.post('/common/orders/manualConsumePoint', data);
|
||||
},
|
||||
|
||||
/**
|
||||
* 取消消耗
|
||||
*/
|
||||
cancelConsumeTianyibi: (data: { orderId: number }) => {
|
||||
return requestClient.post('/common/orders/cancelConsumePoint', data);
|
||||
},
|
||||
|
||||
/**
|
||||
* 自动消耗
|
||||
*/
|
||||
autoConsumeTianyibi: () => {
|
||||
return requestClient.post('/common/orders/autoConsumePoint');
|
||||
},
|
||||
};
|
||||
@@ -29,6 +29,24 @@ const routes: RouteRecordRaw[] = [
|
||||
path: '/posting/reconciliate-bills',
|
||||
component: () => import('#/views/posting/reconciliate/index.vue'),
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
title: '天医币订单',
|
||||
keepAlive: true,
|
||||
},
|
||||
name: 'TianyibiOrders',
|
||||
path: '/posting/tianyibi-orders',
|
||||
component: () => import('#/views/posting/tianyibiOrders/index.vue'),
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
title: '用户余额',
|
||||
keepAlive: true,
|
||||
},
|
||||
name: 'UserSurplus',
|
||||
path: '/posting/user-surplus',
|
||||
component: () => import('#/views/posting/userSurplus/index.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -12,6 +12,17 @@ export const useSysStore = defineStore('sys', () => {
|
||||
'1': '对账失败',
|
||||
'2': '对账成功',
|
||||
},
|
||||
source: {
|
||||
'0': '一路健康',
|
||||
'1': '吴门医述',
|
||||
},
|
||||
orderType: {
|
||||
'0': '充值',
|
||||
'1': 'vip',
|
||||
'2': '课',
|
||||
'3': '实物',
|
||||
'4': '培训班',
|
||||
},
|
||||
} as const;
|
||||
|
||||
// 自动推断字典类型
|
||||
@@ -64,6 +75,38 @@ export const useSysStore = defineStore('sys', () => {
|
||||
color: 'success',
|
||||
},
|
||||
],
|
||||
source: [
|
||||
{
|
||||
label: '一路健康',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
label: '吴门医述',
|
||||
value: '1',
|
||||
},
|
||||
],
|
||||
orderType: [
|
||||
{
|
||||
label: '充值',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
label: 'vip',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '课',
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
label: '实物',
|
||||
value: '3',
|
||||
},
|
||||
{
|
||||
label: '培训班',
|
||||
value: '4',
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
|
||||
// 自动推断字典列表类型
|
||||
|
||||
@@ -62,7 +62,6 @@ const formOptions: VbenFormProps = {
|
||||
defaultValue: '',
|
||||
fieldName: 'checkoff',
|
||||
label: '核对状态',
|
||||
// 核对状态占1列
|
||||
formItemClass: 'col-span-2',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -326,7 +326,6 @@ function onCompleteCheckCreated() {
|
||||
:deep(.ant-card-body) {
|
||||
padding: 1px !important;
|
||||
height: calc(100% - 41px); // 减去标签页头部高度
|
||||
overflow: hidden;
|
||||
// background-color: #f1f3f6;
|
||||
}
|
||||
}
|
||||
|
||||
214
apps/finance/src/views/posting/tianyibiOrders/index.vue
Normal file
214
apps/finance/src/views/posting/tianyibiOrders/index.vue
Normal file
@@ -0,0 +1,214 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Button, message, notification } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { tianyibiApi } from '#/api/posting';
|
||||
import { useSysStore } from '#/store';
|
||||
|
||||
const sysStore = useSysStore();
|
||||
|
||||
interface RowType {
|
||||
id: number;
|
||||
source: number;
|
||||
orderSn: string;
|
||||
orderOldId: string;
|
||||
tel: string;
|
||||
fee: number;
|
||||
type: number;
|
||||
orderTime: string;
|
||||
useFlag: string;
|
||||
}
|
||||
|
||||
const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
wrapperClass: 'grid-cols-6',
|
||||
schema: [
|
||||
{
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '未使用',
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
label: '已使用',
|
||||
value: '1',
|
||||
},
|
||||
],
|
||||
optionType: 'button',
|
||||
},
|
||||
defaultValue: '0',
|
||||
fieldName: 'useFlag',
|
||||
label: '状态',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
},
|
||||
fieldName: 'tel',
|
||||
label: '手机号',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: sysStore.getDictList('source'),
|
||||
},
|
||||
fieldName: 'source',
|
||||
label: '来源',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
options: sysStore.getDictList('orderType').filter((item) => item.value !== '0'),
|
||||
},
|
||||
fieldName: 'type',
|
||||
label: '订单类型',
|
||||
},
|
||||
|
||||
{
|
||||
component: 'RangePicker',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
placeholder: ['开始时间', '结束时间'],
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
},
|
||||
fieldName: 'orderTime',
|
||||
label: '订单时间',
|
||||
formItemClass: 'col-span-2',
|
||||
},
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: false,
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
|
||||
// 处理订单时间范围
|
||||
const handleFormValues = (values: any) => {
|
||||
const { orderTime, ...rest } = values;
|
||||
return orderTime && orderTime.length === 2
|
||||
? {
|
||||
...rest,
|
||||
startTime: orderTime[0],
|
||||
endTime: orderTime[1],
|
||||
}
|
||||
: {
|
||||
...rest,
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
};
|
||||
};
|
||||
|
||||
const gridOptions: VxeTableGridOptions<RowType> = {
|
||||
columns: [
|
||||
{ field: 'orderSn', title: '订单编号', width: 250 },
|
||||
{
|
||||
field: 'source',
|
||||
title: '来源',
|
||||
formatter: ({ cellValue }) => {
|
||||
return sysStore.getDictMap('source', cellValue);
|
||||
},
|
||||
},
|
||||
{ field: 'tel', title: '手机号' },
|
||||
{ field: 'fee', title: '金额' },
|
||||
{
|
||||
field: 'type',
|
||||
title: '类型',
|
||||
formatter: ({ cellValue }) => {
|
||||
return sysStore.getDictMap('orderType', cellValue);
|
||||
},
|
||||
},
|
||||
{ field: 'orderTime', title: '订单时间', formatter: 'formatDateTime', minWidth: 160 },
|
||||
{
|
||||
title: '操作',
|
||||
slots: {
|
||||
default: 'action',
|
||||
},
|
||||
},
|
||||
],
|
||||
exportConfig: {},
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
rowConfig: {
|
||||
isHover: true,
|
||||
},
|
||||
pagerConfig: {
|
||||
enabled: true,
|
||||
},
|
||||
proxyConfig: {
|
||||
response: {
|
||||
result: 'records',
|
||||
list: 'records',
|
||||
total: 'total',
|
||||
},
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
const params = handleFormValues(formValues);
|
||||
return await tianyibiApi.getPointOrdersList({
|
||||
page: page.currentPage,
|
||||
limit: page.pageSize,
|
||||
...params,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
});
|
||||
|
||||
async function onStartAutoMatch() {
|
||||
const hide = message.loading('系统自动匹配消耗中...', 0);
|
||||
await tianyibiApi.autoConsumeTianyibi();
|
||||
hide();
|
||||
notification.success({
|
||||
message: '自动匹配消耗成功',
|
||||
});
|
||||
// 刷新表格数据
|
||||
gridApi?.query();
|
||||
}
|
||||
|
||||
async function onConsumption(row: RowType, code: number) {
|
||||
const api = code === 1 ? tianyibiApi.manualConsumeTianyibi : tianyibiApi.cancelConsumeTianyibi;
|
||||
await api({ orderId: row.id });
|
||||
notification.success({
|
||||
message: code === 1 ? '确认消耗成功' : '取消消耗成功',
|
||||
});
|
||||
// 刷新表格数据
|
||||
gridApi?.query();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<Grid>
|
||||
<template #toolbar-actions>
|
||||
<div class="flex gap-2">
|
||||
<Button type="primary" @click="onStartAutoMatch()">启动自动匹配消耗</Button>
|
||||
</div>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<Button v-if="!row.useFlag" type="primary" size="small" @click="onConsumption(row, 1)">
|
||||
确认消耗
|
||||
</Button>
|
||||
<Button v-else type="primary" danger ghost size="small" @click="onConsumption(row, 0)">
|
||||
取消消耗
|
||||
</Button>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
82
apps/finance/src/views/posting/userSurplus/index.vue
Normal file
82
apps/finance/src/views/posting/userSurplus/index.vue
Normal file
@@ -0,0 +1,82 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { balanceApi } from '#/api/posting';
|
||||
|
||||
interface RowType {
|
||||
id: number;
|
||||
tel: string;
|
||||
totalPoint: number;
|
||||
point: number;
|
||||
}
|
||||
|
||||
const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
wrapperClass: 'grid-cols-6',
|
||||
schema: [
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
},
|
||||
fieldName: 'tel',
|
||||
label: '手机号',
|
||||
},
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: false,
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
|
||||
const gridOptions: VxeTableGridOptions<RowType> = {
|
||||
columns: [
|
||||
{ field: 'tel', title: '用户手机号' },
|
||||
{ field: 'totalPoint', title: '总充值' },
|
||||
{ field: 'point', title: '可用' },
|
||||
],
|
||||
exportConfig: {},
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
rowConfig: {
|
||||
isHover: true,
|
||||
},
|
||||
pagerConfig: {
|
||||
enabled: true,
|
||||
},
|
||||
proxyConfig: {
|
||||
response: {
|
||||
result: 'records',
|
||||
list: 'records',
|
||||
total: 'total',
|
||||
},
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await balanceApi.getUserBalanceList({
|
||||
page: page.currentPage,
|
||||
limit: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const [Grid] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<Grid />
|
||||
</Page>
|
||||
</template>
|
||||
Reference in New Issue
Block a user