From 0ec961e210c02843f8184992d483fc8de2b7c2b1 Mon Sep 17 00:00:00 2001 From: 2910410219 <2910410219@qq.com> Date: Thu, 27 Aug 2026 11:54:50 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9A=E8=B4=B4?= =?UTF-8?q?=E7=89=87=E6=A8=A1=E6=9D=BF=E7=BB=9F=E4=B8=80=E8=B5=B0CDN?= =?UTF-8?q?=E5=8F=82=E8=80=83=EF=BC=88=E9=BB=98=E8=AE=A4=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E4=B8=8E=E4=B8=8B=E8=BD=BD=E3=80=81=E7=A7=BB=E9=99=A4=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E6=A8=A1=E6=9D=BF=E6=96=87=E4=BB=B6=EF=BC=89=E3=80=81?= =?UTF-8?q?=E9=A6=96=E9=A1=B5=E6=96=B0=E5=BB=BA=E4=BC=9A=E8=AF=9D=E9=A6=96?= =?UTF-8?q?=E6=AC=A1=E6=89=A7=E8=A1=8C=E5=90=8E=E8=B4=B4=E7=89=87=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E5=9B=9E=E6=98=BE=E7=A9=BA=E7=99=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- public/template_example.html | 213 ------------------ .../patchTemplate/PatchTemplateEditor.vue | 28 ++- src/views/home/index.vue | 15 ++ 3 files changed, 34 insertions(+), 222 deletions(-) delete mode 100644 public/template_example.html diff --git a/public/template_example.html b/public/template_example.html deleted file mode 100644 index fcbeed4..0000000 --- a/public/template_example.html +++ /dev/null @@ -1,213 +0,0 @@ - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - -
标题文字
- - - - - - - - - - -
产品卖点描述
- - - - -
- - - 免责声明文字 - -
- -
- - - - - - - - - diff --git a/src/components/patchTemplate/PatchTemplateEditor.vue b/src/components/patchTemplate/PatchTemplateEditor.vue index 5cb1723..60f8131 100644 --- a/src/components/patchTemplate/PatchTemplateEditor.vue +++ b/src/components/patchTemplate/PatchTemplateEditor.vue @@ -166,7 +166,7 @@ function nextImageKey(template: PatchTemplate): string | null { return null; } -const DEFAULT_TEMPLATE_URL = 'https://cdn.redpowerfuture.com/tenantid-1/template_example1.html'; +const DEFAULT_TEMPLATE_URL = 'https://cdn.redpowerfuture.com/tenantid-1/vertical_template_example.html'; const props = defineProps<{ modelValue: PatchTemplate[]; @@ -290,14 +290,24 @@ const handleImageUpload = async (templateIndex: number, imgIdx: number, file: an } }; -// 下载参考模板文件 -const handleDownload = (_e?: Event) => { - const a = document.createElement('a'); - a.href = '/template_example.html'; - a.download = 'template_example.html'; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); +// 下载参考模板文件:统一走 CDN,转 blob 触发下载(避免跨域 直接打开页面) +const handleDownload = async (_e?: Event) => { + try { + const res = await fetch(DEFAULT_TEMPLATE_URL); + if (!res.ok) throw new Error('参考模板获取失败'); + const blob = await res.blob(); + const fileName = decodeURIComponent(DEFAULT_TEMPLATE_URL.split('/').pop() || 'vertical_template_example.html'); + const blobUrl = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = blobUrl; + a.download = fileName; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(blobUrl); + } catch (error: any) { + ElMessage.error(error?.message || '参考模板下载失败'); + } }; diff --git a/src/views/home/index.vue b/src/views/home/index.vue index 702476e..741298f 100644 --- a/src/views/home/index.vue +++ b/src/views/home/index.vue @@ -1234,6 +1234,17 @@ const snapshotFormValuesToMsg = (msg: ChatMessage, formValues: Record { + const nodes = msg.form?.nodeInputParams; + if (!Array.isArray(nodes)) return; + nodes.forEach((n: any) => { + if (n.patchLayout === true) n.templates = templates; + }); +}; + const startWorkflowFromCard = async ( msg: ChatMessage, payload: { formValues: Record; formFileNames: Record; templates?: any[] } @@ -1254,6 +1265,10 @@ const startWorkflowFromCard = async ( // 会话认领(virtual→UUID 触发 MainContent 重建)/ 历史回显重挂载时能从消息对象恢复已填值, // 避免执行后表单被清空 snapshotFormValuesToMsg(msg, payload.formValues); + // 贴片模板同样写回消息对象 patchLayout 节点(保持与后端执行副本一致),重建/重挂载后能恢复回显 + if (Array.isArray(payload.templates)) { + snapshotTemplatesToMsg(msg, payload.templates); + } // 标记本轮发送中:工作流执行期间 InputBar 显示停止按钮(isGenerating 依赖 sendingSessions) sendingSessions[sid] = true; From c2244f50b1957a59580b68cc7559622fcf5bf061 Mon Sep 17 00:00:00 2001 From: 2910410219 <2910410219@qq.com> Date: Fri, 28 Aug 2026 15:28:03 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=AE=A1=E7=90=86=E5=A2=9E=E5=8A=A0=E5=85=85=E5=80=BC?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E4=B8=8E=E9=92=B1=E5=8C=85=E6=B5=81=E6=B0=B4?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 操作列新增"充值""流水信息"按钮,权限与修改一致(isOperation 控制) - 新增充值弹窗 rechargeUser.vue:展示账户名称(只读),金额单位元(校验大于0、最多两位小数),提交 userId/userName/amount/orderNo/description - 新增流水弹窗 walletLog.vue:展示充值与扣费明细(income/expense),金额元,后端分页(pageNum/pageSize) - 新增 recharge、getWalletLogs 接口,路径 shop-user-trade/account/controller/* - other.scss 补充 op-btn-recharge 固定绿色样式(含 dark 主题) Co-Authored-By: Claude --- src/api/system/user/index.ts | 16 +++ src/theme/other.scss | 8 ++ .../system/user/component/rechargeUser.vue | 111 ++++++++++++++++++ src/views/system/user/component/walletLog.vue | 68 +++++++++++ src/views/system/user/index.vue | 41 ++++++- 5 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 src/views/system/user/component/rechargeUser.vue create mode 100644 src/views/system/user/component/walletLog.vue diff --git a/src/api/system/user/index.ts b/src/api/system/user/index.ts index f4115f7..9fc36dd 100644 --- a/src/api/system/user/index.ts +++ b/src/api/system/user/index.ts @@ -70,6 +70,22 @@ export function deleteUser(ids: number[]) { }); } +export function recharge(data: object) { + return request({ + url: '/shop-user-trade/account/controller/recharge', + method: 'post', + data: data, + }); +} + +export function getWalletLogs(userId: number, pageNum: number, pageSize: number) { + return request({ + url: '/shop-user-trade/account/controller/logs', + method: 'get', + params: { userId, pageNum, pageSize }, + }); +} + export function checkIsSuperAdmin() { return request({ url: '/admin-go/api/v1/system/user/checkIsSuperAdmin', diff --git a/src/theme/other.scss b/src/theme/other.scss index 9995a55..abcd4e7 100644 --- a/src/theme/other.scss +++ b/src/theme/other.scss @@ -31,6 +31,10 @@ color: #f56c6c !important; } +.op-btn-recharge { + color: #67c23a !important; // 充值 绿色 +} + [data-theme='dark'] { // textarea - css vars --w-e-textarea-bg-color: var(--el-color-white) !important; @@ -49,6 +53,10 @@ color: #f56c6c !important; // 删除 红色 } + .op-btn-recharge { + color: #67c23a !important; // 充值 绿色 + } + // toolbar - css vars --w-e-toolbar-color: var(--el-text-color-primary) !important; --w-e-toolbar-bg-color: var(--el-color-white) !important; diff --git a/src/views/system/user/component/rechargeUser.vue b/src/views/system/user/component/rechargeUser.vue new file mode 100644 index 0000000..5ed0817 --- /dev/null +++ b/src/views/system/user/component/rechargeUser.vue @@ -0,0 +1,111 @@ + + + diff --git a/src/views/system/user/component/walletLog.vue b/src/views/system/user/component/walletLog.vue new file mode 100644 index 0000000..509440e --- /dev/null +++ b/src/views/system/user/component/walletLog.vue @@ -0,0 +1,68 @@ + + + diff --git a/src/views/system/user/index.vue b/src/views/system/user/index.vue index 7d0fa94..e70c4d8 100644 --- a/src/views/system/user/index.vue +++ b/src/views/system/user/index.vue @@ -117,7 +117,7 @@ - + @@ -154,6 +173,8 @@ + + @@ -162,6 +183,8 @@ import { toRefs, reactive, onMounted, ref, defineComponent, watch, getCurrentIns import { ElMessageBox, ElMessage, ElTree, FormInstance } from 'element-plus'; import { Search } from '@element-plus/icons-vue'; import EditUser from '/@/views/system/user/component/editUser.vue'; +import RechargeUser from '/@/views/system/user/component/rechargeUser.vue'; +import WalletLog from '/@/views/system/user/component/walletLog.vue'; import { getUserList, getDeptTree, resetUserPwd, changeUserStatus, deleteUser } from '/@/api/system/user/index'; interface TableDataState { @@ -186,11 +209,13 @@ interface TableDataState { export default defineComponent({ name: 'systemUser', - components: { EditUser }, + components: { EditUser, RechargeUser, WalletLog }, setup() { const { proxy } = getCurrentInstance(); const { sys_user_sex } = proxy.useDict('sys_user_sex'); const editUserRef = ref(); + const rechargeUserRef = ref(); + const walletLogRef = ref(); const queryRef = ref(); const filterText = ref(''); const treeRef = ref>(); @@ -259,6 +284,14 @@ export default defineComponent({ const onOpenEditUser = (row: any) => { editUserRef.value.openDialog(row); }; + // 打开充值弹窗 + const onOpenRecharge = (row: any) => { + rechargeUserRef.value.openDialog(row); + }; + // 打开流水信息弹窗 + const onOpenWalletLog = (row: any) => { + walletLogRef.value.openDialog(row.id); + }; // 删除用户 const onRowDel = (row: any) => { let msg = '你确定要删除所选用户?'; @@ -358,8 +391,12 @@ export default defineComponent({ return { queryRef, editUserRef, + rechargeUserRef, + walletLogRef, onOpenAddUser, onOpenEditUser, + onOpenRecharge, + onOpenWalletLog, onRowDel, onHandleSizeChange, onHandleCurrentChange, From bdbf989b5e7488086f9c7e421e3be03354350a7a Mon Sep 17 00:00:00 2001 From: 2910410219 <2910410219@qq.com> Date: Tue, 1 Sep 2026 11:47:37 +0800 Subject: [PATCH 3/8] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E4=BB=B7?= =?UTF-8?q?=E6=A0=BC=E7=AE=A1=E7=90=86=EF=BC=88=E8=AE=A1=E4=BB=B7=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E7=AE=A1=E7=90=86=EF=BC=89=E9=A1=B5=E9=9D=A2=E4=B8=8E?= =?UTF-8?q?=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 左侧计价对象枚举列表(工作流 / 模型 / 业务模块,模型支持类型筛选与配置状态圆点) - 右侧按对象类型切换工作流 / 模型 / 业务编辑器,费率规则编辑与后端配置回显 - 新增 src/api/trade/pricing 接口(subjects / config/get / config/save) - rules 兼容对象与历史字符串两种形态;配置 id 用字符串承载避免雪花 ID 精度丢失 Co-Authored-By: Claude Code --- src/api/trade/pricing/index.ts | 35 ++ .../system/price/component/businessEditor.vue | 192 +++++++ .../system/price/component/modelEditor.vue | 479 ++++++++++++++++++ .../system/price/component/subjectList.vue | 281 ++++++++++ .../system/price/component/workflowEditor.vue | 334 ++++++++++++ src/views/system/price/index.vue | 449 ++++++++++++++++ src/views/system/price/mock.ts | 321 ++++++++++++ 7 files changed, 2091 insertions(+) create mode 100644 src/api/trade/pricing/index.ts create mode 100644 src/views/system/price/component/businessEditor.vue create mode 100644 src/views/system/price/component/modelEditor.vue create mode 100644 src/views/system/price/component/subjectList.vue create mode 100644 src/views/system/price/component/workflowEditor.vue create mode 100644 src/views/system/price/index.vue create mode 100644 src/views/system/price/mock.ts diff --git a/src/api/trade/pricing/index.ts b/src/api/trade/pricing/index.ts new file mode 100644 index 0000000..d0bd679 --- /dev/null +++ b/src/api/trade/pricing/index.ts @@ -0,0 +1,35 @@ +import request from '/@/utils/request'; + +/** 计价对象枚举 */ +export function getSubjects() { + return request({ + url: '/shop-user-trade/pricing/controller/subjects', + method: 'get', + }); +} + +/** 计价配置详情 */ +export function getPricingConfig(params: { subjectType: string; subjectId: string }) { + return request({ + url: '/shop-user-trade/pricing/controller/config/get', + method: 'get', + params, + }); +} + +/** 新增/更新计价配置(id > 0 更新,0 新增;rules 传费率对象;id 兼容雪花字符串避免精度丢失) */ +export function savePricingConfig(data: { + id: string | number; + subjectType: string; + subjectId: string; + rules: object; + minBalance?: number; + currency?: string; + enabled?: number; +}) { + return request({ + url: '/shop-user-trade/pricing/controller/config/save', + method: 'post', + data, + }); +} diff --git a/src/views/system/price/component/businessEditor.vue b/src/views/system/price/component/businessEditor.vue new file mode 100644 index 0000000..5305f28 --- /dev/null +++ b/src/views/system/price/component/businessEditor.vue @@ -0,0 +1,192 @@ + + + + + diff --git a/src/views/system/price/component/modelEditor.vue b/src/views/system/price/component/modelEditor.vue new file mode 100644 index 0000000..2e91266 --- /dev/null +++ b/src/views/system/price/component/modelEditor.vue @@ -0,0 +1,479 @@ + + + + + diff --git a/src/views/system/price/component/subjectList.vue b/src/views/system/price/component/subjectList.vue new file mode 100644 index 0000000..55c50db --- /dev/null +++ b/src/views/system/price/component/subjectList.vue @@ -0,0 +1,281 @@ + + + + + diff --git a/src/views/system/price/component/workflowEditor.vue b/src/views/system/price/component/workflowEditor.vue new file mode 100644 index 0000000..affe3da --- /dev/null +++ b/src/views/system/price/component/workflowEditor.vue @@ -0,0 +1,334 @@ + + + + + diff --git a/src/views/system/price/index.vue b/src/views/system/price/index.vue new file mode 100644 index 0000000..4f5fd4c --- /dev/null +++ b/src/views/system/price/index.vue @@ -0,0 +1,449 @@ + + + + + diff --git a/src/views/system/price/mock.ts b/src/views/system/price/mock.ts new file mode 100644 index 0000000..b675e28 --- /dev/null +++ b/src/views/system/price/mock.ts @@ -0,0 +1,321 @@ +/** + * 价格管理(计价配置管理)—— 类型定义、接口映射与本地示例数据 + * + * 说明: + * - 计价对象枚举来自后端 GET .../subjects(见 src/api/trade/pricing/index.ts), + * 本文件提供后端项 → 页面 Subject 的映射(中文 modelType/modelTypeCode → 英文枚举等) + * - 计价配置读取后端的 rules JSON 字符串(JSON.parse),保存时 JSON.stringify 提交 + * - 模型类型编码:reason=100推理 / image=200图片 / audio=300音频 / video=600视频 + * - EXAMPLES 仅为编辑器本地"示例填充" UX,不参与后端数据 + */ + +/* ================= 枚举类型 ================= */ + +export type SubjectType = 'workflow' | 'model' | 'business'; + +export type ModelType = 'reason' | 'video' | 'audio' | 'image'; + +export type Unit = + | 'per_1M' + | 'per_1K' + | 'per_1' + | 'per_second' + | 'per_minute' + | 'per_hour' + | 'per_char'; + +export type Currency = 'CNY' | 'USD'; + +/* ================= 后端枚举接口 ================= */ + +/** 后端 GET .../subjects 返回的计价对象项 */ +export interface ApiSubjectItem { + subjectType: SubjectType; + subjectId: string; + name: string; + /** 仅 model:中文类型(推理/视频/音频/图片) */ + modelType?: string; + /** 仅 model:模型类型编码(100推理/200图片/300音频/600视频) */ + modelTypeCode?: number; +} + +/** modelTypeCode → 英文模型类型 */ +export const MODEL_TYPE_CODE_TO_EN: Record = { + 100: 'reason', + 200: 'image', + 300: 'audio', + 600: 'video', +}; + +/** 各 subjectType 固定的计费模式(后端不下发 modes,按类型硬编码展示) */ +export const SUBJECT_MODE_MAP: Partial> = { + workflow: ['per_item', 'per_second', 'per_token'], + business: ['per_period'], +}; + +/** 后端枚举项 → 页面 Subject */ +export function toSubject(item: ApiSubjectItem): Subject { + if (item.subjectType === 'model') { + const modelType = MODEL_TYPE_CODE_TO_EN[item.modelTypeCode ?? -1] ?? 'reason'; + return { + type: 'model', + id: item.subjectId, + name: item.name, + modelType, + modelTypeCode: item.modelTypeCode, + typeLabel: item.modelType || modelType, + hasConfig: false, + }; + } + return { + type: item.subjectType, + id: item.subjectId, + name: item.name, + modes: SUBJECT_MODE_MAP[item.subjectType] ?? [], + hasConfig: false, + }; +} + +/* ================= 计价对象枚举(页面态) ================= */ + +export interface Subject { + type: SubjectType; + /** subjectId */ + id: string; + /** 显示名称 */ + name: string; + /** 仅 model:模型类型(英文枚举,由 modelTypeCode 映射) */ + modelType?: ModelType; + /** 仅 model:模型类型编码 */ + modelTypeCode?: number; + /** 仅 model:模型中文类型(推理/视频/音频/图片) */ + typeLabel?: string; + /** workflow/business:支持的计费模式(本地硬编码) */ + modes?: string[]; + /** 是否已配置计价(选中后由 config/get 判断) */ + hasConfig: boolean; +} + +/* ================= 工作流费率 JSON ================= */ + +export interface WorkflowTier { + /** 档位最大秒数 */ + maxSec: number; + /** 档位价格(元) */ + price: number; +} + +export interface WorkflowRules { + /** 按条计费 */ + per_item?: { + tiers: WorkflowTier[]; + /** 超出最大档单价(元/秒) */ + overflowUnitPrice: number; + }; + /** 按秒计费 */ + per_second?: { + unitPrice: number; + }; + /** 按 token 计费(价格取自各模型,仅占位空对象) */ + per_token?: Record; +} + +/* ================= 模型费率 JSON ================= */ + +/** 编辑器内部规则行态(thinking/outputAudio 用字符串承载"未设置") */ +export interface ModelRule { + name: string; + mediaType: '' | 'text' | 'audio' | 'video' | 'image'; + thinking: '' | 'true' | 'false'; + outputAudio: '' | 'true' | 'false'; + outputResolution: string; + inLenMin?: number; + inLenMax?: number; + /** token 单位:输入单价 */ + input: number; + /** token 单位:输出单价 */ + output: number; + /** token 单位:缓存命中输入单价 */ + cacheHit: number; + /** 非 token 单位:每单位单价 */ + unitPrice: number; +} + +/** 示例数据源规则(thinking/outputAudio 为布尔,由 loadExample 转换) */ +export interface ExampleRule { + name: string; + mediaType?: string; + thinking?: boolean; + outputAudio?: boolean; + outputResolution?: string; + inLenMin?: number; + inLenMax?: number; + input?: number; + output?: number; + cacheHit?: number; + unitPrice?: number; +} + +/** 构建态:规则命中条件 */ +export interface BuiltModelRuleMatch { + mediaType?: string; + thinking?: boolean; + outputAudio?: boolean; + outputResolution?: string; + inputLengthMin?: number; + inputLengthMax?: number; +} + +/** 构建态:单条规则 */ +export interface BuiltModelRule { + name: string; + match?: BuiltModelRuleMatch; + price: Record; +} + +/** 模型费率 JSON 结构(对齐后端,含内嵌币种) */ +export interface ModelRules { + unit: Unit; + tiered: boolean; + rules: BuiltModelRule[]; + currency: Currency; +} + +/** 模型示例 */ +export interface ModelExample { + label: string; + unit: Unit; + tiered: boolean; + currency?: Currency; + minBalance?: number; + rules: ExampleRule[]; +} + +export type ModelExampleMap = Record; + +/* ================= 业务模块费率 JSON ================= */ + +export interface BusinessRules { + period: 'year'; + price: number; +} + +/* ================= 编辑器初始化配置 ================= */ + +/** 后端 config/get 解析后的编辑器初始化数据(公共字段 + 已 parse 的费率 JSON) */ +export interface EditorInitConfig { + /** 配置记录 id(>0 更新,0 新增;雪花 id 超安全整数,用字符串承载避免精度丢失) */ + id: string | number; + minBalance: number; + currency: Currency; + enabled: boolean; + /** 已 parse 的费率 JSON(编辑器按 subjectType 断言具体结构) */ + rules: WorkflowRules | ModelRules | BusinessRules; +} + +/* ================= 常量 ================= */ + +export const WORKFLOW_DEFAULT_TIERS: WorkflowTier[] = [ + { maxSec: 15, price: 29 }, + { maxSec: 30, price: 49 }, + { maxSec: 60, price: 89 }, +]; + +export const MODEL_TYPE_CODES: Record = { + reason: 100, + image: 200, + audio: 300, + video: 600, +}; + +export const UNIT_OPTIONS: { value: Unit; label: string }[] = [ + { value: 'per_1M', label: 'per_1M(每百万token)' }, + { value: 'per_1K', label: 'per_1K(每千token)' }, + { value: 'per_1', label: 'per_1(每个/每张图)' }, + { value: 'per_second', label: 'per_second(每秒)' }, + { value: 'per_minute', label: 'per_minute(每分钟)' }, + { value: 'per_hour', label: 'per_hour(每小时)' }, + { value: 'per_char', label: 'per_char(每字)' }, +]; + +export const MEDIA_TYPE_OPTIONS: { value: ModelRule['mediaType']; label: string }[] = [ + { value: '', label: '不限制' }, + { value: 'text', label: '文本 text' }, + { value: 'audio', label: '音频 audio' }, + { value: 'video', label: '视频 video' }, + { value: 'image', label: '图片 image' }, +]; + +export const BOOL_TRIPLE_OPTIONS: { value: '' | 'true' | 'false'; label: string }[] = [ + { value: '', label: '不限制' }, + { value: 'true', label: '是' }, + { value: 'false', label: '否' }, +]; + +/** 模型示例(本地填充 UX,逐字对应原型定价配置) */ +export const EXAMPLES: ModelExampleMap = { + video: [ + { + label: '视频·按秒(分辨率×有声/无声×输入媒体)', + unit: 'per_second', + tiered: false, + rules: [ + { name: '无声-720p-输入不含视频', mediaType: 'text', outputAudio: false, outputResolution: '720p', unitPrice: 0.8 }, + { name: '有声-1080p-输入含视频', mediaType: 'video', outputAudio: true, outputResolution: '1080p', unitPrice: 1.5 }, + ], + }, + { + label: '视频·按token(百万)', + unit: 'per_1M', + tiered: false, + rules: [ + { name: '文本输入-无声-1080p', mediaType: 'text', outputAudio: false, outputResolution: '1080p', input: 0.6, output: 3.6, cacheHit: 0.12 }, + { name: '视频输入-有声-1080p', mediaType: 'video', outputAudio: true, outputResolution: '1080p', input: 9, output: 27, cacheHit: 1.8 }, + ], + }, + ], + reason: [ + { + label: '推理·阶梯(思考×输入token档×输入媒体)', + unit: 'per_1M', + tiered: true, + rules: [ + { name: '思考-输入≤32000-文本', mediaType: 'text', thinking: true, inLenMax: 32000, input: 0.6, output: 3.6, cacheHit: 0.12 }, + { name: '思考-输入32001~128000-文本', mediaType: 'text', thinking: true, inLenMin: 32001, inLenMax: 128000, input: 0.9, output: 5.4, cacheHit: 0.18 }, + { name: '思考-输入含音频', mediaType: 'audio', thinking: true, input: 9, output: 3.6, cacheHit: 1.8 }, + { name: '非思考-统一价', thinking: false, input: 0.3, output: 1.2, cacheHit: 0.06 }, + ], + }, + ], + audio: [ + { + label: '音频·按字数', + unit: 'per_char', + tiered: false, + rules: [{ name: '语音-统一价(万字符5元)', unitPrice: 0.0005 }], + }, + { + label: '音频·按分钟', + unit: 'per_minute', + tiered: false, + rules: [{ name: '语音-统一价', unitPrice: 2 }], + }, + { + label: '音频·按token(百万)', + unit: 'per_1M', + tiered: false, + rules: [{ name: '统一价', input: 500, output: 500, cacheHit: 0 }], + }, + ], + image: [ + { + label: '图片·按张数×分辨率', + unit: 'per_1', + tiered: false, + rules: [ + { name: '512x512', outputResolution: '512x512', unitPrice: 0.2 }, + { name: '1024x1024', outputResolution: '1024x1024', unitPrice: 0.5 }, + ], + }, + ], +}; From fbe2bcfee6fa713cbbbf6cfb2a2300f10dd7f63d Mon Sep 17 00:00:00 2001 From: 2910410219 <2910410219@qq.com> Date: Tue, 1 Sep 2026 15:09:15 +0800 Subject: [PATCH 4/8] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E8=AE=A1=E4=BB=B7=E7=BC=96=E8=BE=91=E5=99=A8=E9=80=82?= =?UTF-8?q?=E9=85=8D=E6=96=B0=E8=AE=A1=E8=B4=B9=E7=BB=93=E6=9E=84=EF=BC=88?= =?UTF-8?q?mediaPrices=E5=AA=92=E4=BD=93=E4=BB=B7=E6=A0=BC=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E3=80=81thinking/outputAudio/outputResolution?= =?UTF-8?q?=E4=B8=AD=E6=96=87=E5=B1=95=E7=A4=BA=E4=B8=8E=E4=B8=8B=E6=8B=89?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code --- .../system/price/component/modelEditor.vue | 145 +++++++++++++++--- src/views/system/price/mock.ts | 97 ++++++++---- 2 files changed, 193 insertions(+), 49 deletions(-) diff --git a/src/views/system/price/component/modelEditor.vue b/src/views/system/price/component/modelEditor.vue index 2e91266..267a931 100644 --- a/src/views/system/price/component/modelEditor.vue +++ b/src/views/system/price/component/modelEditor.vue @@ -43,37 +43,36 @@
命中条件 match
- - - - -
@@ -100,6 +99,22 @@
+ + +
+
媒体价格模板 mediaPrices(可选)
+
+ + + + + + + 删除 +
+ + 添加媒体价格 +
基础价(input/output/cacheHit)为默认价;媒体模板仅覆盖指定媒体类型的价,未选媒体类型的行保存时丢弃。
+
+ 添加规则 @@ -147,14 +162,17 @@ diff --git a/src/views/system/price/index.vue b/src/views/system/price/index.vue index 4f5fd4c..e111021 100644 --- a/src/views/system/price/index.vue +++ b/src/views/system/price/index.vue @@ -141,9 +141,9 @@ const current = computed(() => const headerSub = computed(() => { const s = current.value; if (!s) return '加载计价对象…'; - if (s.type === 'workflow') return `subjectType=workflow · subjectId=${s.id}`; - if (s.type === 'model') return `subjectType=model · subjectId=${s.id}(${s.typeLabel},modelType=${s.modelType})`; - return `subjectType=business · subjectId=${s.id}`; + if (s.type === 'workflow') return `工作流对象 · 标识:${s.id}`; + if (s.type === 'model') return `模型对象(${s.typeLabel})· 标识:${s.id}`; + return `业务模块对象 · 标识:${s.id}`; }); /** 详情请求序号,防止快速切换时旧响应覆盖当前对象 */ diff --git a/src/views/system/price/mock.ts b/src/views/system/price/mock.ts index 988796e..b5c6761 100644 --- a/src/views/system/price/mock.ts +++ b/src/views/system/price/mock.ts @@ -240,21 +240,21 @@ export const MODEL_TYPE_CODES: Record = { }; export const UNIT_OPTIONS: { value: Unit; label: string }[] = [ - { value: 'per_1M', label: 'per_1M(每百万token)' }, - { value: 'per_1K', label: 'per_1K(每千token)' }, - { value: 'per_1', label: 'per_1(每个/每张图)' }, - { value: 'per_second', label: 'per_second(每秒)' }, - { value: 'per_minute', label: 'per_minute(每分钟)' }, - { value: 'per_hour', label: 'per_hour(每小时)' }, - { value: 'per_char', label: 'per_char(每字)' }, + { value: 'per_1M', label: '每百万 Token' }, + { value: 'per_1K', label: '每千 Token' }, + { value: 'per_1', label: '每个 / 每张图' }, + { value: 'per_second', label: '每秒' }, + { value: 'per_minute', label: '每分钟' }, + { value: 'per_hour', label: '每小时' }, + { value: 'per_char', label: '每字' }, ]; /** 媒体价格模板可选媒体类型(媒体命中条件已移除,媒体改按模板定价) */ export const MEDIA_PRICE_OPTIONS: { value: 'text' | 'audio' | 'video' | 'image'; label: string }[] = [ - { value: 'text', label: '文本 text' }, - { value: 'audio', label: '音频 audio' }, - { value: 'video', label: '视频 video' }, - { value: 'image', label: '图片 image' }, + { value: 'text', label: '文本' }, + { value: 'audio', label: '音频' }, + { value: 'video', label: '视频' }, + { value: 'image', label: '图片' }, ]; /** outputAudio 命中条件(video):给用户展示中文(有声/无声) */ From a5262941ea9fe369531cf7ac6c7b7d70668cfd32 Mon Sep 17 00:00:00 2001 From: 2910410219 <2910410219@qq.com> Date: Tue, 1 Sep 2026 17:20:16 +0800 Subject: [PATCH 8/8] =?UTF-8?q?=E4=BB=B7=E6=A0=BC=E7=AE=A1=E7=90=86?= =?UTF-8?q?=EF=BC=9A=E5=AA=92=E4=BD=93=E4=BB=B7=E6=A0=BC=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E4=B8=89=E4=B8=AA=E8=BE=93=E5=85=A5=E6=A1=86=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=B8=8A=E6=96=B9=20label?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code --- .../system/price/component/modelEditor.vue | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/views/system/price/component/modelEditor.vue b/src/views/system/price/component/modelEditor.vue index 2912c4f..78f2109 100644 --- a/src/views/system/price/component/modelEditor.vue +++ b/src/views/system/price/component/modelEditor.vue @@ -134,9 +134,18 @@ - - - +
+ 输入价 + +
+
+ 输出价 + +
+
+ 缓存命中价 + +
删除 + 添加媒体价格 @@ -527,7 +536,7 @@ defineExpose({ save }); .media-price-row { display: flex; - align-items: center; + align-items: flex-end; gap: 8px; margin-bottom: 8px; @@ -536,9 +545,21 @@ defineExpose({ save }); flex-shrink: 0; } - .mp-num { + .mp-field { flex: 1; min-width: 0; + display: flex; + flex-direction: column; + gap: 2px; + + .mp-label { + font-size: 12px; + color: #606266; + } + + .mp-num { + width: 100%; + } } }