Compare commits

..

2 Commits

Author SHA1 Message Date
ac9712819a fix(config): 更新AI代理配置为本地Ollama服务
- 将AI代理目标从远程服务器改为本地127.0.1:11434
- 更新Nginx配置中的代理地址和Host头部设置
- 修改AI_API_URL默认值指向本地Ollama服务
- 调整AI视图组件中的基础URL配置逻辑
- 更新环境变量示例文件中的默认API地址
- 修正Vite开发服务器代理配置指向本地服务
2026-02-28 01:42:18 +08:00
91708315f3 style(ai): 格式化 AI API 错误消息和调整 AI 视图布局
- 格式化 Ollama 和 OpenAI API 的错误消息字符串以提高可读性
- 移除 AI 视图中的 BaseURL 输入字段并硬编码为固定端点
- 简化 AI 视图中 API 调用的基础 URL 配置逻辑
- 修复多个组件中的代码格式和空格缩进问题
- 清理经销商订单视图中的多余注释和代码结构
- 调整表单组件的标签和布局格式以提升用户体验
2026-02-28 00:53:26 +08:00
21 changed files with 859 additions and 807 deletions

View File

@@ -9,12 +9,12 @@ VITE_FILE_SERVER=https://your-file-server.com
# AI 网关(OpenAI兼容) # AI 网关(OpenAI兼容)
# - 开发环境推荐走同源反代VITE_AI_API_URL=/ai-proxy配合 vite.config.ts # - 开发环境推荐走同源反代VITE_AI_API_URL=/ai-proxy配合 vite.config.ts
# - 生产环境可直连(需 AI 服务允许 CORS或在 Nginx 里配置 /ai-proxy 反代 # - 生产环境可直连(需 AI 服务允许 CORS或在 Nginx 里配置 /ai-proxy 反代
VITE_AI_API_URL=https://ai-api.websoft.top/api/v1 VITE_AI_API_URL=http://127.0.0.1:11434/api/v1
# Ollama 原生接口(默认端口 11434 # Ollama 原生接口(默认端口 11434
# - 开发环境推荐走同源反代VITE_OLLAMA_API_URL=/proxy配合 vite.config.ts # - 开发环境推荐走同源反代VITE_OLLAMA_API_URL=/proxy配合 vite.config.ts
# - 生产环境不要直接用 http会混合内容被拦截建议 Nginx 反代成同源 https # - 生产环境不要直接用 http会混合内容被拦截建议 Nginx 反代成同源 https
VITE_OLLAMA_API_URL=http://47.119.165.234:11434 VITE_OLLAMA_API_URL=http://127.0.0.1:11434
# 仅用于本地开发反代注入vite.config.ts 会读取并注入到 /ai-proxy 请求头) # 仅用于本地开发反代注入vite.config.ts 会读取并注入到 /ai-proxy 请求头)
# 不要加 VITE_ 前缀,避免被打包到前端产物里 # 不要加 VITE_ 前缀,避免被打包到前端产物里

View File

