import { _ as __nuxt_component_0 } from './LangSelect-Bvw-e1Cn.mjs';
import { defineComponent, ref, computed, reactive, watch, useSSRContext } from 'vue';
import { ssrRenderAttrs, ssrRenderStyle, ssrIncludeBooleanAttr, ssrLooseContain, ssrLooseEqual, ssrRenderList, ssrRenderAttr, ssrInterpolate, ssrRenderClass, ssrRenderComponent, ssrRenderDynamicModel } from 'vue/server-renderer';
import { u as useApi } from './useApi-BNSkSrat.mjs';
import { u as useToast } from './useToast-MBvLaNjH.mjs';
import { _ as _export_sfc } from './server.mjs';
import './useLangOptions-CXXbv5NM.mjs';
import '../nitro/nitro.mjs';
import 'node:http';
import 'node:https';
import 'node:events';
import 'node:buffer';
import 'node:fs';
import 'node:path';
import 'node:crypto';
import 'node:url';
import '../routes/renderer.mjs';
import 'vue-bundle-renderer/runtime';
import 'unhead/server';
import 'devalue';
import 'unhead/utils';
import 'pinia';
import 'perfect-debounce';
const SCAT_MCP = 10;
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "serviceconfig",
__ssrInlineRender: true,
setup(__props) {
const REGIONS = [
{ value: 1, label: "中国" },
{ value: 2, label: "美国" },
{ value: 3, label: "东亚" },
{ value: 4, label: "东南亚" },
{ value: 5, label: "南亚" },
{ value: 6, label: "中东" },
{ value: 7, label: "欧洲" },
{ value: 8, label: "俄罗斯" },
{ value: 9, label: "非洲" },
{ value: 10, label: "南美" },
{ value: 11, label: "北美" },
{ value: 12, label: "大洋洲" },
{ value: 13, label: "巴西" },
{ value: 14, label: "印度" }
];
const SCAT = [
{ v: 1, label: "STT 语音识别", short: "STT", color: "#0369a1", bg: "#dbeafe" },
{ v: 8, label: "录音文件识别", short: "录音识别", color: "#0e7490", bg: "#cffafe" },
{ v: 2, label: "TTS 语音合成", short: "TTS", color: "#7c3aed", bg: "#ede9fe" },
{ v: 3, label: "MT 机器翻译", short: "MT", color: "#059669", bg: "#dcfce7" },
{ v: 4, label: "AST 端到端翻译", short: "AST", color: "#c2410c", bg: "#fff7ed" },
{ v: 5, label: "LLM 文本大模型", short: "文本LLM", color: "#b45309", bg: "#fef9c3" },
{ v: 9, label: "多媒体大模型", short: "多媒体LLM", color: "#9333ea", bg: "#f3e8ff" },
{ v: 7, label: "STS 端到端对话", short: "STS", color: "#be185d", bg: "#fce7f3" },
{ v: 6, label: "存储 OSS/COS", short: "存储", color: "#475569", bg: "#f1f5f9" },
{ v: 10, label: "MCP 服务", short: "MCP", color: "#0d9488", bg: "#ccfbf1" }
];
const { webApi } = useApi();
useToast();
const scopeApp = ref("");
const scopeApps = ref([]);
function scatInfo(v) {
return SCAT.find((s) => s.v === v) || { short: "?", color: "#999", bg: "#eee", v: 0, label: "" };
}
function isMcpType() {
if (editType.value === SCAT_MCP) return true;
return !!curSvc.value && parseCats(curSvc.value.categories).includes(SCAT_MCP);
}
function parseCats(s) {
return (s || "").split(",").map((x) => parseInt(x) || 0).filter((x) => x > 0);
}
function encCount(s) {
return (s.fields || []).filter((f) => f.encrypted).length;
}
function regionChip(s) {
const rs = svcRegions.value[s.id] || [];
const names = rs.map((v) => REGIONS.find((x) => x.value === v)?.label || "区域" + v);
if (curRegion.value === 0) {
if (!rs.length) return { text: "全区域通用", cls: "plain", title: "未做任何区域分叉,各区域都用默认配置" };
return { text: rs.length + " 区域分叉", cls: "has", title: "已分叉:" + names.join("、") };
}
if (rs.includes(curRegion.value)) {
return { text: "本区域已分叉", cls: "has", title: curRegionLabel.value + "有独立的字段配置" };
}
return { text: "继承默认", cls: "plain", title: curRegionLabel.value + "未分叉,使用默认配置" };
}
const VOICE_CATS = [2, 4, 7];
const voiceRows = ref([]);
const voicesLoading = ref(false);
const isVoiceCat = computed(() => VOICE_CATS.includes(editType.value));
const voiceLangGroups = computed(() => {
const map = /* @__PURE__ */ new Map();
for (const v of voiceRows.value) {
if (!map.has(v.language)) map.set(v.language, []);
map.get(v.language).push(v);
}
return Array.from(map, ([lang, items]) => ({ lang, items }));
});
async function loadVoicesForProvider(provider) {
const p = (provider || "").trim();
voiceRows.value = [];
if (!p) return;
voicesLoading.value = true;
try {
const d = await webApi("getttsvoices", { provider: p });
voiceRows.value = d?.voices ?? [];
} catch {
voiceRows.value = [];
} finally {
voicesLoading.value = false;
}
}
const backendReady = ref(true);
const allSvcs = ref([]);
const svcRegions = ref({});
const curSvc = ref(null);
const curRegion = ref(0);
const svcModalVisible = ref(false);
const detailMode = ref("default");
const editForm = reactive({ id: "", name: "", provider: "", description: "", enable: false });
const editType = ref(0);
const editFields = ref([]);
const selectedTemplateId = ref("");
const wizardStep = ref(1);
const overrideFields = ref([]);
const extraRegionFields = ref([]);
const regionLoading = ref(false);
const templates = ref([]);
const curRegionLabel = computed(() => {
const r = REGIONS.find((x) => x.value === curRegion.value);
return r ? r.label : "区域" + curRegion.value;
});
const groupedSvcs = computed(() => {
const groups = SCAT.map((sc) => ({
...sc,
items: allSvcs.value.filter((s) => parseCats(s.categories).includes(sc.v))
})).filter((g) => g.items.length);
const unclassified = allSvcs.value.filter((s) => !parseCats(s.categories).length);
if (unclassified.length) {
groups.push({ v: 0, label: "未分类", short: "未分类", color: "#64748b", bg: "#f1f5f9", items: unclassified });
}
return groups;
});
const folded = reactive({});
const allFolded = computed(() => groupedSvcs.value.length > 0 && groupedSvcs.value.every((g) => folded[g.v]));
const templatesByCat = computed(() => {
return SCAT.map((sc) => ({
cat: sc.v,
label: sc.label,
items: templates.value.filter((t) => t.category === sc.v)
})).filter((g) => g.items.length);
});
const templatesForType = computed(() => templates.value.filter((t) => t.category === editType.value));
const syncVisible = ref(false);
ref(false);
const syncDesc = ref("");
const syncScopeNote = ref("");
const syncTargets = ref([]);
const syncRegionList = computed(() => REGIONS.filter((r) => r.value !== curRegion.value));
const tplModalVisible = ref(false);
const tplView = ref("list");
const tplEditing = ref(false);
const tplForm = reactive({ id: "", category: 2, provider: "", name: "", description: "" });
const tplFields = ref([]);
watch([() => editForm.provider, isVoiceCat, svcModalVisible], ([provider, isVoice, visible]) => {
if (!visible || !isVoice) {
voiceRows.value = [];
return;
}
loadVoicesForProvider(provider);
}, { immediate: false });
return (_ctx, _push, _parent, _attrs) => {
const _component_LangSelect = __nuxt_component_0;
_push(`
`);
if (!backendReady.value) {
_push(`
⚠ 「第三方服务配置」后端接口暂不可用(连接失败),请检查 console 服务是否运行。
`);
} else {
_push(``);
}
_push(`
第三方服务配置
按服务类型分组管理已接入的第三方服务(火山引擎、阿里云、Azure 等)。默认展示全局默认配置,可切换到具体应用;区域默认中国,切换区域后点击卡片可为该区域覆盖字段值。
`);
if (curRegion.value !== 0) {
_push(`
✦ 当前处于「${ssrInterpolate(curRegionLabel.value)}」区域视图:点击服务卡片编辑该区域的字段分叉(覆盖值 / 停用字段 / 区域专属字段)。要改服务本身的默认配置,请切回「默认(全区域)」。
`);
} else {
_push(``);
}
if (groupedSvcs.value.length) {
_push(`
`);
ssrRenderList(groupedSvcs.value, (g) => {
_push(`
▾${ssrInterpolate(g.short)}${ssrInterpolate(g.label)}
${ssrInterpolate(g.items.length)} 个服务 `);
ssrRenderList(g.items, (s) => {
_push(`
${ssrInterpolate(s.icon || (s.name || "").charAt(0))}
${ssrInterpolate(s.name)}${ssrInterpolate(s.enable ? "启用" : "停用")}
${ssrInterpolate(s.id)}
${ssrInterpolate(s.description || "—")}
`);
if (s.provider) {
_push(`${ssrInterpolate(s.provider)}`);
} else {
_push(``);
}
_push(`${ssrInterpolate((s.fields || []).length)} 字段`);
if (encCount(s)) {
_push(`🔒${ssrInterpolate(encCount(s))}`);
} else {
_push(``);
}
_push(`
${ssrInterpolate(regionChip(s).text)}
`);
});
_push(`
+添加${ssrInterpolate(g.short)}服务
`);
});
_push(`
`);
} else {
_push(`
`);
}
if (svcModalVisible.value) {
_push(`
`);
if (detailMode.value === "default" && curSvc.value) {
_push(`
${ssrInterpolate(curSvc.value.icon || (curSvc.value.name || "").charAt(0))}
${ssrInterpolate(curSvc.value.name)}
${ssrInterpolate(curSvc.value.id)}`);
if (editForm.provider) {
_push(` · 服务商 ${ssrInterpolate(editForm.provider)}`);
} else {
_push(``);
}
_push(`
基本信息
服务类型
`);
if (editType.value) {
_push(`
${ssrInterpolate(scatInfo(editType.value).short)}`);
} else {
_push(``);
}
_push(`
${ssrInterpolate(editType.value ? scatInfo(editType.value).label : "未设置")}(类型在创建时确定,不可更改)`);
if (isVoiceCat.value) {
_push(`
内置音色表`);
if (voicesLoading.value) {
_push(`加载中…`);
} else if (voiceRows.value.length) {
_push(`${ssrInterpolate(editForm.provider)} · ${ssrInterpolate(voiceRows.value.length)} 个音色 / ${ssrInterpolate(voiceLangGroups.value.length)} 种语言`);
} else {
_push(`未覆盖 ${ssrInterpolate(editForm.provider || "该服务商")}`);
}
_push(``);
if (voiceRows.value.length) {
_push(``);
} else {
_push(``);
}
_push(`
`);
if (!voicesLoading.value && !voiceRows.value.length) {
_push(`
内置音色表里没有这家服务商,不影响接入——Agent 配音色时手填服务商的音色 id 即可。
`);
} else if (voiceRows.value.length) {
_push(`
`);
ssrRenderList(voiceLangGroups.value, (g) => {
_push(`
${ssrInterpolate(g.lang)}`);
ssrRenderList(g.items, (v, i) => {
_push(`${ssrInterpolate(v.gender === 1 ? "♂" : v.gender === 2 ? "♀" : "·")} ${ssrInterpolate(v.voice_id)}`);
if (i === 0) {
_push(`默认`);
} else {
_push(``);
}
_push(``);
});
_push(`
`);
});
_push(`
`);
} else {
_push(``);
}
if (voiceRows.value.length) {
_push(`
这 ${ssrInterpolate(voiceLangGroups.value.length)} 种语言就是该服务能说的语言,已作为下方 languages 的预填值——通话翻译、AI 助手排查「这条链路有没有服务支持」看的就是它。 音色本身是代码里的常量表(后端 comm/ttsvoice.go),实际用哪个在 Agent 里按语言+性别选,同组第一个是默认。
`);
} else {
_push(``);
}
_push(`
`);
} else {
_push(``);
}
_push(`
配置字段(全区域共用)
`);
if (isMcpType()) {
_push(`
✦ MCP 服务与其他第三方服务同表存储(svc_config):这里填的 url/type/tools 为全区域默认; 只在某些区域生效的地址,切到对应区域做分叉即可,卡片上会显示已分叉的区域数。
`);
} else {
_push(``);
}
_push(`
`);
if (!editFields.value.length) {
_push(`
尚无字段,点击下方添加
`);
} else {
_push(`
| 字段名 | 字段描述 | 加密 | 默认值 | |
`);
ssrRenderList(editFields.value, (f, i) => {
_push(` | | | `);
if (f.type === "langs") {
_push(ssrRenderComponent(_component_LangSelect, {
modelValue: f.def_value,
"onUpdate:modelValue": ($event) => f.def_value = $event,
multiple: true
}, null, _parent));
} else if (f.type === "lang") {
_push(ssrRenderComponent(_component_LangSelect, {
modelValue: f.def_value,
"onUpdate:modelValue": ($event) => f.def_value = $event
}, null, _parent));
} else if (f.encrypted) {
_push(`🔒 加密`);
} else if (isMcpType() && f.key === "type") {
_push(``);
} else {
_push(``);
}
_push(` | |
`);
});
_push(`
`);
}
_push(`
`);
} else if (detailMode.value === "new") {
_push(`
新增第三方服务
① 服务类型`);
if (editType.value) {
_push(` · ${ssrInterpolate(scatInfo(editType.value).short)}`);
} else {
_push(``);
}
_push(`
② 服务商`);
if (editForm.provider) {
_push(` · ${ssrInterpolate(editForm.provider)}`);
} else {
_push(``);
}
_push(`
③ 配置
`);
if (wizardStep.value === 1) {
_push(`
请选择要接入的服务类型(每个服务归属单一类型,独立配置):
`);
ssrRenderList(SCAT, (sc) => {
_push(`
${ssrInterpolate(sc.short)}${ssrInterpolate(sc.label)}
`);
});
_push(`
`);
} else if (wizardStep.value === 2) {
_push(`
为「${ssrInterpolate(scatInfo(editType.value).label)}」选择服务商:
`);
ssrRenderList(templatesForType.value, (t) => {
_push(`
${ssrInterpolate(t.provider)}${ssrInterpolate(t.name)}${ssrInterpolate((t.fields || []).length)} 字段
`);
});
_push(`
自定义空白自定义手动添加字段
`);
if (!templatesForType.value.length) {
_push(`
该类型暂无内置模板,可选「空白自定义」,或到「服务商模板」新增。
`);
} else {
_push(``);
}
_push(``);
} else {
_push(`
${ssrInterpolate(scatInfo(editType.value).short)}`);
if (editForm.provider) {
_push(`${ssrInterpolate(editForm.provider)}`);
} else {
_push(``);
}
_push(`
`);
if (isVoiceCat.value) {
_push(`
内置音色表`);
if (voicesLoading.value) {
_push(`加载中…`);
} else if (voiceRows.value.length) {
_push(`${ssrInterpolate(editForm.provider)} · ${ssrInterpolate(voiceRows.value.length)} 个音色 / ${ssrInterpolate(voiceLangGroups.value.length)} 种语言`);
} else {
_push(`未覆盖 ${ssrInterpolate(editForm.provider || "该服务商")}`);
}
_push(`
`);
if (!voicesLoading.value && !voiceRows.value.length) {
_push(`
内置音色表里没有这家服务商,不影响接入——Agent 配音色时手填服务商的音色 id 即可。
`);
} else if (voiceRows.value.length) {
_push(`
`);
ssrRenderList(voiceLangGroups.value, (g) => {
_push(`
${ssrInterpolate(g.lang)}`);
ssrRenderList(g.items, (v, i) => {
_push(`${ssrInterpolate(v.gender === 1 ? "♂" : v.gender === 2 ? "♀" : "·")} ${ssrInterpolate(v.voice_id)}`);
if (i === 0) {
_push(`默认`);
} else {
_push(``);
}
_push(``);
});
_push(`
`);
});
_push(`
`);
} else {
_push(``);
}
if (voiceRows.value.length) {
_push(`
这 ${ssrInterpolate(voiceLangGroups.value.length)} 种语言就是该服务能说的语言,已作为下方 languages 的预填值——通话翻译、AI 助手排查「这条链路有没有服务支持」看的就是它。 音色本身是代码里的常量表(后端 comm/ttsvoice.go),实际用哪个在 Agent 里按语言+性别选,同组第一个是默认。
`);
} else {
_push(``);
}
_push(`
`);
} else {
_push(``);
}
_push(`
配置字段
`);
if (!editFields.value.length) {
_push(`
尚无字段,选模板自动带出或点击下方添加
`);
} else {
_push(`
| 字段名 | 字段描述 | 加密 | 默认值 | |
`);
ssrRenderList(editFields.value, (f, i) => {
_push(` | | | `);
if (f.type === "langs") {
_push(ssrRenderComponent(_component_LangSelect, {
modelValue: f.def_value,
"onUpdate:modelValue": ($event) => f.def_value = $event,
multiple: true
}, null, _parent));
} else if (f.type === "lang") {
_push(ssrRenderComponent(_component_LangSelect, {
modelValue: f.def_value,
"onUpdate:modelValue": ($event) => f.def_value = $event
}, null, _parent));
} else if (f.encrypted) {
_push(`🔒 加密`);
} else if (isMcpType() && f.key === "type") {
_push(``);
} else {
_push(``);
}
_push(` | |
`);
});
_push(`
`);
}
_push(`
`);
}
_push(``);
} else if (detailMode.value === "region" && curSvc.value) {
_push(``);
if (regionLoading.value) {
_push(`
加载中...
`);
} else {
_push(`
${ssrInterpolate(curSvc.value.icon || (curSvc.value.name || "").charAt(0))}
${ssrInterpolate(curSvc.value.name)}
${ssrInterpolate(curRegionLabel.value)} 分叉模式 ✦ 当前编辑「${ssrInterpolate(curRegionLabel.value)}」的字段分叉:默认字段可覆盖值或勾选「不适用」停用;也可新增本区域专属字段。留空的覆盖字段继承默认值,加密字段留空保留已存储密文。
默认字段
| 字段名 | 字段描述 | 状态 | 本区域不适用 | 有效值 |
`);
ssrRenderList(overrideFields.value, (f, i) => {
_push(`| ${ssrInterpolate(f.key)} | ${ssrInterpolate(f.description)} | `);
if (f.disabled) {
_push(`停用`);
} else if (f.is_overridden) {
_push(`覆盖`);
} else {
_push(`继承`);
}
_push(` | | `);
if (f.type === "langs") {
_push(ssrRenderComponent(_component_LangSelect, {
modelValue: f._input,
"onUpdate:modelValue": ($event) => f._input = $event,
multiple: true,
disabled: f.disabled
}, null, _parent));
} else if (f.type === "lang") {
_push(ssrRenderComponent(_component_LangSelect, {
modelValue: f._input,
"onUpdate:modelValue": ($event) => f._input = $event,
disabled: f.disabled
}, null, _parent));
} else if (f.encrypted) {
_push(`🔒`);
} else if (isMcpType() && f.key === "type") {
_push(``);
} else {
_push(``);
}
_push(` |
`);
});
_push(``);
if (!overrideFields.value.length) {
_push(`| 该服务尚无默认字段 |
`);
} else {
_push(``);
}
_push(`
${ssrInterpolate(curRegionLabel.value)} 专属字段
`);
if (!extraRegionFields.value.length) {
_push(`
尚无区域专属字段,点击下方添加
`);
} else {
_push(`
`);
}
_push(`
`);
}
_push(``);
} else {
_push(``);
}
_push(`
`);
} else {
_push(``);
}
_push(`
同步到其他区域
${ssrInterpolate(syncDesc.value)}
${syncScopeNote.value ?? ""}
`);
ssrRenderList(syncRegionList.value, (r) => {
_push(``);
});
_push(`
⚠ 已勾选区域将被当前配置(值覆盖 / 区域专属字段 / 停用字段)整体替换;加密字段密文一并同步。
`);
if (tplView.value === "list") {
_push(`
服务商模板
模板是字段预设,新增服务时可一键带出。内置模板可编辑;删除后重启会被重新补回。
`);
ssrRenderList(templatesByCat.value, (g) => {
_push(`
${ssrInterpolate(g.label)}
`);
ssrRenderList(g.items, (t) => {
_push(`
${ssrInterpolate(t.name)}${ssrInterpolate(t.provider)}`);
if (t.builtin) {
_push(`内置`);
} else {
_push(``);
}
_push(`
${ssrInterpolate(t.id)} · ${ssrInterpolate((t.fields || []).length)} 字段
`);
});
_push(`
`);
});
_push(``);
if (!templates.value.length) {
_push(`
暂无模板
`);
} else {
_push(``);
}
_push(`
`);
} else {
_push(`
${ssrInterpolate(tplEditing.value ? "编辑模板" : "新增模板")}
字段
| 字段名 | 字段描述 | 加密 | 默认/提示值 | |
`);
ssrRenderList(tplFields.value, (f, i) => {
_push(` | | | `);
if (f.type === "langs") {
_push(ssrRenderComponent(_component_LangSelect, {
modelValue: f.def_value,
"onUpdate:modelValue": ($event) => f.def_value = $event,
multiple: true
}, null, _parent));
} else if (f.type === "lang") {
_push(ssrRenderComponent(_component_LangSelect, {
modelValue: f.def_value,
"onUpdate:modelValue": ($event) => f.def_value = $event
}, null, _parent));
} else {
_push(``);
}
_push(` | |
`);
});
_push(``);
if (!tplFields.value.length) {
_push(`| 尚无字段 |
`);
} else {
_push(``);
}
_push(`
`);
}
_push(`
`);
};
}
});
const _sfc_setup = _sfc_main.setup;
_sfc_main.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("pages/serviceconfig.vue");
return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
};
const serviceconfig = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-e10e0bc9"]]);
export { serviceconfig as default };
//# sourceMappingURL=serviceconfig-Bq9We0Qx.mjs.map