feat(sdy): 新增订单与资金管理功能- 在 ShopDealerOrder 模型中添加客户名称、业务员、价格等相关字段- 在 ShopDealerCapital 模型中增加订单编号字段及用户ID查询参数

- 优化 clinicPatientUserEdit 组件用户ID显示逻辑
- 移除 cmsArticle 组件中未使用的 push 方法导入
- 重构 shopDealerCapital 搜索组件,支持关键词搜索和导出功能
- 调整 shopDealerCapital 表格列宽和金额显示样式-重构 shopDealerOrder2 搜索组件,支持关键词搜索和导出功能
- 在 shopDealerOrder2 表格中新增订单编号列- 启用开发环境 API 地址配置
This commit is contained in:
2025-11-04 01:21:03 +08:00
parent d2d8dff1e1
commit dc456f78a5
9 changed files with 249 additions and 175 deletions

View File

@@ -1,5 +1,5 @@
VITE_APP_NAME=后台管理(开发环境)
#VITE_API_URL=http://127.0.0.1:9200/api
VITE_API_URL=http://127.0.0.1:9200/api
#VITE_SERVER_API_URL=http://127.0.0.1:8000/api

View File

@@ -6,12 +6,28 @@ import type { PageParam } from '@/api';
export interface ShopDealerOrder {
// 主键ID
id?: number;
// 客户名称
title?: string;
// 买家用户ID
userId?: number;
// 业务员
nickname?: string;
// 订单编号
orderNo?: string;
// 订单总金额(不含运费)
orderPrice?: string;
// 价格
price?: string;
// 结算金额
settledPrice?: string;
// 换算成度
degreePrice?: string;
// 汇率
rate?: number;
// 月份
month?: string;
// 实发金额
payPrice?: string;
// 分销商用户id(一级)
firstUserId?: number;
// 分销商用户id(二级)

View File

@@ -10,6 +10,8 @@ export interface ShopDealerCapital {
userId?: number;
// 订单ID
orderId?: number;
// 订单编号
orderNo?: string;
// 资金流动类型 (10佣金收入 20提现支出 30转账支出 40转账收入)
flowType?: number;
// 金额
@@ -31,5 +33,7 @@ export interface ShopDealerCapital {
*/
export interface ShopDealerCapitalParam extends PageParam {
id?: number;
userId?: number;
toUserId?: number;
keywords?: string;
}

View File

@@ -26,7 +26,7 @@
<!-- v-model:value="form.type"-->
<!-- />-->
<!-- </a-form-item>-->
<a-form-item label="用户ID" name="userId">
<a-form-item v-if="isUpdate" label="用户ID" name="userId">
{{ form.userId }}
</a-form-item>
<a-form-item label="姓名" name="realName">

View File

@@ -88,7 +88,7 @@ import {CmsNavigation} from '@/api/cms/cmsNavigation/model';
import dayjs from 'dayjs';
import Import from "./Import.vue";
import {useWebsiteSettingStore} from "@/store/modules/setting";
import {openUrl, push} from "@/utils/common";
import {openUrl} from "@/utils/common";
const props = withDefaults(
defineProps<{

View File

@@ -1,19 +1,25 @@
<!-- 搜索表单 -->
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-button type="primary" class="ele-btn-icon" @click="add">
<template #icon>
<PlusOutlined />
</template>
<span>添加</span>
</a-button>
<a-input-search
allow-clear
placeholder="用户ID|订单编号"
style="width: 240px"
v-model:value="where.keywords"
@search="reload"
/>
<a-button type="dashed" @click="handleExport">导出xls</a-button>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import type { GradeParam } from '@/api/user/grade/model';
import { watch } from 'vue';
import {ref, watch} from 'vue';
import {utils, writeFile} from 'xlsx';
import {message} from 'ant-design-vue';
import {pageShopDealerCapital} from "@/api/shop/shopDealerCapital";
import {ShopDealerCapital, ShopDealerCapitalParam} from "@/api/shop/shopDealerCapital/model";
import {getTenantId} from "@/utils/domain";
import useSearch from "@/utils/use-search";
const props = withDefaults(
defineProps<{
@@ -24,15 +30,85 @@
);
const emit = defineEmits<{
(e: 'search', where?: GradeParam): void;
(e: 'search', where?: ShopDealerCapitalParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 新增
const add = () => {
emit('add');
const reload = () => {
emit('search', where);
};
// 表单数据
const {where} = useSearch<ShopDealerCapitalParam>({
keywords: '',
userId: undefined,
toUserId: undefined,
limit: 5000
});
const list = ref<ShopDealerCapital[]>([]);
// 导出
const handleExport = async () => {
const array: (string | number)[][] = [
[
'用户ID',
'流动类型',
'金额',
'订单编号',
'对方用户ID',
`创建时间`,
'租户ID'
]
];
// 按搜索结果导出
await pageShopDealerCapital(where)
.then((data) => {
list.value = data?.list || [];
list.value?.forEach((d: ShopDealerCapital) => {
array.push([
`${d.userId}`,
`${d.flowType == 10 ? '佣金收入' : ''}`,
`${d.money}`,
`${d.orderNo}`,
`${d.toUserId}`,
`${d.createTime}`,
`${d.tenantId}`
]);
});
const sheetName = `bak_shop_dealer_capital_${getTenantId()}`;
const workbook = {
SheetNames: [sheetName],
Sheets: {}
};
const sheet = utils.aoa_to_sheet(array);
workbook.Sheets[sheetName] = sheet;
// 设置列宽
sheet['!cols'] = [
{wch: 10},
{wch: 20},
{wch: 20},
{wch: 15},
{wch: 10},
{wch: 10},
{wch: 20}
];
message.loading('正在导出...');
setTimeout(() => {
writeFile(
workbook,
`${sheetName}.xlsx`
);
}, 1000);
})
.catch((msg) => {
message.error(msg);
})
.finally(() => {
});
};
watch(

View File

@@ -112,7 +112,6 @@
dataIndex: 'userId',
key: 'userId',
align: 'center',
width: 100,
fixed: 'left'
},
{
@@ -120,7 +119,6 @@
dataIndex: 'flowType',
key: 'flowType',
align: 'center',
width: 120,
customRender: ({ text }) => {
const typeMap = {
10: { text: '佣金收入', color: 'success' },
@@ -137,7 +135,6 @@
dataIndex: 'money',
key: 'money',
align: 'center',
width: 120,
customRender: ({ text, record }) => {
const amount = parseFloat(text || '0').toFixed(2);
const isIncome = record.flowType === 10 || record.flowType === 40;
@@ -145,11 +142,10 @@
type: 'span',
props: {
style: {
color: isIncome ? '#52c41a' : '#ff4d4f',
fontWeight: 'bold'
color: isIncome ? '#424242' : '#ff4d4f',
}
},
children: `${isIncome ? '+' : '-'}¥${amount}`
children: `${isIncome ? '' : '-'} ${amount}`
};
}
},
@@ -165,7 +161,6 @@
dataIndex: 'toUserId',
key: 'toUserId',
align: 'center',
width: 100,
customRender: ({ text }) => text ? `ID: ${text}` : '-'
},
{
@@ -173,7 +168,6 @@
dataIndex: 'describe',
key: 'describe',
align: 'left',
width: 200,
ellipsis: true,
customRender: ({ text }) => text || '-'
},
@@ -182,24 +176,16 @@
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
sorter: true,
ellipsis: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
},
{
title: '修改时间',
dataIndex: 'updateTime',
key: 'updateTime',
align: 'center',
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
sorter: true
}
// {
// title: '操作',
// key: 'action',
// width: 180,
// fixed: 'right',
// align: 'center',
// hideInSetting: true
// }
]);
/* 搜索 */

View File

@@ -1,146 +1,132 @@
<template>
<div class="flex items-center gap-20">
<!-- 搜索表单 -->
<a-form
:model="where"
layout="inline"
class="search-form"
@finish="handleSearch"
>
<a-form-item>
<a-space>
<a-button
danger
class="ele-btn-icon"
v-if="selection.length > 0"
:disabled="selection?.length === 0"
@click="removeBatch"
>
<template #icon>
<DeleteOutlined/>
</template>
<span>批量删除</span>
</a-button>
</a-space>
</a-form-item>
<a-form-item>
<a-space>
<template>
<a-space :size="10" style="flex-wrap: wrap">
<a-input-search
allow-clear
placeholder="请输入关键词"
placeholder="客户名称|订单编号"
style="width: 240px"
v-model:value="where.keywords"
@search="handleSearch"
@search="reload"
/>
<a-button type="dashed" @click="handleExport">导出xls</a-button>
</a-space>
</a-form-item>
</a-form>
<a-divider type="vertical"/>
<!-- <a-space>-->
<!-- <a-button @click="exportData" class="ele-btn-icon">-->
<!-- <template #icon>-->
<!-- <ExportOutlined/>-->
<!-- </template>-->
<!-- 导出数据-->
<!-- </a-button>-->
<!-- </a-space>-->
</div>
<!-- 导入弹窗 -->
<Import v-model:visible="showImport" @done="emit('importDone')"/>
</template>
<script lang="ts" setup>
import {ref} from 'vue';
import {
DeleteOutlined
} from '@ant-design/icons-vue';
import type {ShopDealerOrderParam} from '@/api/shop/shopDealerOrder/model';
import Import from './Import.vue';
import type { GradeParam } from '@/api/user/grade/model';
import {ref, watch} from 'vue';
import {utils, writeFile} from 'xlsx';
import {message} from 'ant-design-vue';
import {ShopDealerCapital} from "@/api/shop/shopDealerCapital/model";
import {getTenantId} from "@/utils/domain";
import useSearch from "@/utils/use-search";
import {ShopDealerOrder, ShopDealerOrderParam} from "@/api/sdy/sdyDealerOrder/model";
import {pageShopDealerOrder} from "@/api/shop/shopDealerOrder";
withDefaults(
const props = withDefaults(
defineProps<{
// 选中的数据
selection?: any[];
// 选中的角色
selection?: [];
}>(),
{
selection: () => []
}
{}
);
const emit = defineEmits<{
(e: 'search', where?: ShopDealerOrderParam): void;
(e: 'batchSettle'): void;
(e: 'export'): void;
(e: 'importDone'): void;
(e: 'search', where?: GradeParam): void;
(e: 'add'): void;
(e: 'remove'): void;
(e: 'batchMove'): void;
}>();
// 是否显示导入弹窗
const showImport = ref(false);
const reload = () => {
emit('search', where);
};
// 搜索表单
const {where, resetFields} = useSearch<ShopDealerOrderParam>({
orderId: undefined,
orderNo: '',
productName: '',
isInvalid: undefined,
isSettled: undefined
// 表单数据
const {where} = useSearch<ShopDealerOrderParam>({
keywords: '',
userId: undefined,
orderNo: undefined,
limit: 5000
});
// 搜索
const handleSearch = () => {
const searchParams = {...where};
// 清除空值
Object.keys(searchParams).forEach(key => {
if (searchParams[key] === '' || searchParams[key] === undefined) {
delete searchParams[key];
}
const list = ref<ShopDealerCapital[]>([]);
// 导出
const handleExport = async () => {
const array: (string | number)[][] = [
[
'客户名称',
'业务员',
'订单编号',
'结算电量',
'换算成度',
'结算单价',
'结算金额',
'税费',
'实发金额',
'月份',
'创建时间',
'结算时间',
'租户ID'
]
];
// 按搜索结果导出
await pageShopDealerOrder(where)
.then((data) => {
list.value = data?.list || [];
list.value?.forEach((d: ShopDealerOrder) => {
array.push([
`${d.title}`,
`${d.nickname}(${d.userId})`,
`${d.orderNo}`,
`${d.orderPrice}`,
`${d.degreePrice}`,
`${d.price}`,
`${d.settledPrice}`,
`${d.rate}`,
`${d.payPrice}`,
`${d.month}`,
`${d.createTime}`,
`${d.settleTime}`,
`${d.tenantId}`
]);
});
const sheetName = `bak_shop_dealer_order_${getTenantId()}`;
const workbook = {
SheetNames: [sheetName],
Sheets: {}
};
const sheet = utils.aoa_to_sheet(array);
workbook.Sheets[sheetName] = sheet;
// 设置列宽
sheet['!cols'] = [
{wch: 10},
{wch: 20},
{wch: 20},
{wch: 15},
{wch: 10},
{wch: 10},
{wch: 20}
];
message.loading('正在导出...');
setTimeout(() => {
writeFile(
workbook,
`${sheetName}.xlsx`
);
}, 1000);
})
.catch((msg) => {
message.error(msg);
})
.finally(() => {
});
emit('search', searchParams);
};
// 重置搜索
const resetSearch = () => {
// Object.keys(searchForm).forEach(key => {
// searchForm[key] = key === 'orderId' ? undefined : '';
// });
resetFields();
emit('search', {});
};
// 批量删除
const removeBatch = () => {
emit('remove');
};
// 导出数据
const exportData = () => {
emit('export');
};
watch(
() => props.selection,
() => {}
);
</script>
<style lang="less" scoped>
.search-container {
background: #fff;
padding: 16px;
border-radius: 6px;
margin-bottom: 16px;
.search-form {
margin-bottom: 16px;
:deep(.ant-form-item) {
margin-bottom: 8px;
}
}
.action-buttons {
border-top: 1px solid #f0f0f0;
padding-top: 16px;
}
}
</style>

View File

@@ -24,7 +24,7 @@
<template v-if="column.key === 'title'">
<div>{{ record.title }}</div>
<div class="text-gray-400">用户ID{{ record.userId }}</div>
<div class="text-gray-400">业务员{{ record.userId }}</div>
</template>
<template v-if="column.key === 'orderPrice'">
@@ -194,7 +194,13 @@ const columns = ref<ColumnItem[]>([
title: '客户名称',
dataIndex: 'title',
key: 'title',
width: 180
width: 150
},
{
title: '订单编号',
dataIndex: 'orderNo',
key: 'orderNo',
align: 'center'
},
{
title: '结算电量',