@@ -6,7 +6,7 @@
项目已在 `vite.config.ts` 配置(默认目标可通过 `AI_PROXY_TARGET` 调整): 项目已在 `vite.config.ts` 配置(默认目标可通过 `AI_PROXY_TARGET` 调整):
- `/ai-proxy/*` -> `https://ai-api.websoft.top/api/v1/*` - `/ai-proxy/*` -> `http://127.0.0.1:11434/api/v1/*`
配合 `.env.development` 配合 `.env.development`
@@ -20,7 +20,7 @@ VITE_AI_API_URL=/ai-proxy
```nginx ```nginx
location /ai-proxy/ { location /ai-proxy/ {
proxy_pass https://ai-api.websoft.top/api/v1/; proxy_pass http://127.0.0.1:11434/api/v1/;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Host ai-api.websoft.top; proxy_set_header Host ai-api.websoft.top;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
@@ -48,9 +48,9 @@ VITE_AI_API_URL=/ai-proxy
```nginx ```nginx
location /proxy/ { location /proxy/ {
proxy_pass http://47.119.165.234:11434/; proxy_pass http://127.0.0.1:11434/;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Host 47.119.165.234; proxy_set_header Host 127.0.0.1;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;

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

@@ -20,7 +20,7 @@ export const FILE_SERVER =
export const AI_API_URL = export const AI_API_URL =
import.meta.env.VITE_AI_API_URL || import.meta.env.VITE_AI_API_URL ||
// Prefer same-origin reverse proxy during local development to avoid CORS. // Prefer same-origin reverse proxy during local development to avoid CORS.
(import.meta.env.DEV ? '/ai-proxy' : 'https://ai-api.websoft.top/api/v1'); (import.meta.env.DEV ? '/ai-proxy' : 'http://127.0.0.1:11434/api/v1');
// Ollama native API endpoint (usually http://host:11434). // Ollama native API endpoint (usually http://host:11434).
// Note: browsers cannot call http from an https site (mixed-content); prefer same-origin proxy. // Note: browsers cannot call http from an https site (mixed-content); prefer same-origin proxy.

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,11 @@
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 = import.meta.env.PROD
? 'http://127.0.0.1:11434'
: '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 +48,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 +99,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 +114,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 +164,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

@@ -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

@@ -27,7 +27,7 @@
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;

View File

@@ -17,11 +17,14 @@
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<{
@@ -124,17 +127,13 @@
]; ];
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,11 +162,14 @@
</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>
@@ -166,7 +179,7 @@ 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);
@@ -236,10 +249,9 @@ const rules = reactive({
message: '请选择用户ID', message: '请选择用户ID',
trigger: 'blur' trigger: 'blur'
} }
], ]
}); });
const { resetFields } = useForm(form, rules); const { resetFields } = useForm(form, rules);
/* 保存编辑 */ /* 保存编辑 */
@@ -275,11 +287,10 @@ const save = () => {
message.error(e.message); message.error(e.message);
}); });
}) })
.catch(() => { .catch(() => {});
});
}; };
console.log(localStorage.getItem('')) console.log(localStorage.getItem(''));
watch( watch(
() => props.visible, () => props.visible,
(visible) => { (visible) => {

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>
@@ -109,9 +114,7 @@
@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,7 +122,11 @@
</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>
@@ -128,7 +135,7 @@ 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 {
@@ -138,9 +145,19 @@ import type {
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);
@@ -283,9 +300,11 @@ const reload = (where?: ShopDealerOrderParam) => {
/* 结算单个订单 */ /* 结算单个订单 */
const settleOrder = (row: ShopDealerOrder) => { const settleOrder = (row: ShopDealerOrder) => {
const totalCommission = (parseFloat(row.firstMoney || '0') + const totalCommission = (
parseFloat(row.firstMoney || '0') +
parseFloat(row.secondMoney || '0') + parseFloat(row.secondMoney || '0') +
parseFloat(row.thirdMoney || '0')).toFixed(2); parseFloat(row.thirdMoney || '0')
).toFixed(2);
Modal.confirm({ Modal.confirm({
title: '确认结算', title: '确认结算',
@@ -300,7 +319,7 @@ const settleOrder = (row: ShopDealerOrder) => {
updateSdyDealerOrder({ updateSdyDealerOrder({
...row, ...row,
isSettled: 1 isSettled: 1
}) });
setTimeout(() => { setTimeout(() => {
hide(); hide();
message.success('结算成功'); message.success('结算成功');
@@ -317,8 +336,8 @@ const batchSettle = () => {
return; return;
} }
const validOrders = selection.value.filter(order => const validOrders = selection.value.filter(
order.isSettled === 0 && order.isInvalid === 0 (order) => order.isSettled === 0 && order.isInvalid === 0
); );
if (!validOrders.length) { if (!validOrders.length) {
@@ -326,11 +345,16 @@ const batchSettle = () => {
return; return;
} }
const totalCommission = validOrders.reduce((sum, order) => { const totalCommission = validOrders
return sum + parseFloat(order.firstMoney || '0') + .reduce((sum, order) => {
return (
sum +
parseFloat(order.firstMoney || '0') +
parseFloat(order.secondMoney || '0') + parseFloat(order.secondMoney || '0') +
parseFloat(order.thirdMoney || '0'); parseFloat(order.thirdMoney || '0')
}, 0).toFixed(2); );
}, 0)
.toFixed(2);
Modal.confirm({ Modal.confirm({
title: '批量结算确认', title: '批量结算确认',

View File

@@ -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;
@@ -417,7 +420,7 @@
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

@@ -29,7 +29,10 @@
<!-- <a-tag v-if="record.type === 2" color="purple">集团</a-tag>--> <!-- <a-tag v-if="record.type === 2" color="purple">集团</a-tag>-->
</template> </template>
<template v-if="column.key === 'qrcode'"> <template v-if="column.key === 'qrcode'">
<QrcodeOutlined :style="{fontSize: '24px'}" @click="openQrCode(record)" /> <QrcodeOutlined
:style="{ fontSize: '24px' }"
@click="openQrCode(record)"
/>
</template> </template>
<template v-if="column.key === 'status'"> <template v-if="column.key === 'status'">
<a-tag v-if="record.status === 0" color="green">显示</a-tag> <a-tag v-if="record.status === 0" color="green">显示</a-tag>
@@ -52,7 +55,11 @@
</a-card> </a-card>
<!-- 编辑弹窗 --> <!-- 编辑弹窗 -->
<ShopDealerUserEdit v-model:visible="showEdit" :data="current" @done="reload" /> <ShopDealerUserEdit
v-model:visible="showEdit"
:data="current"
@done="reload"
/>
<!-- 二维码预览 --> <!-- 二维码预览 -->
<a-modal <a-modal
@@ -64,7 +71,12 @@
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
v-if="qrModalUrl"
:src="qrModalUrl"
:width="280"
:preview="false"
/>
</div> </div>
<div style="display: flex; justify-content: center; margin-top: 12px"> <div style="display: flex; justify-content: center; margin-top: 12px">
<a-space> <a-space>
@@ -79,7 +91,10 @@
<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 {
@@ -89,8 +104,15 @@
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

@@ -101,7 +101,7 @@ export default defineConfig(({ command, mode }) => {
// GET /ai-proxy/models -> https://ai.websoft.top/api/v1/models // GET /ai-proxy/models -> https://ai.websoft.top/api/v1/models
// POST /ai-proxy/chat/completions -> https://ai.websoft.top/api/v1/chat/completions // POST /ai-proxy/chat/completions -> https://ai.websoft.top/api/v1/chat/completions
'/ai-proxy': { '/ai-proxy': {
target: env.AI_PROXY_TARGET || 'https://ai-api.websoft.top', target: env.AI_PROXY_TARGET || 'http://127.0.0.1:11434',
changeOrigin: true, changeOrigin: true,
secure: false, secure: false,
rewrite: (path) => rewrite: (path) =>
@@ -124,10 +124,10 @@ export default defineConfig(({ command, mode }) => {
} }
}, },
// Ollama native API reverse proxy (dev only). // Ollama native API reverse proxy (dev only).
// GET /proxy/api/tags -> http://47.119.165.234:11434/api/tags // GET /proxy/api/tags -> http://127.0.0.1:11434/api/tags
// POST /proxy/api/chat -> http://47.119.165.234:11434/api/chat // POST /proxy/api/chat -> http://127.0.0.1:11434/api/chat
'/proxy': { '/proxy': {
target: 'http://47.119.165.234:11434', target: 'http://127.0.0.1:11434',
changeOrigin: true, changeOrigin: true,
secure: false, secure: false,
rewrite: (path) => path.replace(/^\/proxy/, '') rewrite: (path) => path.replace(/^\/proxy/, '')