Files
mp-vue/src/views/shop/shopDealerOrder/index.vue
赵忠林 3ecfaeae87 feat(sdy): 更新经销商订单模型和导入功能
- 将 orderId 字段更改为 orderNo,类型从 number 改为 string
- 更新经销商资本模型中的 describe 字段为 comments- 在 shopDealerOrder 模型中添加 settledPrice 和 payPrice 字段
- 修改导入接口 URL,使用 MODULES_API_URL 前缀- 在用户验证页面添加权限控制
- 移除订单状态和结算状态的搜索选项
- 添加新的经销商订单编辑组件和相关功能
- 创建新的经销商订单管理页面,包含完整的CRUD操作- 添加批量删除和订单结算功能- 更新表格列配置,展示更多订单详情信息
2025-10-13 23:03:44 +08:00

487 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="shopDealerOrderId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
@batchSettle="batchSettle"
@export="handleExport"
@importDone="reload"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'orderInfo'">
<div class="order-info">
<div class="order-id">订单号: {{ record.orderId || '-' }}</div>
<div class="order-price">金额: ¥{{ parseFloat(record.orderPrice || '0').toFixed(2) }}</div>
</div>
</template>
<template v-if="column.key === 'dealerInfo'">
<div class="dealer-info">
<div v-if="record.firstUserId" class="dealer-level">
<a-tag color="red">一级</a-tag>
用户{{ record.firstUserId }} - ¥{{ parseFloat(record.firstMoney || '0').toFixed(2) }}
</div>
<div v-if="record.secondUserId" class="dealer-level">
<a-tag color="orange">二级</a-tag>
用户{{ record.secondUserId }} - ¥{{ parseFloat(record.secondMoney || '0').toFixed(2) }}
</div>
<div v-if="record.thirdUserId" class="dealer-level">
<a-tag color="gold">三级</a-tag>
用户{{ record.thirdUserId }} - ¥{{ parseFloat(record.thirdMoney || '0').toFixed(2) }}
</div>
</div>
</template>
<template v-if="column.key === 'isInvalid'">
<a-tag v-if="record.isInvalid === 0" color="success">有效</a-tag>
<a-tag v-if="record.isInvalid === 1" color="error">失效</a-tag>
</template>
<template v-if="column.key === 'isSettled'">
<a-tag v-if="record.isSettled === 0" color="processing">未结算</a-tag>
<a-tag v-if="record.isSettled === 1" color="success">已结算</a-tag>
</template>
<template v-if="column.key === 'action'">
<a @click="viewDetail(record)" class="ele-text-info">
<EyeOutlined/>
详情
</a>
<template v-if="record.isSettled === 0 && record.isInvalid === 0">
<a-divider type="vertical"/>
<a @click="openEdit(record)" class="ele-text-success">
<DollarOutlined/>
结算
</a>
</template>
<template v-if="record.isInvalid === 0">
<a-divider type="vertical"/>
<a-popconfirm
title="确定要标记此订单为失效吗?"
@confirm="invalidateOrder(record)"
placement="topRight"
>
<a class="ele-text-warning">
<CloseOutlined/>
失效
</a>
</a-popconfirm>
</template>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<ShopDealerOrderEdit v-model:visible="showEdit" :data="current" @done="reload"/>
</a-page-header>
</template>
<script lang="ts" setup>
import {createVNode, ref} from 'vue';
import {message, Modal} from 'ant-design-vue';
import {
ExclamationCircleOutlined,
EyeOutlined,
DollarOutlined,
CloseOutlined
} from '@ant-design/icons-vue';
import type {EleProTable} from 'ele-admin-pro';
import {toDateString} from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import {getPageTitle} from '@/utils/common';
import ShopDealerOrderEdit from './components/shopDealerOrderEdit.vue';
import {
pageShopDealerOrder,
removeShopDealerOrder,
removeBatchShopDealerOrder,
exportShopDealerOrder
} from '@/api/shop/shopDealerOrder';
import type {ShopDealerOrder, ShopDealerOrderParam} from '@/api/shop/shopDealerOrder/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<ShopDealerOrder[]>([]);
// 当前编辑数据
const current = ref<ShopDealerOrder | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 加载状态
const loading = ref(true);
// 当前搜索条件
const currentWhere = ref<ShopDealerOrderParam>({});
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
// 保存当前搜索条件用于导出
currentWhere.value = {...where};
return pageShopDealerOrder({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: '商品信息',
key: 'productInfo',
align: 'left',
width: 200,
customRender: ({record}) => {
return `商品ID: ${record.productId || '-'}`;
}
},
{
title: '单价/数量',
key: 'priceInfo',
align: 'center',
width: 120,
customRender: ({record}) => {
return `¥${parseFloat(record.unitPrice || '0').toFixed(2)} × ${record.quantity || 1}`;
}
},
{
title: '订单信息',
key: 'orderInfo',
align: 'left',
width: 180
},
{
title: '买家',
dataIndex: 'userId',
key: 'userId',
align: 'center',
width: 100,
customRender: ({text}) => `用户${text || '-'}`
},
{
title: '分销商信息',
key: 'dealerInfo',
align: 'left',
width: 300
},
{
title: '订单状态',
dataIndex: 'isInvalid',
key: 'isInvalid',
align: 'center',
width: 100,
filters: [
{text: '有效', value: 0},
{text: '失效', value: 1}
]
},
{
title: '结算状态',
dataIndex: 'isSettled',
key: 'isSettled',
align: 'center',
width: 100,
filters: [
{text: '未结算', value: 0},
{text: '已结算', value: 1}
]
},
{
title: '结算时间',
dataIndex: 'settleTime',
key: 'settleTime',
align: 'center',
width: 120,
customRender: ({text}) => text ? toDateString(new Date(text), 'yyyy-MM-dd HH:mm') : '-'
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
width: 180,
sorter: true,
customRender: ({text}) => toDateString(text, 'yyyy-MM-dd HH:mm')
},
{
title: '操作',
key: 'action',
width: 240,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: ShopDealerOrderParam) => {
selection.value = [];
tableRef?.value?.reload({where: where});
};
/* 查看订单详情 */
const viewDetail = (row: ShopDealerOrder) => {
Modal.info({
title: '分销订单详情',
width: 800,
content: createVNode('div', {style: 'max-height: 500px; overflow-y: auto;'}, [
createVNode('div', {class: 'detail-section'}, [
createVNode('h4', null, '订单基本信息'),
createVNode('p', null, `订单ID: ${row.orderId || '-'}`),
createVNode('p', null, `买家用户ID: ${row.userId || '-'}`),
createVNode('p', null, `订单金额: ¥${parseFloat(row.orderPrice || '0').toFixed(2)}`),
createVNode('p', null, `创建时间: ${row.createTime ? toDateString(row.createTime, 'yyyy-MM-dd HH:mm:ss') : '-'}`),
]),
createVNode('div', {class: 'detail-section', style: 'margin-top: 16px;'}, [
createVNode('h4', null, '分销商信息'),
...(row.firstUserId ? [
createVNode('div', {style: 'margin: 8px 0; padding: 8px; background: #fff2f0; border-left: 3px solid #ff4d4f;'}, [
createVNode('strong', null, '一级分销商'),
createVNode('p', null, `用户ID: ${row.firstUserId}`),
createVNode('p', null, `佣金: ¥${parseFloat(row.firstMoney || '0').toFixed(2)}`)
])
] : []),
...(row.secondUserId ? [
createVNode('div', {style: 'margin: 8px 0; padding: 8px; background: #fff7e6; border-left: 3px solid #fa8c16;'}, [
createVNode('strong', null, '二级分销商'),
createVNode('p', null, `用户ID: ${row.secondUserId}`),
createVNode('p', null, `佣金: ¥${parseFloat(row.secondMoney || '0').toFixed(2)}`)
])
] : []),
...(row.thirdUserId ? [
createVNode('div', {style: 'margin: 8px 0; padding: 8px; background: #fffbe6; border-left: 3px solid #fadb14;'}, [
createVNode('strong', null, '三级分销商'),
createVNode('p', null, `用户ID: ${row.thirdUserId}`),
createVNode('p', null, `佣金: ¥${parseFloat(row.thirdMoney || '0').toFixed(2)}`)
])
] : [])
]),
createVNode('div', {class: 'detail-section', style: 'margin-top: 16px;'}, [
createVNode('h4', null, '状态信息'),
createVNode('p', null, [
'订单状态: ',
createVNode('span', {
style: `color: ${row.isInvalid === 0 ? '#52c41a' : '#ff4d4f'}; font-weight: bold;`
}, row.isInvalid === 0 ? '有效' : '失效')
]),
createVNode('p', null, [
'结算状态: ',
createVNode('span', {
style: `color: ${row.isSettled === 1 ? '#52c41a' : '#1890ff'}; font-weight: bold;`
}, row.isSettled === 1 ? '已结算' : '未结算')
]),
createVNode('p', null, `结算时间: ${row.settleTime ? toDateString(new Date(row.settleTime), 'yyyy-MM-dd HH:mm:ss') : '-'}`),
])
]),
okText: '关闭'
});
};
/* 标记订单失效 */
const invalidateOrder = (row: ShopDealerOrder) => {
const hide = message.loading('正在处理...', 0);
// 这里调用失效API
setTimeout(() => {
hide();
message.success('订单已标记为失效');
reload();
}, 1000);
};
/* 批量结算 */
const batchSettle = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
const validOrders = selection.value.filter(order =>
order.isSettled === 0 && order.isInvalid === 0
);
if (!validOrders.length) {
message.error('所选订单中没有可结算的订单');
return;
}
const totalCommission = validOrders.reduce((sum, order) => {
return sum + parseFloat(order.firstMoney || '0') +
parseFloat(order.secondMoney || '0') +
parseFloat(order.thirdMoney || '0');
}, 0).toFixed(2);
Modal.confirm({
title: '批量结算确认',
content: `确定要结算选中的 ${validOrders.length} 个订单吗?总佣金金额:¥${totalCommission}`,
icon: createVNode(ExclamationCircleOutlined),
okText: '确认结算',
okType: 'primary',
cancelText: '取消',
onOk: () => {
const hide = message.loading('正在批量结算...', 0);
// 这里调用批量结算API
setTimeout(() => {
hide();
message.success(`成功结算 ${validOrders.length} 个订单`);
reload();
}, 1500);
}
});
};
/* 导出数据 */
const handleExport = () => {
// 调用导出API传入当前搜索条件
exportShopDealerOrder(currentWhere.value);
};
/* 打开编辑弹窗 */
const openEdit = (row?: ShopDealerOrder) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 删除单个 */
const remove = (row: ShopDealerOrder) => {
const hide = message.loading('请求中..', 0);
removeShopDealerOrder(row.id)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchShopDealerOrder(selection.value.map((d) => d.id))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 查询 */
const query = () => {
loading.value = true;
};
/* 自定义行属性 */
const customRow = (record: ShopDealerOrder) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
query();
</script>
<script lang="ts">
export default {
name: 'ShopDealerOrder'
};
</script>
<style lang="less" scoped>
.order-info {
.order-id {
font-weight: 500;
color: #333;
margin-bottom: 4px;
}
.order-price {
color: #ff4d4f;
font-weight: 600;
}
}
.dealer-info {
.dealer-level {
margin-bottom: 6px;
font-size: 12px;
&:last-child {
margin-bottom: 0;
}
}
}
:deep(.detail-section) {
h4 {
color: #1890ff;
margin-bottom: 12px;
border-bottom: 1px solid #f0f0f0;
padding-bottom: 8px;
}
p {
margin: 4px 0;
line-height: 1.5;
}
}
:deep(.ant-table-tbody > tr > td) {
vertical-align: top;
}
:deep(.ant-tag) {
margin: 2px 4px 2px 0;
}
</style>