diff --git a/public/web/subscribe.html b/public/web/subscribe.html index 176420a..886f7f3 100644 --- a/public/web/subscribe.html +++ b/public/web/subscribe.html @@ -468,7 +468,6 @@ try { const token = getToken(); - console.log('[subscribe] token:', token); const headers = { 'Content-Type': 'application/json', @@ -483,7 +482,6 @@ // 先获取文本,检查是否为有效JSON const text = await response.text(); - console.log('[subscribe] response text:', text); let result; try { @@ -502,7 +500,6 @@ renderTypeList(tenantModuleTypes); renderSkuList(assetData.skus || []); } catch (error) { - console.error('加载失败:', error); showError(error.message || '加载套餐信息失败,请稍后重试'); } finally { showLoading(false); @@ -692,8 +689,6 @@ // 延迟跳转回原页面 const targetUrl = decodeURIComponent(returnUrl); - // console.log('[subscribe] 开通成功,即将跳转到:', targetUrl); - // console.log('[subscribe] 原始 returnUrl:', returnUrl); setTimeout(() => { let finalUrl; diff --git a/src/api/digitalHuman/modelConfig/modelModule/index.ts b/src/api/digitalHuman/modelConfig/modelModule/index.ts index c7c4ae9..6a431fb 100644 --- a/src/api/digitalHuman/modelConfig/modelModule/index.ts +++ b/src/api/digitalHuman/modelConfig/modelModule/index.ts @@ -3,7 +3,7 @@ import request from '/@/utils/request'; export interface ModelModuleListParams { pageNum?: number; pageSize?: number; - keyword?: string; + modelName?: string; } export interface ModelFormItem { @@ -13,6 +13,54 @@ export interface ModelFormItem { type: 'input' | 'number' | 'textarea' | 'switch' | string; } +export interface ModelFormEntry { + key: string; + value: string; +} + +/** 模型类型(listType 接口项,字段名以后端为准,前端做兼容解析) */ +export interface ModelTypeListItem { + id?: number | string; + typeId?: number | string; + modelsType?: number | string; + name?: string; + typeName?: string; + label?: string; +} + +/** listType 标准返回:data.type 为 Record,如 { "1": "推理模型", "2": "图片模型" } */ +export function normalizeModelTypeOptions(res: { data?: unknown }): Array<{ id: number | string; label: string }> { + const data = res?.data as { type?: Record } | undefined; + const typeMap = data?.type; + if (typeMap && typeof typeMap === 'object' && !Array.isArray(typeMap)) { + return Object.entries(typeMap) + .map(([key, label]) => { + const n = Number(key); + const id = Number.isNaN(n) ? key : n; + return { id, label: String(label ?? '') }; + }) + .sort((a, b) => { + const na = Number(a.id); + const nb = Number(b.id); + if (!Number.isNaN(na) && !Number.isNaN(nb)) { + return na - nb; + } + return String(a.id).localeCompare(String(b.id)); + }); + } + + /** 兼容旧结构:data 为数组或 data.list */ + const raw = res?.data; + const arr: ModelTypeListItem[] = Array.isArray(raw) ? raw : ((raw as { list?: ModelTypeListItem[] })?.list ?? []); + return arr + .map((item) => { + const id = item.id ?? item.typeId ?? item.modelsType; + const label = item.name ?? item.typeName ?? item.label ?? (id != null && id !== '' ? String(id) : ''); + return { id: id as number | string, label: label || String(id) }; + }) + .filter((x) => x.id !== undefined && x.id !== null && x.id !== ''); +} + export interface ModelModuleItem { id: number | string; tenantId?: number; @@ -23,18 +71,28 @@ export interface ModelModuleItem { deletedAt?: string | null; isDeleted?: boolean; modelName: string; + /** 模型类型 ID,与 listType 返回项对应 */ + modelsType?: number | string; baseUrl: string; - route: string; + route?: string; httpMethod: string; apiKey?: string; + isPrivate?: number; + isChatModel?: number; enabled: number; maxConcurrency: number; queueLimit: number; - timeoutMs: number; + timeoutMs?: number; + timeoutSeconds?: number; + expectedSeconds?: number; retryTimes: number; retryQueueMaxSeconds: number; autoCleanSeconds: number; remark?: string; + headMsg?: string; + form?: ModelFormEntry[] | Record; + requestMapping?: Record; + responseMapping?: Record; } export interface ModelModuleListResponse { @@ -48,17 +106,23 @@ export interface ModelModuleListResponse { export interface CreateModelParams { modelName: string; - modelsType: number; + /** 与 listType 返回的类型 id 一致,可能为数字或字符串 */ + modelsType: number | string; baseUrl: string; - route: string; - httpMethod: string; + httpMethod?: string; headMsg?: string; + isPrivate: number; enabled: number; - maxConcurrency: number; - queueLimit: number; + isChatModel: number; + apiKey?: string; + form: ModelFormEntry[]; + requestMapping?: Record; + responseMapping?: Record; + maxConcurrency?: number; + queueLimit?: number; timeoutSeconds: number; expectedSeconds: number; - retryTimes: number; + retryTimes?: number; retryQueueMaxSeconds: number; autoCleanSeconds: number; remark?: string; @@ -93,6 +157,16 @@ export function getModelModuleList(params?: ModelModuleListParams) { }); } +/** + * 获取模型类型列表(用于下拉与列表回显) + */ +export function getModelTypeList() { + return request({ + url: '/model-gateway/model/listType', + method: 'get', + }); +} + /** * 新增模型配置 */ @@ -110,7 +184,7 @@ export function addModelModule(data: CreateModelParams) { export function updateModelModule(data: Partial & { id: number | string }) { return request({ url: '/model-gateway/model/updateModel', - method: 'post', + method: 'put', data, }); } @@ -121,17 +195,18 @@ export function updateModelModule(data: Partial & { id: numbe export function deleteModelModule(id: number | string) { return request({ url: '/model-gateway/model/deleteModel', - method: 'post', + method: 'delete', data: { id }, }); } /** - * 获取模型配置(按类型分组) + * 获取单条模型配置详情(编辑用,需传 id) */ -export function getModelConfig() { +export function getModelModuleDetail(id: number | string) { return request({ url: '/model-gateway/model/getModel', method: 'get', + params: { id }, }); } diff --git a/src/components/model/ModelSelector.vue b/src/components/model/ModelSelector.vue index f2e75fa..032b779 100644 --- a/src/components/model/ModelSelector.vue +++ b/src/components/model/ModelSelector.vue @@ -2,8 +2,10 @@ - - + + 搜索 @@ -94,7 +96,7 @@ const props = withDefaults(defineProps(), { const emit = defineEmits(); const visible = ref(false); -const searchParams = reactive({ keyword: '' }); +const searchParams = reactive({ modelName: '' }); const pagination = reactive({ pageNum: 1, pageSize: 10, total: 0 }); const modelList = ref([]); const loading = ref(false); @@ -133,7 +135,7 @@ const fetchModelList = async () => { const params = { pageNum: pagination.pageNum, pageSize: pagination.pageSize, - keyword: searchParams.keyword || undefined, + modelName: searchParams.modelName || undefined, }; const res = await getModelModuleList(params, { errorMode: 'message' }); modelList.value = res.data?.list || []; diff --git a/src/utils/assetSubscribe.ts b/src/utils/assetSubscribe.ts index 27fead5..4cd5c73 100644 --- a/src/utils/assetSubscribe.ts +++ b/src/utils/assetSubscribe.ts @@ -5,10 +5,10 @@ const SUBSCRIBE_PAGE_URL = '/web/subscribe.html'; const ROUTE_ASSET_MAP: Record = { // CID广告业务(聚合广告) '/cidService': { assetId: '696f423705e496ba4ccbe665', serviceName: '聚合广告' }, - + // AI客服业务 '/customerService': { assetId: '696f421205e496ba4ccbe662', serviceName: 'AI客服' }, - + // 聚合电商业务(资产管理) '/assets': { assetId: '696b4acd1be1c8b76c4b4c15', serviceName: '资产管理' }, }; @@ -21,14 +21,14 @@ export function getAssetInfoByRoute(routePath: string): { assetId: string; servi if (ROUTE_ASSET_MAP[routePath]) { return ROUTE_ASSET_MAP[routePath]; } - + // 前缀匹配 for (const [prefix, info] of Object.entries(ROUTE_ASSET_MAP)) { if (routePath.startsWith(prefix)) { return info; } } - + return null; } @@ -41,7 +41,6 @@ export function redirectToSubscribePage(assetId: string) { const returnUrl = encodeURIComponent(window.location.href); // 构建跳转URL const url = `${SUBSCRIBE_PAGE_URL}?assetId=${assetId}&returnUrl=${returnUrl}`; - console.log('[redirectToSubscribePage] 跳转到开通页面:', url); window.location.href = url; } @@ -49,17 +48,13 @@ export function redirectToSubscribePage(assetId: string) { * 处理 402 错误码(模块未开通) */ export function handleModuleNotEnabled(routePath: string): boolean { - console.log('[模块未开通] 当前路由路径:', routePath); const assetInfo = getAssetInfoByRoute(routePath); - console.log('[模块未开通] 匹配到的资产信息:', assetInfo); - + if (assetInfo) { redirectToSubscribePage(assetInfo.assetId); return true; } - - // 如果没有匹配到路由,尝试使用默认的资产管理 - console.warn('[模块未开通] 未匹配到路由,使用默认资产管理'); + redirectToSubscribePage('696b4acd1be1c8b76c4b4c15'); return true; } diff --git a/src/utils/request.ts b/src/utils/request.ts index ae304de..623b400 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -202,16 +202,15 @@ const responseInterceptor = (response: AxiosResponse) => { // 业务失败处理 if (code !== undefined && !knownSuccessCodes.includes(code)) { let errorMsg: string; - + // 已知的业务错误 code,使用后端返回的 message if (knownErrorCodes.includes(code)) { errorMsg = message || `请求失败(${code})`; } else { // 未知的 code,统一提示后端异常 errorMsg = '后端异常,请联系管理员'; - console.error(`未知的业务错误码: ${code}, 原始消息: ${message}`); } - + showErrorMessage(errorMsg, config); return Promise.reject(new Error(errorMsg)); } diff --git a/src/views/assets/asset/component/editAsset.vue b/src/views/assets/asset/component/editAsset.vue index eba6a3e..ca99726 100644 --- a/src/views/assets/asset/component/editAsset.vue +++ b/src/views/assets/asset/component/editAsset.vue @@ -19,7 +19,7 @@ - + - @@ -98,12 +97,7 @@ class="w100" clearable > - + - + - + - + - + @@ -220,7 +196,6 @@ - 资产描述 @@ -229,7 +204,6 @@ - 实物资产配置 @@ -249,7 +223,7 @@ 虚拟资产配置 - + @@ -302,9 +276,17 @@ : - + - 添加请求头 + 添加请求头 @@ -317,9 +299,17 @@ : - + - 添加请求参数 + 添加请求参数 @@ -337,7 +327,13 @@ - + @@ -346,7 +342,7 @@ 服务资产配置 - + @@ -386,9 +382,13 @@ - + - + - @@ -396,29 +396,33 @@ - - 添加时间段 - + 添加时间段 - + - + - + @@ -591,8 +595,6 @@ const assetFormDiff = createFormDiff>(); // 获取租户ID const tenantId = ref(Session.get('userInfo')?.tenantId || ''); -console.log(tenantId.value,'租户id'); - const formatImageUrl = (url?: string) => { if (!url) return ''; @@ -980,7 +982,7 @@ const openDialog = (row?: any, edit?: boolean) => { if (data.type === 'virtual' && data.virtualAssetConfig) { // 先处理 headers 和 params 为数组格式,再赋值 const config = { ...data.virtualAssetConfig }; - + // 确保 apiConfig 存在 if (!config.apiConfig) { config.apiConfig = { @@ -1036,7 +1038,7 @@ const openDialog = (row?: any, edit?: boolean) => { exc.exceptionType = 'dayOfWeek'; } else { // 默认值 - exc.exceptionType = 'date'; + exc.exceptionType = 'date'; } } }); @@ -1121,7 +1123,7 @@ const onCancel = () => { // 上传图片并返回URL const uploadImage = async (file: File): Promise => { const res: any = await uploadAssetImage(file); - + // 1. 尝试获取并设置 fileAddressPrefix // 优先检查顶层,再检查 data 内部 if (res.fileAddressPrefix) { @@ -1133,7 +1135,7 @@ const uploadImage = async (file: File): Promise => { // 2. 尝试获取 fileURL / url // 优先检查顶层 fileURL if (res.fileURL) return res.fileURL; - + // 检查 data 对象中的 fileURL 或 url if (res.data && typeof res.data === 'object') { if (res.data.fileURL) return res.data.fileURL; @@ -1205,7 +1207,7 @@ const buildRequestBody = async (): Promise => { } else if (ruleForm.type === 'virtual') { // 深拷贝 virtualAssetConfig 以避免修改原对象 const virtualConfig = JSON.parse(JSON.stringify(ruleForm.virtualAssetConfig)); - + // 将数组转换为对象 if (virtualConfig.apiConfig) { if (Array.isArray(virtualConfig.apiConfig.headers)) { @@ -1215,7 +1217,7 @@ const buildRequestBody = async (): Promise => { }); virtualConfig.apiConfig.headers = headersObj; } - + if (Array.isArray(virtualConfig.apiConfig.params)) { const paramsObj: Record = {}; virtualConfig.apiConfig.params.forEach((item: KeyValuePair) => { @@ -1224,7 +1226,7 @@ const buildRequestBody = async (): Promise => { virtualConfig.apiConfig.params = paramsObj; } } - + body.virtualAssetConfig = virtualConfig; } else if (ruleForm.type === 'service') { body.serviceAssetConfig = ruleForm.serviceAssetConfig; @@ -1252,9 +1254,7 @@ const buildRequestBody = async (): Promise => { // 只有单选和多选类型才传递 options,且只传递选中的值对应的选项 if ((attr.type === 'select' || attr.type === 'multi_select') && attr.options && value) { const selectedValues = Array.isArray(value) ? value : [value]; - metaItem.options = attr.options.filter((opt: { label: string; value: string }) => - selectedValues.includes(opt.value) - ); + metaItem.options = attr.options.filter((opt: { label: string; value: string }) => selectedValues.includes(opt.value)); } metadataArray.push(metaItem); @@ -1269,13 +1269,13 @@ const buildRequestBody = async (): Promise => { const onSubmit = async () => { const form = formRef.value; if (!form) return; - + form.validate(async (valid: boolean) => { if (valid) { submitLoading.value = true; try { const fullRequestBody = await buildRequestBody(); - + let requestBody: any; if (isEdit.value) { // 编辑模式:通过 _originalData 让拦截器自动处理最小化传参 @@ -1296,7 +1296,7 @@ const onSubmit = async () => { closeDialog(); emit('getAssetList'); } catch (error) { - console.error('提交失败:', error); + ElMessage.error('提交失败'); } finally { submitLoading.value = false; } diff --git a/src/views/assets/asset/component/skuDialog.vue b/src/views/assets/asset/component/skuDialog.vue index 2bac64c..eb1e300 100644 --- a/src/views/assets/asset/component/skuDialog.vue +++ b/src/views/assets/asset/component/skuDialog.vue @@ -1,5 +1,5 @@ - + @@ -28,10 +28,15 @@ {{ item.name }}: {{ item.options?.[0]?.label || (Array.isArray(item.value) ? item.value[0] : item.value) }} - - - {{ key }}: {{ value }} - + + {{ key }}: {{ value }} - @@ -47,9 +52,7 @@ - - ¥{{ (scope.row.price / 100).toFixed(2) }} - + ¥{{ (scope.row.price / 100).toFixed(2) }} @@ -122,7 +125,15 @@ {{ attr.name }}: - + @@ -144,12 +155,7 @@ 数值越小越靠前 - + @@ -194,13 +200,7 @@ style="width: 200px" /> - + @@ -217,7 +217,18 @@ import { ref, reactive } from 'vue'; import { ElMessage, type FormInstance, type FormRules } from 'element-plus'; import { ElMessageBox } from 'element-plus'; -import { listAssetSkus, createAssetSku, updateAssetSku, deleteAssetSku, getAssetSku, getAsset, uploadAssetImage, getSpecsUnitOptions, getStockFormFields, stockOperation } from '/@/api/assets/asset'; +import { + listAssetSkus, + createAssetSku, + updateAssetSku, + deleteAssetSku, + getAssetSku, + getAsset, + uploadAssetImage, + getSpecsUnitOptions, + getStockFormFields, + stockOperation, +} from '/@/api/assets/asset'; import { createFormDiff } from '/@/utils/diffUtils'; import OperationLogDialog from '../../component/operationLogDialog.vue'; import type { UploadRequestOptions, UploadUserFile } from 'element-plus'; @@ -307,15 +318,15 @@ const skuRules: FormRules = { specsUnit: [{ required: true, message: '请选择规格单位', trigger: 'change' }], specsCount: [ { required: true, message: '请输入规格数量', trigger: 'blur' }, - { type: 'number', min: 1, message: '规格数量必须大于0', trigger: 'blur' } + { type: 'number', min: 1, message: '规格数量必须大于0', trigger: 'blur' }, ], price: [ { required: true, message: '请输入价格', trigger: 'blur' }, - { type: 'number', min: 0.01, message: '价格必须大于0', trigger: 'blur' } + { type: 'number', min: 0.01, message: '价格必须大于0', trigger: 'blur' }, ], stock: [ { required: true, message: '请输入库存数量', trigger: 'blur' }, - { + { validator: (rule: any, value: any, callback: any) => { if (skuForm.unlimitedStock) { callback(); @@ -324,12 +335,11 @@ const skuRules: FormRules = { } else { callback(); } - }, - trigger: 'blur' - } + }, + trigger: 'blur', + }, ], imageUrl: [{ required: true, message: '请上传SKU图片', trigger: 'change' }], - }; // 打开弹窗 @@ -560,14 +570,14 @@ const onGenerateStock = async (row: any) => { currentSkuName.value = row.skuName; stockFormVisible.value = true; stockFormLoading.value = true; - + // 重置表单 Object.keys(stockForm).forEach((key) => delete stockForm[key]); - + try { const res = await getStockFormFields(row.id); stockFormFields.value = res.data.fields || []; - + // 设置默认值,根据字段类型转换 stockFormFields.value.forEach((field) => { if (field.default !== undefined) { @@ -584,7 +594,6 @@ const onGenerateStock = async (row: any) => { } }); } catch (error) { - console.error('获取库存表单字段失败:', error); ElMessage.error('获取库存表单字段失败'); stockFormVisible.value = false; } finally { @@ -596,7 +605,7 @@ const onGenerateStock = async (row: any) => { const onSubmitStock = async () => { const form = stockFormRef.value; if (!form) return; - + form.validate(async (valid: boolean) => { if (valid) { stockSubmitLoading.value = true; @@ -613,13 +622,13 @@ const onSubmitStock = async () => { submitData[field.name] = value; } }); - + await stockOperation(submitData as any); ElMessage.success('库存生成成功'); stockFormVisible.value = false; getSkuList(); } catch (error) { - console.error('库存操作失败:', error); + ElMessage.error('库存操作失败'); } finally { stockSubmitLoading.value = false; } @@ -702,7 +711,7 @@ const formatImageUrl = (url?: string) => { // 上传图片 const uploadImage = async (file: File): Promise => { const res: any = await uploadAssetImage(file); - + if (res.fileAddressPrefix) { fileAddressPrefix.value = res.fileAddressPrefix; } else if (res.data && typeof res.data === 'object' && res.data.fileAddressPrefix) { @@ -835,7 +844,7 @@ const onSubmitSku = async () => { specsUnit: skuForm.specsUnit, specsCount: skuForm.specsCount, }; - + const changedFields = skuFormDiff.getChanges(currentFormData, { alwaysInclude: ['id'], transformers: { @@ -844,15 +853,15 @@ const onSubmitSku = async () => { imageUrl: (val) => val || undefined, }, }); - + // 添加 id const changedData: Record = { id: editSkuId.value, ...changedFields }; - + // 比较规格属性 if (specValuesMapDiff.hasChanges(specValuesMap) && specValues.length > 0) { changedData.specValues = specValues; } - + await updateAssetSku(changedData as any); } else { // 新增模式:传递所有字段 diff --git a/src/views/digitalHuman/modelConfig/modelModule/component/editModule.vue b/src/views/digitalHuman/modelConfig/modelModule/component/editModule.vue index 1317906..4e6c4d5 100644 --- a/src/views/digitalHuman/modelConfig/modelModule/component/editModule.vue +++ b/src/views/digitalHuman/modelConfig/modelModule/component/editModule.vue @@ -1,7 +1,14 @@ - + @@ -12,20 +19,18 @@ - - - + - - - - - - - + + @@ -38,6 +43,33 @@ + + + + 私有 + 公共 + + + + + + + + + + + + 是 + 否 + + + 配置请求头 ({{ state.headers.length }}) @@ -110,13 +142,43 @@ + + + + + + + + + + 取 消 - {{ state.dialog.submitTxt }} + + {{ state.dialog.submitTxt }} + @@ -161,11 +223,32 @@