style(ai): 格式化 AI API 错误消息和调整 AI 视图布局

- 格式化 Ollama 和 OpenAI API 的错误消息字符串以提高可读性
- 移除 AI 视图中的 BaseURL 输入字段并硬编码为固定端点
- 简化 AI 视图中 API 调用的基础 URL 配置逻辑
- 修复多个组件中的代码格式和空格缩进问题
- 清理经销商订单视图中的多余注释和代码结构
- 调整表单组件的标签和布局格式以提升用户体验
This commit is contained in:
2026-02-28 00:53:26 +08:00
parent cc01095107
commit 91708315f3
17 changed files with 846 additions and 796 deletions

View File

@@ -48,7 +48,9 @@ export async function listOllamaModels(opts?: { baseURL?: string }) {
if (!res.ok) { if (!res.ok) {
const text = await res.text().catch(() => ''); const text = await res.text().catch(() => '');
throw new Error( throw new Error(
`listOllamaModels failed: ${res.status} ${res.statusText}${text ? ` - ${text}` : ''}` `listOllamaModels failed: ${res.status} ${res.statusText}${
text ? ` - ${text}` : ''
}`
); );
} }
return (await res.json()) as OllamaTagsResponse; return (await res.json()) as OllamaTagsResponse;
@@ -68,7 +70,9 @@ export async function ollamaChat(
if (!res.ok) { if (!res.ok) {
const text = await res.text().catch(() => ''); const text = await res.text().catch(() => '');
throw new Error( throw new Error(
`ollamaChat failed: ${res.status} ${res.statusText}${text ? ` - ${text}` : ''}` `ollamaChat failed: ${res.status} ${res.statusText}${
text ? ` - ${text}` : ''
}`
); );
} }
return (await res.json()) as OllamaChatResponseChunk; return (await res.json()) as OllamaChatResponseChunk;
@@ -97,7 +101,9 @@ export async function ollamaChatStream(
if (!res.ok || !res.body) { if (!res.ok || !res.body) {
const text = await res.text().catch(() => ''); const text = await res.text().catch(() => '');
throw new Error( throw new Error(
`ollamaChatStream failed: ${res.status} ${res.statusText}${text ? ` - ${text}` : ''}` `ollamaChatStream failed: ${res.status} ${res.statusText}${
text ? ` - ${text}` : ''
}`
); );
} }
@@ -143,4 +149,3 @@ export async function ollamaChatStream(
opts.onDone?.(); opts.onDone?.();
} }

View File

@@ -71,7 +71,9 @@ export async function listModels(opts?: { apiKey?: string; baseURL?: string }) {
if (!res.ok) { if (!res.ok) {
const text = await res.text().catch(() => ''); const text = await res.text().catch(() => '');
throw new Error( throw new Error(
`listModels failed: ${res.status} ${res.statusText}${text ? ` - ${text}` : ''}` `listModels failed: ${res.status} ${res.statusText}${
text ? ` - ${text}` : ''
}`
); );
} }
return (await res.json()) as OpenAIListModelsResponse; return (await res.json()) as OpenAIListModelsResponse;
@@ -91,7 +93,9 @@ export async function chatCompletions(
if (!res.ok) { if (!res.ok) {
const text = await res.text().catch(() => ''); const text = await res.text().catch(() => '');
throw new Error( throw new Error(
`chatCompletions failed: ${res.status} ${res.statusText}${text ? ` - ${text}` : ''}` `chatCompletions failed: ${res.status} ${res.statusText}${
text ? ` - ${text}` : ''
}`
); );
} }
return (await res.json()) as OpenAIChatCompletionResponse; return (await res.json()) as OpenAIChatCompletionResponse;
@@ -124,7 +128,9 @@ export async function chatCompletionsStream(
if (!res.ok || !res.body) { if (!res.ok || !res.body) {
const text = await res.text().catch(() => ''); const text = await res.text().catch(() => '');
throw new Error( throw new Error(
`chatCompletionsStream failed: ${res.status} ${res.statusText}${text ? ` - ${text}` : ''}` `chatCompletionsStream failed: ${res.status} ${res.statusText}${
text ? ` - ${text}` : ''
}`
); );
} }

View File

@@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, onBeforeUnmount, ref } from 'vue'; import { computed, onBeforeUnmount, ref } from 'vue';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { OLLAMA_API_URL } from '@/config/setting';
import { import {
listOllamaModels, listOllamaModels,
ollamaChat, ollamaChat,
@@ -11,8 +10,9 @@
type Msg = OllamaChatMessage; type Msg = OllamaChatMessage;
// Only keep Ollama native API mode. // Hardcode endpoint to avoid going through mp.websoft.top `/proxy`.
const baseURL = ref<string>(OLLAMA_API_URL); // The API methods append `/api/*` paths.
const BASE_URL = 'https://ai-api.websoft.top';
const modelLoading = ref(false); const modelLoading = ref(false);
const models = ref<Array<{ id: string; name?: string }>>([]); const models = ref<Array<{ id: string; name?: string }>>([]);
@@ -46,11 +46,8 @@
modelLoading.value = true; modelLoading.value = true;
errorText.value = ''; errorText.value = '';
try { try {
if (!baseURL.value.trim()) {
baseURL.value = OLLAMA_API_URL;
}
const res = await listOllamaModels({ const res = await listOllamaModels({
baseURL: baseURL.value.trim() || OLLAMA_API_URL baseURL: BASE_URL
}); });
models.value = (res.models ?? []).map((m) => ({ models.value = (res.models ?? []).map((m) => ({
id: m.name, id: m.name,
@@ -100,7 +97,7 @@
options: { temperature: temperature.value } options: { temperature: temperature.value }
}, },
{ {
baseURL: baseURL.value.trim() || OLLAMA_API_URL, baseURL: BASE_URL,
signal: controller.signal, signal: controller.signal,
onDelta: (t) => { onDelta: (t) => {
assistantText.value += t; assistantText.value += t;
@@ -115,7 +112,7 @@
options: { temperature: temperature.value } options: { temperature: temperature.value }
}, },
{ {
baseURL: baseURL.value.trim() || OLLAMA_API_URL, baseURL: BASE_URL,
signal: controller.signal signal: controller.signal
} }
); );
@@ -165,16 +162,6 @@
description="支持Qwen3.5、DeepSeek、Gemini3等主流的开源大模型免费使用" description="支持Qwen3.5、DeepSeek、Gemini3等主流的开源大模型免费使用"
/> />
<a-row :gutter="12">
<a-col :xs="24" :md="12">
<a-input
v-model:value="baseURL"
addon-before="BaseURL"
placeholder="http://localhost:11434"
/>
</a-col>
</a-row>
<a-row :gutter="12"> <a-row :gutter="12">
<a-col :xs="24" :md="12"> <a-col :xs="24" :md="12">
<a-select <a-select

View File

@@ -62,9 +62,9 @@
<div class="text-gray-400" <div class="text-gray-400"
>{{ record.thirdNickname || '-' }} {{ record.thirdMoney }}</div >{{ record.thirdNickname || '-' }} {{ record.thirdMoney }}</div
> >
<!-- <div class="text-gray-400" v-if="record.thirdNickname"--> <!-- <div class="text-gray-400" v-if="record.thirdNickname"-->
<!-- >{{ record.thirdNickname }} {{ record.thirdMoney }}</div--> <!-- >{{ record.thirdNickname }} {{ record.thirdMoney }}</div-->
<!-- >--> <!-- >-->
</template> </template>
<template v-if="column.key === 'dealerInfo'"> <template v-if="column.key === 'dealerInfo'">

View File

@@ -56,15 +56,7 @@
// 导出 // 导出
const handleExport = async () => { const handleExport = async () => {
const array: (string | number)[][] = [ const array: (string | number)[][] = [
[ ['订单号', '用户', '收益类型', '金额', '描述', '创建时间', '租户ID']
'订单号',
'用户',
'收益类型',
'金额',
'描述',
'创建时间',
'租户ID'
]
]; ];
// 按搜索结果导出 // 按搜索结果导出

View File

@@ -91,7 +91,8 @@
? `\n${d.nickname ?? '-'}(${d.userId ?? '-'})` ? `\n${d.nickname ?? '-'}(${d.userId ?? '-'})`
: ''); : '');
const firstDividendUserName = (d as any)?.firstDividendUserName ?? '-'; const firstDividendUserName =
(d as any)?.firstDividendUserName ?? '-';
const firstDividend = (d as any)?.firstDividend ?? 0; const firstDividend = (d as any)?.firstDividend ?? 0;
const secondDividendUserName = const secondDividendUserName =
(d as any)?.secondDividendUserName ?? '-'; (d as any)?.secondDividendUserName ?? '-';

View File

@@ -11,9 +11,7 @@
class="sys-org-table" class="sys-org-table"
> >
<template #toolbar> <template #toolbar>
<search <search @search="reload" />
@search="reload"
/>
</template> </template>
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'title'"> <template v-if="column.key === 'title'">
@@ -55,16 +53,16 @@
<template v-if="column.key === 'firstDividendUserName'"> <template v-if="column.key === 'firstDividendUserName'">
<div>{{ record.firstDividend }}</div> <div>{{ record.firstDividend }}</div>
<div class="text-gray-400" <div class="text-gray-400">{{
>{{ record.firstDividendUserName || '-' }}</div record.firstDividendUserName || '-'
> }}</div>
</template> </template>
<template v-if="column.key === 'secondDividendUserName'"> <template v-if="column.key === 'secondDividendUserName'">
<div>{{ record.secondDividend }}</div> <div>{{ record.secondDividend }}</div>
<div class="text-gray-400" <div class="text-gray-400">{{
>{{ record.secondDividendUserName || '-' }}</div record.secondDividendUserName || '-'
> }}</div>
</template> </template>
<template v-if="column.key === 'dealerInfo'"> <template v-if="column.key === 'dealerInfo'">
@@ -175,9 +173,7 @@
ShopDealerOrder, ShopDealerOrder,
ShopDealerOrderParam ShopDealerOrderParam
} from '@/api/shop/shopDealerOrder/model'; } from '@/api/shop/shopDealerOrder/model';
import { import { updateShopDealerOrder } from '@/api/shop/shopDealerOrder';
updateShopDealerOrder
} from '@/api/shop/shopDealerOrder';
// 表格实例 // 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null); const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);

View File

@@ -15,7 +15,7 @@
style="padding: 24px 0; margin-bottom: 16px" style="padding: 24px 0; margin-bottom: 16px"
> >
<p class="ant-upload-drag-icon"> <p class="ant-upload-drag-icon">
<cloud-upload-outlined/> <cloud-upload-outlined />
</p> </p>
<p class="ant-upload-hint">将文件拖到此处或点击上传</p> <p class="ant-upload-hint">将文件拖到此处或点击上传</p>
</a-upload-dragger> </a-upload-dragger>
@@ -24,56 +24,56 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {ref} from 'vue'; import { ref } from 'vue';
import {message} from 'ant-design-vue/es'; import { message } from 'ant-design-vue/es';
import {CloudUploadOutlined} from '@ant-design/icons-vue'; import { CloudUploadOutlined } from '@ant-design/icons-vue';
import {importSdyDealerOrder} from "@/api/sdy/sdyDealerOrder"; import { importSdyDealerOrder } from '@/api/sdy/sdyDealerOrder';
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'done'): void; (e: 'done'): void;
(e: 'update:visible', visible: boolean): void; (e: 'update:visible', visible: boolean): void;
}>(); }>();
defineProps<{ defineProps<{
// 是否打开弹窗 // 是否打开弹窗
visible: boolean; visible: boolean;
}>(); }>();
// 导入请求状态 // 导入请求状态
const loading = ref(false); const loading = ref(false);
/* 上传 */ /* 上传 */
const doUpload = ({file}) => { const doUpload = ({ file }) => {
if ( if (
![ ![
'application/vnd.ms-excel', 'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
].includes(file.type) ].includes(file.type)
) { ) {
message.error('只能选择 excel 文件'); message.error('只能选择 excel 文件');
return false;
}
if (file.size / 1024 / 1024 > 10) {
message.error('大小不能超过 10MB');
return false;
}
loading.value = true;
importSdyDealerOrder(file)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
return false; return false;
} };
if (file.size / 1024 / 1024 > 10) {
message.error('大小不能超过 10MB');
return false;
}
loading.value = true;
importSdyDealerOrder(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 */ /* 更新 visible */
const updateVisible = (value: boolean) => { const updateVisible = (value: boolean) => {
emit('update:visible', value); emit('update:visible', value);
}; };
</script> </script>

View File

@@ -14,14 +14,17 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { GradeParam } from '@/api/user/grade/model'; import type { GradeParam } from '@/api/user/grade/model';
import {ref, watch} from 'vue'; import { ref, watch } from 'vue';
import {utils, writeFile} from 'xlsx'; import { utils, writeFile } from 'xlsx';
import {message} from 'ant-design-vue'; import { message } from 'ant-design-vue';
import {ShopDealerCapital} from "@/api/shop/shopDealerCapital/model"; import { ShopDealerCapital } from '@/api/shop/shopDealerCapital/model';
import {getTenantId} from "@/utils/domain"; import { getTenantId } from '@/utils/domain';
import useSearch from "@/utils/use-search"; import useSearch from '@/utils/use-search';
import {ShopDealerOrder, ShopDealerOrderParam} from "@/api/sdy/sdyDealerOrder/model"; import {
import {pageShopDealerOrder} from "@/api/shop/shopDealerOrder"; ShopDealerOrder,
ShopDealerOrderParam
} from '@/api/sdy/sdyDealerOrder/model';
import { pageShopDealerOrder } from '@/api/shop/shopDealerOrder';
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@@ -43,7 +46,7 @@
}; };
// 表单数据 // 表单数据
const {where} = useSearch<ShopDealerOrderParam>({ const { where } = useSearch<ShopDealerOrderParam>({
keywords: '', keywords: '',
userId: undefined, userId: undefined,
orderNo: undefined, orderNo: undefined,
@@ -114,27 +117,23 @@
workbook.Sheets[sheetName] = sheet; workbook.Sheets[sheetName] = sheet;
// 设置列宽 // 设置列宽
sheet['!cols'] = [ sheet['!cols'] = [
{wch: 10}, { wch: 10 },
{wch: 20}, { wch: 20 },
{wch: 20}, { wch: 20 },
{wch: 15}, { wch: 15 },
{wch: 10}, { wch: 10 },
{wch: 10}, { wch: 10 },
{wch: 20} { wch: 20 }
]; ];
message.loading('正在导出...'); message.loading('正在导出...');
setTimeout(() => { setTimeout(() => {
writeFile( writeFile(workbook, `${sheetName}.xlsx`);
workbook,
`${sheetName}.xlsx`
);
}, 1000); }, 1000);
}) })
.catch((msg) => { .catch((msg) => {
message.error(msg); message.error(msg);
}) })
.finally(() => { .finally(() => {});
});
}; };
watch( watch(

View File

@@ -20,7 +20,7 @@
> >
<!-- 订单基本信息 --> <!-- 订单基本信息 -->
<a-divider orientation="left"> <a-divider orientation="left">
<span style="color: #1890ff; font-weight: 600;">基本信息</span> <span style="color: #1890ff; font-weight: 600">基本信息</span>
</a-divider> </a-divider>
<a-row :gutter="16"> <a-row :gutter="16">
@@ -68,13 +68,25 @@
<div class="font-bold text-gray-400 bg-gray-50">开发调试</div> <div class="font-bold text-gray-400 bg-gray-50">开发调试</div>
<div class="text-gray-400 bg-gray-50"> <div class="text-gray-400 bg-gray-50">
<div>业务员({{ form.userId }}){{ form.nickname }}</div> <div>业务员({{ form.userId }}){{ form.nickname }}</div>
<div>一级分销商({{ form.firstUserId }}){{ form.firstNickname }}一级佣金30%{{ form.firstMoney }}</div> <div
<div>级分销商({{ form.secondUserId }}){{ form.secondNickname }}二级佣金10%{{ form.secondMoney }}</div> >级分销商({{ form.firstUserId }}){{
<div>三级分销商({{ form.thirdUserId }}){{ form.thirdNickname }}三级佣金60%{{ form.thirdMoney }}</div> form.firstNickname
}}一级佣金30%{{ form.firstMoney }}</div
>
<div
>二级分销商({{ form.secondUserId }}){{
form.secondNickname
}}二级佣金10%{{ form.secondMoney }}</div
>
<div
>三级分销商({{ form.thirdUserId }}){{
form.thirdNickname
}}三级佣金60%{{ form.thirdMoney }}</div
>
</div> </div>
<!-- 分销商信息 --> <!-- 分销商信息 -->
<a-divider orientation="left"> <a-divider orientation="left">
<span style="color: #1890ff; font-weight: 600;">收益计算</span> <span style="color: #1890ff; font-weight: 600">收益计算</span>
</a-divider> </a-divider>
<!-- 一级分销商 --> <!-- 一级分销商 -->
@@ -117,9 +129,7 @@
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="12"> <a-col :span="12">
<a-form-item label="占比" name="rate"> <a-form-item label="占比" name="rate"> 10% </a-form-item>
10%
</a-form-item>
<a-form-item label="获取收益" name="firstMoney"> <a-form-item label="获取收益" name="firstMoney">
{{ form.secondMoney }} {{ form.secondMoney }}
</a-form-item> </a-form-item>
@@ -152,201 +162,202 @@
</a-row> </a-row>
</div> </div>
<a-form-item label="结算时间" name="settleTime" v-if="form.isSettled === 1"> <a-form-item
label="结算时间"
name="settleTime"
v-if="form.isSettled === 1"
>
{{ form.settleTime }} {{ form.settleTime }}
</a-form-item> </a-form-item>
</a-form> </a-form>
</ele-modal> </ele-modal>
</template> </template>
<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 {assignObject} from 'ele-admin-pro'; import { assignObject } from 'ele-admin-pro';
import {ShopDealerOrder} from '@/api/shop/shopDealerOrder/model'; import { ShopDealerOrder } from '@/api/shop/shopDealerOrder/model';
import {FormInstance} from 'ant-design-vue/es/form'; import { FormInstance } from 'ant-design-vue/es/form';
import {updateSdyDealerOrder} from "@/api/sdy/sdyDealerOrder"; import { updateSdyDealerOrder } from '@/api/sdy/sdyDealerOrder';
// 是否是修改 // 是否是修改
const isUpdate = ref(false); const isUpdate = ref(false);
const useForm = Form.useForm; const useForm = Form.useForm;
const props = defineProps<{ const props = defineProps<{
// 弹窗是否打开 // 弹窗是否打开
visible: boolean; visible: boolean;
// 修改回显的数据 // 修改回显的数据
data?: ShopDealerOrder | null; data?: ShopDealerOrder | null;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'done'): void; (e: 'done'): void;
(e: 'update:visible', visible: boolean): void; (e: 'update:visible', visible: boolean): void;
}>(); }>();
// 提交状态 // 提交状态
const loading = ref(false); const loading = ref(false);
// 是否显示最大化切换按钮 // 是否显示最大化切换按钮
const maxable = ref(true); const maxable = ref(true);
// 表格选中数据 // 表格选中数据
const formRef = ref<FormInstance | null>(null); const formRef = ref<FormInstance | null>(null);
// 表单数据 // 表单数据
const form = reactive<ShopDealerOrder>({ const form = reactive<ShopDealerOrder>({
id: undefined, id: undefined,
userId: undefined, userId: undefined,
nickname: undefined, nickname: undefined,
orderNo: undefined, orderNo: undefined,
title: undefined, title: undefined,
orderPrice: undefined, orderPrice: undefined,
settledPrice: undefined, settledPrice: undefined,
degreePrice: undefined, degreePrice: undefined,
price: undefined, price: undefined,
month: undefined, month: undefined,
payPrice: undefined, payPrice: undefined,
firstUserId: undefined, firstUserId: undefined,
secondUserId: undefined, secondUserId: undefined,
thirdUserId: undefined, thirdUserId: undefined,
firstMoney: undefined, firstMoney: undefined,
secondMoney: undefined, secondMoney: undefined,
thirdMoney: undefined, thirdMoney: undefined,
firstNickname: undefined, firstNickname: undefined,
secondNickname: undefined, secondNickname: undefined,
thirdNickname: undefined, thirdNickname: undefined,
rate: undefined, rate: undefined,
comments: undefined, comments: undefined,
isInvalid: 0, isInvalid: 0,
isSettled: 0, isSettled: 0,
settleTime: undefined, settleTime: undefined,
tenantId: undefined, tenantId: undefined,
createTime: undefined, createTime: undefined,
updateTime: undefined updateTime: undefined
}); });
/* 更新visible */ /* 更新visible */
const updateVisible = (value: boolean) => { const updateVisible = (value: boolean) => {
emit('update:visible', value); emit('update:visible', value);
}; };
// 表单验证规则 // 表单验证规则
const rules = reactive({ const rules = reactive({
userId: [ userId: [
{ {
required: true, required: true,
message: '请选择用户ID', message: '请选择用户ID',
trigger: 'blur' trigger: 'blur'
}
],
});
const {resetFields} = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
if (form.isSettled == 1) {
message.error('请勿重复结算');
return;
}
if (form.userId == 0) {
message.error('未签约');
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form,
isSettled: 1
};
updateSdyDealerOrder(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {
});
};
console.log(localStorage.getItem(''))
watch(
() => props.visible,
(visible) => {
if (visible) {
if (props.data) {
assignObject(form, props.data);
isUpdate.value = true;
} else {
// 重置为默认值
Object.assign(form, {
id: undefined,
userId: undefined,
orderNo: undefined,
orderPrice: undefined,
firstUserId: undefined,
secondUserId: undefined,
thirdUserId: undefined,
firstMoney: undefined,
secondMoney: undefined,
thirdMoney: undefined,
isInvalid: 0,
isSettled: 0,
settleTime: undefined
});
isUpdate.value = false;
} }
} else { ]
resetFields(); });
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
} }
}, if (form.isSettled == 1) {
{immediate: true} message.error('请勿重复结算');
); return;
}
if (form.userId == 0) {
message.error('未签约');
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form,
isSettled: 1
};
updateSdyDealerOrder(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
console.log(localStorage.getItem(''));
watch(
() => props.visible,
(visible) => {
if (visible) {
if (props.data) {
assignObject(form, props.data);
isUpdate.value = true;
} else {
// 重置为默认值
Object.assign(form, {
id: undefined,
userId: undefined,
orderNo: undefined,
orderPrice: undefined,
firstUserId: undefined,
secondUserId: undefined,
thirdUserId: undefined,
firstMoney: undefined,
secondMoney: undefined,
thirdMoney: undefined,
isInvalid: 0,
isSettled: 0,
settleTime: undefined
});
isUpdate.value = false;
}
} else {
resetFields();
}
},
{ immediate: true }
);
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.dealer-section { .dealer-section {
margin-bottom: 24px; margin-bottom: 24px;
padding: 16px; padding: 16px;
background: #fafafa; background: #fafafa;
border-radius: 6px; border-radius: 6px;
border-left: 3px solid #1890ff; border-left: 3px solid #1890ff;
.dealer-title { .dealer-title {
margin: 0 0 16px 0; margin: 0 0 16px 0;
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
color: #333; color: #333;
.ant-tag { .ant-tag {
margin-right: 8px; margin-right: 8px;
}
} }
} }
}
:deep(.ant-divider-horizontal.ant-divider-with-text-left) { :deep(.ant-divider-horizontal.ant-divider-with-text-left) {
margin: 24px 0 16px 0; margin: 24px 0 16px 0;
.ant-divider-inner-text { .ant-divider-inner-text {
padding: 0 16px 0 0; padding: 0 16px 0 0;
}
} }
}
:deep(.ant-form-item) { :deep(.ant-form-item) {
margin-bottom: 16px; margin-bottom: 16px;
} }
:deep(.ant-input-number) { :deep(.ant-input-number) {
width: 100%; width: 100%;
} }
</style> </style>

View File

@@ -21,7 +21,6 @@
/> />
</template> </template>
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'title'"> <template v-if="column.key === 'title'">
<div>{{ record.title }}</div> <div>{{ record.title }}</div>
<div class="text-gray-400">业务员{{ record.userId }}</div> <div class="text-gray-400">业务员{{ record.userId }}</div>
@@ -51,15 +50,21 @@
<div class="dealer-info"> <div class="dealer-info">
<div v-if="record.firstUserId" class="dealer-level"> <div v-if="record.firstUserId" class="dealer-level">
<a-tag color="red">一级</a-tag> <a-tag color="red">一级</a-tag>
用户{{ record.firstUserId }} - ¥{{ parseFloat(record.firstMoney || '0').toFixed(2) }} 用户{{ record.firstUserId }} - ¥{{
parseFloat(record.firstMoney || '0').toFixed(2)
}}
</div> </div>
<div v-if="record.secondUserId" class="dealer-level"> <div v-if="record.secondUserId" class="dealer-level">
<a-tag color="orange">二级</a-tag> <a-tag color="orange">二级</a-tag>
用户{{ record.secondUserId }} - ¥{{ parseFloat(record.secondMoney || '0').toFixed(2) }} 用户{{ record.secondUserId }} - ¥{{
parseFloat(record.secondMoney || '0').toFixed(2)
}}
</div> </div>
<div v-if="record.thirdUserId" class="dealer-level"> <div v-if="record.thirdUserId" class="dealer-level">
<a-tag color="gold">三级</a-tag> <a-tag color="gold">三级</a-tag>
用户{{ record.thirdUserId }} - ¥{{ parseFloat(record.thirdMoney || '0').toFixed(2) }} 用户{{ record.thirdUserId }} - ¥{{
parseFloat(record.thirdMoney || '0').toFixed(2)
}}
</div> </div>
</div> </div>
</template> </template>
@@ -90,28 +95,26 @@
<a @click="settleOrder(record)" class="ele-text-success"> <a @click="settleOrder(record)" class="ele-text-success">
结算 结算
</a> </a>
<a-divider type="vertical"/> <a-divider type="vertical" />
</template> </template>
<!-- <template v-if="record.isInvalid === 0">--> <!-- <template v-if="record.isInvalid === 0">-->
<!-- <a-popconfirm--> <!-- <a-popconfirm-->
<!-- title="确定要标记此订单为失效吗?"--> <!-- title="确定要标记此订单为失效吗?"-->
<!-- @confirm="invalidateOrder(record)"--> <!-- @confirm="invalidateOrder(record)"-->
<!-- placement="topRight"--> <!-- placement="topRight"-->
<!-- >--> <!-- >-->
<!-- <a class="text-purple-500">--> <!-- <a class="text-purple-500">-->
<!-- 验证--> <!-- 验证-->
<!-- </a>--> <!-- </a>-->
<!-- </a-popconfirm>--> <!-- </a-popconfirm>-->
<!-- </template>--> <!-- </template>-->
<a-popconfirm <a-popconfirm
v-if="record.isSettled === 0" v-if="record.isSettled === 0"
title="确定要删除吗?" title="确定要删除吗?"
@confirm="remove(record)" @confirm="remove(record)"
placement="topRight" placement="topRight"
> >
<a class="text-red-500"> <a class="text-red-500"> 删除 </a>
删除
</a>
</a-popconfirm> </a-popconfirm>
</template> </template>
</template> </template>
@@ -119,363 +122,384 @@
</a-card> </a-card>
<!-- 编辑弹窗 --> <!-- 编辑弹窗 -->
<ShopDealerOrderEdit v-model:visible="showEdit" :data="current" @done="reload"/> <ShopDealerOrderEdit
v-model:visible="showEdit"
:data="current"
@done="reload"
/>
</a-page-header> </a-page-header>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {createVNode, ref} from 'vue'; import { createVNode, ref } from 'vue';
import {message, Modal} from 'ant-design-vue'; import { message, Modal } from 'ant-design-vue';
import { import {
ExclamationCircleOutlined, ExclamationCircleOutlined,
DollarOutlined, DollarOutlined
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import type {EleProTable} from 'ele-admin-pro'; import type { EleProTable } from 'ele-admin-pro';
import type { import type {
DatasourceFunction, DatasourceFunction,
ColumnItem ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types'; } from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue'; import Search from './components/search.vue';
import {getPageTitle} from '@/utils/common'; 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 {
import type {ShopDealerOrder, ShopDealerOrderParam} from '@/api/shop/shopDealerOrder/model'; pageShopDealerOrder,
import {exportSdyDealerOrder, updateSdyDealerOrder} from "@/api/sdy/sdyDealerOrder"; removeShopDealerOrder,
removeBatchShopDealerOrder
} from '@/api/shop/shopDealerOrder';
import type {
ShopDealerOrder,
ShopDealerOrderParam
} from '@/api/shop/shopDealerOrder/model';
import {
exportSdyDealerOrder,
updateSdyDealerOrder
} from '@/api/sdy/sdyDealerOrder';
// 表格实例 // 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null); const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据 // 表格选中数据
const selection = ref<ShopDealerOrder[]>([]); const selection = ref<ShopDealerOrder[]>([]);
// 当前编辑数据 // 当前编辑数据
const current = ref<ShopDealerOrder | null>(null); const current = ref<ShopDealerOrder | null>(null);
// 是否显示编辑弹窗 // 是否显示编辑弹窗
const showEdit = ref(false); const showEdit = ref(false);
// 加载状态 // 加载状态
const loading = ref(true); const loading = ref(true);
// 当前搜索条件 // 当前搜索条件
const currentWhere = ref<ShopDealerOrderParam>({}); const currentWhere = ref<ShopDealerOrderParam>({});
// 表格数据源 // 表格数据源
const datasource: DatasourceFunction = ({ const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
// 保存当前搜索条件用于导出
currentWhere.value = {...where};
// 已结算订单
where.isSettled = 1;
return pageShopDealerOrder({
...where,
...orders,
page, page,
limit limit,
}); where,
}; orders,
filters
// 表格列配置 }) => {
const columns = ref<ColumnItem[]>([ if (filters) {
{ where.status = filters.status;
key: 'index',
width: 48,
align: 'center',
fixed: 'left',
hideInSetting: true,
customRender: ({index}) => index + (tableRef.value?.tableIndex ?? 0)
},
{
title: '客户名称',
dataIndex: 'title',
key: 'title',
width: 150
},
{
title: '订单编号',
dataIndex: 'orderNo',
key: 'orderNo',
align: 'center'
},
{
title: '结算电量',
dataIndex: 'orderPrice',
key: 'orderPrice',
align: 'center'
},
{
title: '换算成度',
dataIndex: 'degreePrice',
key: 'degreePrice',
align: 'center'
},
{
title: '结算单价',
dataIndex: 'price',
key: 'price',
align: 'center'
},
{
title: '结算金额',
dataIndex: 'settledPrice',
key: 'settledPrice',
align: 'center'
},
{
title: '税费',
dataIndex: 'rate',
key: 'rate',
align: 'center'
},
{
title: '实发金额',
dataIndex: 'payPrice',
key: 'payPrice',
align: 'center'
},
{
title: '签约状态',
dataIndex: 'isInvalid',
key: 'isInvalid',
align: 'center',
width: 100
},
{
title: '月份',
dataIndex: 'month',
key: 'month',
align: 'center',
width: 100
},
{
title: '结算状态',
dataIndex: 'isSettled',
key: 'isSettled',
align: 'center',
width: 100
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center'
}
// {
// title: '操作',
// key: 'action',
// width: 180,
// fixed: 'right',
// align: 'center',
// hideInSetting: true
// }
]);
/* 搜索 */
const reload = (where?: ShopDealerOrderParam) => {
selection.value = [];
tableRef?.value?.reload({where: where});
};
/* 结算单个订单 */
const settleOrder = (row: ShopDealerOrder) => {
const totalCommission = (parseFloat(row.firstMoney || '0') +
parseFloat(row.secondMoney || '0') +
parseFloat(row.thirdMoney || '0')).toFixed(2);
Modal.confirm({
title: '确认结算',
content: `确定要结算此订单吗?总佣金金额:¥${totalCommission}`,
icon: createVNode(DollarOutlined),
okText: '确认结算',
okType: 'primary',
cancelText: '取消',
onOk: () => {
const hide = message.loading('正在结算...', 0);
// 这里调用结算API
updateSdyDealerOrder({
...row,
isSettled: 1
})
setTimeout(() => {
hide();
message.success('结算成功');
reload();
}, 1000);
} }
}); // 保存当前搜索条件用于导出
}; currentWhere.value = { ...where };
// 已结算订单
/* 批量结算 */ where.isSettled = 1;
const batchSettle = () => { return pageShopDealerOrder({
if (!selection.value.length) { ...where,
message.error('请至少选择一条数据'); ...orders,
return; page,
} limit
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传入当前搜索条件
exportSdyDealerOrder(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(); // 表格列配置
const columns = ref<ColumnItem[]>([
{
key: 'index',
width: 48,
align: 'center',
fixed: 'left',
hideInSetting: true,
customRender: ({ index }) => index + (tableRef.value?.tableIndex ?? 0)
},
{
title: '客户名称',
dataIndex: 'title',
key: 'title',
width: 150
},
{
title: '订单编号',
dataIndex: 'orderNo',
key: 'orderNo',
align: 'center'
},
{
title: '结算电量',
dataIndex: 'orderPrice',
key: 'orderPrice',
align: 'center'
},
{
title: '换算成度',
dataIndex: 'degreePrice',
key: 'degreePrice',
align: 'center'
},
{
title: '结算单价',
dataIndex: 'price',
key: 'price',
align: 'center'
},
{
title: '结算金额',
dataIndex: 'settledPrice',
key: 'settledPrice',
align: 'center'
},
{
title: '税费',
dataIndex: 'rate',
key: 'rate',
align: 'center'
},
{
title: '实发金额',
dataIndex: 'payPrice',
key: 'payPrice',
align: 'center'
},
{
title: '签约状态',
dataIndex: 'isInvalid',
key: 'isInvalid',
align: 'center',
width: 100
},
{
title: '月份',
dataIndex: 'month',
key: 'month',
align: 'center',
width: 100
},
{
title: '结算状态',
dataIndex: 'isSettled',
key: 'isSettled',
align: 'center',
width: 100
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center'
}
// {
// title: '操作',
// key: 'action',
// width: 180,
// fixed: 'right',
// align: 'center',
// hideInSetting: true
// }
]);
/* 搜索 */
const reload = (where?: ShopDealerOrderParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 结算单个订单 */
const settleOrder = (row: ShopDealerOrder) => {
const totalCommission = (
parseFloat(row.firstMoney || '0') +
parseFloat(row.secondMoney || '0') +
parseFloat(row.thirdMoney || '0')
).toFixed(2);
Modal.confirm({
title: '确认结算',
content: `确定要结算此订单吗?总佣金金额:¥${totalCommission}`,
icon: createVNode(DollarOutlined),
okText: '确认结算',
okType: 'primary',
cancelText: '取消',
onOk: () => {
const hide = message.loading('正在结算...', 0);
// 这里调用结算API
updateSdyDealerOrder({
...row,
isSettled: 1
});
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传入当前搜索条件
exportSdyDealerOrder(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>
<script lang="ts"> <script lang="ts">
export default { export default {
name: 'ShopDealerOrder' name: 'ShopDealerOrder'
}; };
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.order-info { .order-info {
.order-id { .order-id {
font-weight: 500; font-weight: 500;
color: #333; color: #333;
margin-bottom: 4px; margin-bottom: 4px;
} }
.order-price { .order-price {
color: #ff4d4f; color: #ff4d4f;
font-weight: 600; font-weight: 600;
}
}
.dealer-info {
.dealer-level {
margin-bottom: 6px;
font-size: 12px;
&:last-child {
margin-bottom: 0;
} }
} }
}
:deep(.detail-section) { .dealer-info {
h4 { .dealer-level {
color: #1890ff; margin-bottom: 6px;
margin-bottom: 12px; font-size: 12px;
border-bottom: 1px solid #f0f0f0;
padding-bottom: 8px; &:last-child {
margin-bottom: 0;
}
}
} }
p { :deep(.detail-section) {
margin: 4px 0; h4 {
line-height: 1.5; 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) { :deep(.ant-table-tbody > tr > td) {
vertical-align: top; vertical-align: top;
} }
:deep(.ant-tag) { :deep(.ant-tag) {
margin: 2px 4px 2px 0; margin: 2px 4px 2px 0;
} }
</style> </style>

View File

@@ -40,7 +40,7 @@
<a-select v-model:value="form.type" placeholder="请选择类型"> <a-select v-model:value="form.type" placeholder="请选择类型">
<a-select-option :value="0">经销商</a-select-option> <a-select-option :value="0">经销商</a-select-option>
<a-select-option :value="1">门店</a-select-option> <a-select-option :value="1">门店</a-select-option>
<!-- <a-select-option :value="2">集团</a-select-option>--> <!-- <a-select-option :value="2">集团</a-select-option>-->
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-col> </a-col>
@@ -65,20 +65,20 @@
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
<!-- <a-col :span="12">--> <!-- <a-col :span="12">-->
<!-- <a-form-item--> <!-- <a-form-item-->
<!-- label="支付密码"--> <!-- label="支付密码"-->
<!-- name="payPassword"--> <!-- name="payPassword"-->
<!-- :help="isUpdate ? '留空表示不修改' : undefined"--> <!-- :help="isUpdate ? '留空表示不修改' : undefined"-->
<!-- >--> <!-- >-->
<!-- <a-input-password--> <!-- <a-input-password-->
<!-- allow-clear--> <!-- allow-clear-->
<!-- :maxlength="20"--> <!-- :maxlength="20"-->
<!-- placeholder="请输入支付密码"--> <!-- placeholder="请输入支付密码"-->
<!-- v-model:value="form.payPassword"--> <!-- v-model:value="form.payPassword"-->
<!-- />--> <!-- />-->
<!-- </a-form-item>--> <!-- </a-form-item>-->
<!-- </a-col>--> <!-- </a-col>-->
<a-col :span="12"> <a-col :span="12">
<a-form-item label="头像" name="image"> <a-form-item label="头像" name="image">
<a-image :src="form.avatar" :width="50" :preview="false" /> <a-image :src="form.avatar" :width="50" :preview="false" />
@@ -144,44 +144,44 @@
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
<!-- <a-col :span="12">--> <!-- <a-col :span="12">-->
<!-- <a-form-item label="单价" name="price">--> <!-- <a-form-item label="单价" name="price">-->
<!-- <a-input-number--> <!-- <a-input-number-->
<!-- class="ele-fluid"--> <!-- class="ele-fluid"-->
<!-- :min="0"--> <!-- :min="0"-->
<!-- :precision="2"--> <!-- :precision="2"-->
<!-- stringMode--> <!-- stringMode-->
<!-- placeholder="0.00"--> <!-- placeholder="0.00"-->
<!-- v-model:value="form.price"--> <!-- v-model:value="form.price"-->
<!-- />--> <!-- />-->
<!-- </a-form-item>--> <!-- </a-form-item>-->
<!-- </a-col>--> <!-- </a-col>-->
</a-row> </a-row>
<!-- <a-row :gutter="16">--> <!-- <a-row :gutter="16">-->
<!-- <a-col :span="12">--> <!-- <a-col :span="12">-->
<!-- <a-form-item label="排序号" name="sortNumber">--> <!-- <a-form-item label="排序号" name="sortNumber">-->
<!-- <a-input-number--> <!-- <a-input-number-->
<!-- :min="0"--> <!-- :min="0"-->
<!-- :max="9999"--> <!-- :max="9999"-->
<!-- class="ele-fluid"--> <!-- class="ele-fluid"-->
<!-- placeholder="请输入排序号"--> <!-- placeholder="请输入排序号"-->
<!-- v-model:value="form.sortNumber"--> <!-- v-model:value="form.sortNumber"-->
<!-- />--> <!-- />-->
<!-- </a-form-item>--> <!-- </a-form-item>-->
<!-- </a-col>--> <!-- </a-col>-->
<!-- <a-col :span="12">--> <!-- <a-col :span="12">-->
<!-- <a-form-item label="备注" name="comments">--> <!-- <a-form-item label="备注" name="comments">-->
<!-- <a-textarea--> <!-- <a-textarea-->
<!-- :rows="3"--> <!-- :rows="3"-->
<!-- :maxlength="200"--> <!-- :maxlength="200"-->
<!-- show-count--> <!-- show-count-->
<!-- placeholder="请输入备注"--> <!-- placeholder="请输入备注"-->
<!-- v-model:value="form.comments"--> <!-- v-model:value="form.comments"-->
<!-- />--> <!-- />-->
<!-- </a-form-item>--> <!-- </a-form-item>-->
<!-- </a-col>--> <!-- </a-col>-->
<!-- </a-row>--> <!-- </a-row>-->
</a-form> </a-form>
</ele-modal> </ele-modal>
</template> </template>
@@ -190,7 +190,10 @@
import { computed, ref, reactive, watch } from 'vue'; import { computed, ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue'; import { Form, message } from 'ant-design-vue';
import { assignObject, toDateString, uuid } from 'ele-admin-pro'; import { assignObject, toDateString, uuid } from 'ele-admin-pro';
import { addShopDealerUser, updateShopDealerUser } from '@/api/shop/shopDealerUser'; import {
addShopDealerUser,
updateShopDealerUser
} from '@/api/shop/shopDealerUser';
import { ShopDealerUser } from '@/api/shop/shopDealerUser/model'; import { ShopDealerUser } from '@/api/shop/shopDealerUser/model';
import { useThemeStore } from '@/store/modules/theme'; import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
@@ -260,11 +263,15 @@
}; };
const createTimeText = computed(() => { const createTimeText = computed(() => {
return form.createTime ? toDateString(form.createTime, 'yyyy-MM-dd HH:mm:ss') : ''; return form.createTime
? toDateString(form.createTime, 'yyyy-MM-dd HH:mm:ss')
: '';
}); });
const updateTimeText = computed(() => { const updateTimeText = computed(() => {
return form.updateTime ? toDateString(form.updateTime, 'yyyy-MM-dd HH:mm:ss') : ''; return form.updateTime
? toDateString(form.updateTime, 'yyyy-MM-dd HH:mm:ss')
: '';
}); });
const selectedUserText = ref<string>(''); const selectedUserText = ref<string>('');
@@ -369,13 +376,7 @@
.then(() => { .then(() => {
loading.value = true; loading.value = true;
// 不在弹窗里编辑的字段不提交避免误更新如自增ID、删除标识等 // 不在弹窗里编辑的字段不提交避免误更新如自增ID、删除标识等
const { const { isDelete, tenantId, createTime, updateTime, ...rest } = form;
isDelete,
tenantId,
createTime,
updateTime,
...rest
} = form;
const formData: ShopDealerUser = { ...rest }; const formData: ShopDealerUser = { ...rest };
// userId 新增需要,编辑不允许修改 // userId 新增需要,编辑不允许修改
if (isUpdate.value) { if (isUpdate.value) {
@@ -385,7 +386,9 @@
if (isUpdate.value && !formData.payPassword) { if (isUpdate.value && !formData.payPassword) {
delete formData.payPassword; delete formData.payPassword;
} }
const saveOrUpdate = isUpdate.value ? updateShopDealerUser : addShopDealerUser; const saveOrUpdate = isUpdate.value
? updateShopDealerUser
: addShopDealerUser;
saveOrUpdate(formData) saveOrUpdate(formData)
.then((msg) => { .then((msg) => {
loading.value = false; loading.value = false;
@@ -412,12 +415,12 @@
// 不回显密码,避免误操作 // 不回显密码,避免误操作
form.payPassword = ''; form.payPassword = '';
selectedUserText.value = ''; selectedUserText.value = '';
if(props.data.image){ if (props.data.image) {
images.value.push({ images.value.push({
uid: uuid(), uid: uuid(),
url: props.data.image, url: props.data.image,
status: 'done' status: 'done'
}) });
} }
isUpdate.value = true; isUpdate.value = true;
} else { } else {

View File

@@ -1,85 +1,100 @@
<template> <template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)"> <a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<a-card :bordered="false" :body-style="{ padding: '16px' }"> <a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table <ele-pro-table
ref="tableRef" ref="tableRef"
row-key="id" row-key="id"
:columns="columns" :columns="columns"
:datasource="datasource" :datasource="datasource"
:customRow="customRow" :customRow="customRow"
tool-class="ele-toolbar-form" tool-class="ele-toolbar-form"
class="sys-org-table" class="sys-org-table"
> >
<template #toolbar> <template #toolbar>
<search <search
@search="reload" @search="reload"
:selection="selection" :selection="selection"
@add="openEdit" @add="openEdit"
@remove="removeBatch" @remove="removeBatch"
@batchMove="openMove" @batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="50" />
</template>
<template v-if="column.key === 'type'">
<a-tag v-if="record.type === 0">经销商</a-tag>
<a-tag v-if="record.type === 1" color="orange">门店</a-tag>
<!-- <a-tag v-if="record.type === 2" color="purple">集团</a-tag>-->
</template>
<template v-if="column.key === 'qrcode'">
<QrcodeOutlined
:style="{ fontSize: '24px' }"
@click="openQrCode(record)"
/> />
</template> </template>
<template #bodyCell="{ column, record }"> <template v-if="column.key === 'status'">
<template v-if="column.key === 'image'"> <a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-image :src="record.image" :width="50" /> <a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'type'">
<a-tag v-if="record.type === 0">经销商</a-tag>
<a-tag v-if="record.type === 1" color="orange">门店</a-tag>
<!-- <a-tag v-if="record.type === 2" color="purple">集团</a-tag>-->
</template>
<template v-if="column.key === 'qrcode'">
<QrcodeOutlined :style="{fontSize: '24px'}" @click="openQrCode(record)" />
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag>
<a-tag v-if="record.status === 1" color="red">隐藏</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template> </template>
</ele-pro-table> <template v-if="column.key === 'action'">
</a-card> <a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 --> <!-- 编辑弹窗 -->
<ShopDealerUserEdit v-model:visible="showEdit" :data="current" @done="reload" /> <ShopDealerUserEdit
v-model:visible="showEdit"
:data="current"
@done="reload"
/>
<!-- 二维码预览 --> <!-- 二维码预览 -->
<a-modal <a-modal
v-model:visible="showQrModal" v-model:visible="showQrModal"
:title="qrModalTitle" :title="qrModalTitle"
:footer="null" :footer="null"
:width="380" :width="380"
centered centered
destroy-on-close destroy-on-close
> >
<div style="display: flex; justify-content: center"> <div style="display: flex; justify-content: center">
<a-image v-if="qrModalUrl" :src="qrModalUrl" :width="280" :preview="false" /> <a-image
</div> v-if="qrModalUrl"
<div style="display: flex; justify-content: center; margin-top: 12px"> :src="qrModalUrl"
<a-space> :width="280"
<a-button @click="copyQrUrl">复制链接</a-button> :preview="false"
<a-button type="primary" @click="openQrInNewTab">打开原图</a-button> />
</a-space> </div>
</div> <div style="display: flex; justify-content: center; margin-top: 12px">
</a-modal> <a-space>
<a-button @click="copyQrUrl">复制链接</a-button>
<a-button type="primary" @click="openQrInNewTab">打开原图</a-button>
</a-space>
</div>
</a-modal>
</a-page-header> </a-page-header>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { createVNode, ref, computed } from 'vue'; import { createVNode, ref, computed } from 'vue';
import { message, Modal } from 'ant-design-vue'; import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined, QrcodeOutlined } from '@ant-design/icons-vue'; import {
ExclamationCircleOutlined,
QrcodeOutlined
} from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro'; import type { EleProTable } from 'ele-admin-pro';
import { toDateString } from 'ele-admin-pro'; import { toDateString } from 'ele-admin-pro';
import type { import type {
@@ -87,10 +102,17 @@
ColumnItem ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types'; } from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue'; import Search from './components/search.vue';
import {getPageTitle} from '@/utils/common'; import { getPageTitle } from '@/utils/common';
import ShopDealerUserEdit from './components/shopDealerUserEdit.vue'; import ShopDealerUserEdit from './components/shopDealerUserEdit.vue';
import { pageShopDealerUser, removeShopDealerUser, removeBatchShopDealerUser } from '@/api/shop/shopDealerUser'; import {
import type { ShopDealerUser, ShopDealerUserParam } from '@/api/shop/shopDealerUser/model'; pageShopDealerUser,
removeShopDealerUser,
removeBatchShopDealerUser
} from '@/api/shop/shopDealerUser';
import type {
ShopDealerUser,
ShopDealerUserParam
} from '@/api/shop/shopDealerUser/model';
// 表格实例 // 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null); const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
@@ -111,7 +133,9 @@
const loading = ref(true); const loading = ref(true);
const getQrCodeUrl = (userId?: number) => { const getQrCodeUrl = (userId?: number) => {
return `https://mp-api.websoft.top/api/wx-login/getOrderQRCodeUnlimited/uid_${userId ?? ''}`; return `https://mp-api.websoft.top/api/wx-login/getOrderQRCodeUnlimited/uid_${
userId ?? ''
}`;
}; };
const openQrCode = (row: ShopDealerUser) => { const openQrCode = (row: ShopDealerUser) => {
@@ -120,7 +144,9 @@
return; return;
} }
qrModalUrl.value = getQrCodeUrl(row.userId); qrModalUrl.value = getQrCodeUrl(row.userId);
qrModalTitle.value = row.realName ? `${row.realName} 的二维码` : `UID_${row.userId} 二维码`; qrModalTitle.value = row.realName
? `${row.realName} 的二维码`
: `UID_${row.userId} 二维码`;
showQrModal.value = true; showQrModal.value = true;
}; };
@@ -168,7 +194,7 @@
title: '用户ID', title: '用户ID',
dataIndex: 'userId', dataIndex: 'userId',
key: 'userId', key: 'userId',
width: 90, width: 90
}, },
{ {
title: '类型', title: '类型',

View File

@@ -128,8 +128,8 @@
const normalized: any = Array.isArray(data) const normalized: any = Array.isArray(data)
? data.find((d) => d?.settingKey === settingKey) ?? data[0] ? data.find((d) => d?.settingKey === settingKey) ?? data[0]
: (data as any).data && typeof (data as any).data === 'object' : (data as any).data && typeof (data as any).data === 'object'
? (data as any).data ? (data as any).data
: data; : data;
let parsedContent: any | undefined; let parsedContent: any | undefined;
const rawContent = (normalized as any).content; const rawContent = (normalized as any).content;

View File

@@ -172,8 +172,8 @@
const normalized: any = Array.isArray(data) const normalized: any = Array.isArray(data)
? data.find((d) => d?.settingKey === settingKey.value) ?? data[0] ? data.find((d) => d?.settingKey === settingKey.value) ?? data[0]
: (data as any).data && typeof (data as any).data === 'object' : (data as any).data && typeof (data as any).data === 'object'
? (data as any).data ? (data as any).data
: data; : data;
let parsedContent: any | undefined; let parsedContent: any | undefined;
const rawContent = (normalized as any).content; const rawContent = (normalized as any).content;

View File

@@ -210,8 +210,8 @@
const normalized: any = Array.isArray(data) const normalized: any = Array.isArray(data)
? data.find((d) => d?.settingKey === settingKey.value) ?? data[0] ? data.find((d) => d?.settingKey === settingKey.value) ?? data[0]
: (data as any).data && typeof (data as any).data === 'object' : (data as any).data && typeof (data as any).data === 'object'
? (data as any).data ? (data as any).data
: data; : data;
let parsedContent: any | undefined; let parsedContent: any | undefined;
const rawContent = (normalized as any).content; const rawContent = (normalized as any).content;

View File

@@ -5,9 +5,9 @@
:body-style="{ paddingTop: '0px', minHeight: '800px' }" :body-style="{ paddingTop: '0px', minHeight: '800px' }"
> >
<a-tabs v-model:active-key="active"> <a-tabs v-model:active-key="active">
<!-- <a-tab-pane tab="网站设置" key="website">--> <!-- <a-tab-pane tab="网站设置" key="website">-->
<!-- <Website v-model:value="active" :data="data" />--> <!-- <Website v-model:value="active" :data="data" />-->
<!-- </a-tab-pane>--> <!-- </a-tab-pane>-->
<a-tab-pane tab="上传设置" key="upload"> <a-tab-pane tab="上传设置" key="upload">
<Upload v-model:value="active" :data="data" /> <Upload v-model:value="active" :data="data" />
</a-tab-pane> </a-tab-pane>