feat(sdy): 实现经销商订单结算功能

- 新增结算订单API接口
-优化订单编辑页面字段展示和计算逻辑
- 调整订单状态标签及删除条件限制- 增加订单导入弹窗组件- 修复重复结算问题并更新相关UI交互
This commit is contained in:
2025-10-01 19:25:51 +08:00
parent 320a1939b6
commit c7188ee8eb
5 changed files with 143 additions and 112 deletions

View File

@@ -118,3 +118,17 @@ export async function exportSdyDealerOrder(params?: ShopDealerOrderParam) {
message.error(error.message || '导出失败,请重试'); message.error(error.message || '导出失败,请重试');
} }
} }
/**
* 结算订单
*/
export async function updateSdyDealerOrder(data: ShopDealerOrder) {
const res = await request.put<ApiResult<unknown>>(
'/sdy/sdy-dealer-order',
data
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}

View File

@@ -62,6 +62,7 @@
</a-button> </a-button>
<a-button <a-button
type="primary" type="primary"
danger
@click="batchSettle" @click="batchSettle"
class="ele-btn-icon" class="ele-btn-icon"
> >

View File

@@ -37,16 +37,23 @@
</a-col> </a-col>
<a-col :span="12"> <a-col :span="12">
<a-form-item label="费率" name="rate"> <a-form-item label="结算电量" name="orderPrice">
{{ parseFloat(form.orderPrice || 0).toFixed(2) }}
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="收益比率" name="rate">
{{ form.rate }} {{ form.rate }}
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="12"> <a-col :span="12">
<a-form-item label="结算电量" name="orderPrice"> <a-form-item label="结算金额" name="payPrice">
{{ parseFloat(form.orderPrice || 0).toFixed(2) }} {{ (form.orderPrice * form.rate * 1000).toFixed(2) }}
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="12"> <a-col :span="12">
<a-form-item label="实发金额" name="payPrice"> <a-form-item label="实发金额" name="payPrice">
{{ (form.orderPrice * form.rate * 1000).toFixed(2) }} {{ (form.orderPrice * form.rate * 1000).toFixed(2) }}
@@ -69,16 +76,17 @@
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="12"> <a-col :span="12">
<a-form-item label="用户ID" name="firstUserId"> <a-form-item label="用户ID" name="firstUserId">
<a-input-number {{ form.firstUserId }}
:min="1" </a-form-item>
placeholder="请输入一级分销商用户ID" <a-form-item label="昵称" name="firstNickname">
v-model:value="form.firstUserId" {{ form.firstNickname }}
style="width: 100%"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="12"> <a-col :span="12">
<a-form-item label="分销佣金" name="firstMoney"> <a-form-item label="收益比率" name="rate">
{{ '70%' }}
</a-form-item>
<a-form-item label="获取收益" name="firstMoney">
<a-input-number <a-input-number
:min="0" :min="0"
:precision="2" :precision="2"
@@ -96,21 +104,22 @@
<!-- 二级分销商 --> <!-- 二级分销商 -->
<div class="dealer-section"> <div class="dealer-section">
<h4 class="dealer-title"> <h4 class="dealer-title">
<a-tag color="orange">推收益</a-tag> <a-tag color="orange">推收益</a-tag>
</h4> </h4>
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="12"> <a-col :span="12">
<a-form-item label="用户ID" name="secondUserId"> <a-form-item label="用户ID" name="secondUserId">
<a-input-number {{ form.secondUserId }}
:min="1" </a-form-item>
placeholder="请输入二级分销商用户ID" <a-form-item label="昵称" name="secondNickname">
v-model:value="form.secondUserId" {{ form.secondNickname }}
style="width: 100%"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="12"> <a-col :span="12">
<a-form-item label="分销佣金" name="secondMoney"> <a-form-item label="收益比率" name="rate">
{{ '30%' }}
</a-form-item>
<a-form-item label="获取收益" name="secondMoney">
<a-input-number <a-input-number
:min="0" :min="0"
:precision="2" :precision="2"
@@ -126,12 +135,7 @@
</div> </div>
<a-form-item label="结算时间" name="settleTime" v-if="form.isSettled === 1"> <a-form-item label="结算时间" name="settleTime" v-if="form.isSettled === 1">
<a-date-picker {{ form.settleTime }}
v-model:value="form.settleTime"
show-time
placeholder="请选择结算时间"
style="width: 300px"
/>
</a-form-item> </a-form-item>
</a-form> </a-form>
</ele-modal> </ele-modal>
@@ -140,22 +144,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, watch } from 'vue'; import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue'; import { Form, message } from 'ant-design-vue';
import dayjs from 'dayjs'; import { assignObject } from 'ele-admin-pro';
import { assignObject, uuid } from 'ele-admin-pro';
import { addShopDealerOrder, updateShopDealerOrder } from '@/api/shop/shopDealerOrder';
import { ShopDealerOrder } from '@/api/shop/shopDealerOrder/model'; import { ShopDealerOrder } from '@/api/shop/shopDealerOrder/model';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FormInstance } from 'ant-design-vue/es/form'; import { FormInstance } from 'ant-design-vue/es/form';
import { FileRecord } from '@/api/system/file/model'; import {updateSdyDealerOrder} from "@/api/sdy/sdyDealerOrder";
// 是否是修改 // 是否是修改
const isUpdate = ref(false); const isUpdate = ref(false);
const useForm = Form.useForm; const useForm = Form.useForm;
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{ const props = defineProps<{
// 弹窗是否打开 // 弹窗是否打开
@@ -175,7 +171,6 @@
const maxable = ref(true); const maxable = ref(true);
// 表格选中数据 // 表格选中数据
const formRef = ref<FormInstance | null>(null); const formRef = ref<FormInstance | null>(null);
const images = ref<ItemType[]>([]);
// 表单数据 // 表单数据
const form = reactive<ShopDealerOrder>({ const form = reactive<ShopDealerOrder>({
@@ -210,79 +205,13 @@
// 表单验证规则 // 表单验证规则
const rules = reactive({ const rules = reactive({
orderId: [ userId: [
{ {
required: true, required: true,
message: '请输入订单ID', message: '请选择用户ID',
trigger: 'blur' trigger: 'blur'
} }
], ],
firstUserId: [
{
validator: (rule: any, value: any) => {
if (form.firstMoney && !value) {
return Promise.reject('设置了一级佣金必须填写一级分销商用户ID');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
firstMoney: [
{
validator: (rule: any, value: any) => {
if (form.firstUserId && !value) {
return Promise.reject('设置了一级分销商必须填写一级佣金');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
secondUserId: [
{
validator: (rule: any, value: any) => {
if (form.secondMoney && !value) {
return Promise.reject('设置了二级佣金必须填写二级分销商用户ID');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
secondMoney: [
{
validator: (rule: any, value: any) => {
if (form.secondUserId && !value) {
return Promise.reject('设置了二级分销商必须填写二级佣金');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
thirdUserId: [
{
validator: (rule: any, value: any) => {
if (form.thirdMoney && !value) {
return Promise.reject('设置了三级佣金必须填写三级分销商用户ID');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
thirdMoney: [
{
validator: (rule: any, value: any) => {
if (form.thirdUserId && !value) {
return Promise.reject('设置了三级分销商必须填写三级佣金');
}
return Promise.resolve();
},
trigger: 'blur'
}
]
}); });
@@ -294,6 +223,10 @@
if (!formRef.value) { if (!formRef.value) {
return; return;
} }
if(form.isSettled == 1){
message.error('请勿重复结算');
return;
}
if(form.userId == 0){ if(form.userId == 0){
message.error('未签约'); message.error('未签约');
return; return;
@@ -303,10 +236,10 @@
.then(() => { .then(() => {
loading.value = true; loading.value = true;
const formData = { const formData = {
...form ...form,
isSettled: 1,
}; };
const saveOrUpdate = isUpdate.value ? updateShopDealerOrder : addShopDealerOrder; updateSdyDealerOrder(formData)
saveOrUpdate(formData)
.then((msg) => { .then((msg) => {
loading.value = false; loading.value = false;
message.success(msg); message.success(msg);
@@ -327,9 +260,8 @@
if (visible) { if (visible) {
if (props.data) { if (props.data) {
assignObject(form, props.data); assignObject(form, props.data);
// 处理时间字段 if(props.data.orderPrice && props.data.rate){
if (props.data.settleTime) { form.firstMoney = (Number(props.data.orderPrice) * props.data.rate * 1000 * 0.5).toFixed(2)
form.settleTime = dayjs(props.data.settleTime);
} }
isUpdate.value = true; isUpdate.value = true;
} else { } else {

View File

@@ -66,7 +66,7 @@
<template v-if="column.key === 'isInvalid'"> <template v-if="column.key === 'isInvalid'">
<a-tag v-if="record.isInvalid === 0" color="success">已签约</a-tag> <a-tag v-if="record.isInvalid === 0" color="success">已签约</a-tag>
<a-tag v-if="record.isInvalid === 1" color="error" @click="invalidateOrder(record)">未签约</a-tag> <a-tag v-if="record.isInvalid === 1" color="error">未签约</a-tag>
</template> </template>
<template v-if="column.key === 'isSettled'"> <template v-if="column.key === 'isSettled'">
@@ -104,6 +104,7 @@
<!-- </a-popconfirm>--> <!-- </a-popconfirm>-->
<!-- </template>--> <!-- </template>-->
<a-popconfirm <a-popconfirm
v-if="record.isSettled === 0"
title="确定要删除吗?" title="确定要删除吗?"
@confirm="remove(record)" @confirm="remove(record)"
placement="topRight" placement="topRight"
@@ -139,7 +140,7 @@ import {getPageTitle} from '@/utils/common';
import ShopDealerOrderEdit from './components/shopDealerOrderEdit.vue'; import ShopDealerOrderEdit from './components/shopDealerOrderEdit.vue';
import {pageShopDealerOrder, removeShopDealerOrder, removeBatchShopDealerOrder} from '@/api/shop/shopDealerOrder'; import {pageShopDealerOrder, removeShopDealerOrder, removeBatchShopDealerOrder} from '@/api/shop/shopDealerOrder';
import type {ShopDealerOrder, ShopDealerOrderParam} from '@/api/shop/shopDealerOrder/model'; import type {ShopDealerOrder, ShopDealerOrderParam} from '@/api/shop/shopDealerOrder/model';
import {exportSdyDealerOrder} from "@/api/sdy/sdyDealerOrder"; import {exportSdyDealerOrder, updateSdyDealerOrder} from "@/api/sdy/sdyDealerOrder";
// 表格实例 // 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null); const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
@@ -270,7 +271,7 @@ const settleOrder = (row: ShopDealerOrder) => {
Modal.confirm({ Modal.confirm({
title: '确认结算', title: '确认结算',
content: `确定要结算此订单的佣金吗?总佣金金额:¥${totalCommission}`, content: `确定要结算此订单吗?总佣金金额:¥${totalCommission}`,
icon: createVNode(DollarOutlined), icon: createVNode(DollarOutlined),
okText: '确认结算', okText: '确认结算',
okType: 'primary', okType: 'primary',
@@ -278,6 +279,10 @@ const settleOrder = (row: ShopDealerOrder) => {
onOk: () => { onOk: () => {
const hide = message.loading('正在结算...', 0); const hide = message.loading('正在结算...', 0);
// 这里调用结算API // 这里调用结算API
updateSdyDealerOrder({
...row,
isSettled: 1
})
setTimeout(() => { setTimeout(() => {
hide(); hide();
message.success('结算成功'); message.success('结算成功');

View File

@@ -0,0 +1,79 @@
<!-- 经销商订单导入弹窗 -->
<template>
<ele-modal
:width="520"
:footer="null"
title="导入分销订单"
:visible="visible"
@update:visible="updateVisible"
>
<a-spin :spinning="loading">
<a-upload-dragger
accept=".xls,.xlsx"
:show-upload-list="false"
:customRequest="doUpload"
style="padding: 24px 0; margin-bottom: 16px"
>
<p class="ant-upload-drag-icon">
<cloud-upload-outlined />
</p>
<p class="ant-upload-hint">将文件拖到此处或点击上传</p>
</a-upload-dragger>
</a-spin>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { message } from 'ant-design-vue/es';
import { CloudUploadOutlined } from '@ant-design/icons-vue';
import { importShopDealerOrder } from '@/api/shop/shopDealerOrder';
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
defineProps<{
// 是否打开弹窗
visible: boolean;
}>();
// 导入请求状态
const loading = ref(false);
/* 上传 */
const doUpload = ({ file }) => {
if (
![
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
].includes(file.type)
) {
message.error('只能选择 excel 文件');
return false;
}
if (file.size / 1024 / 1024 > 10) {
message.error('大小不能超过 10MB');
return false;
}
loading.value = true;
importShopDealerOrder(file)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
return false;
};
/* 更新 visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
</script>