Files
chat/backend/public/admin/assets/SettingsView-b3lrgubW.js
T
2026-08-05 15:56:08 +08:00

2 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import{_ as Z,u as G,i as K,D as Q,j as I,c as r,b as t,e as n,v,y as S,d as X,t as d,F as N,l as P,m as O,E as R,f as m,k as B,q as ee,r as i,p as M,n as $,x as te,o as u}from"./index-9aV50nsX.js";const ae={class:"panel"},oe={class:"form-group"},le={class:"form-group"},se={class:"check-item"},ne={class:"panel voice-persona-panel"},re={class:"persona-heading"},ie={class:"persona-preview"},ue={class:"persona-avatar"},de={class:"enable-switch"},pe={class:"preset-row"},ve=["onClick"],me={class:"form-grid"},ce={class:"form-group"},fe={class:"form-group"},be={class:"form-group"},ge={class:"form-group"},_e={class:"form-grid"},ke={class:"form-group"},we={class:"form-group"},ye={class:"form-grid form-grid-3"},xe={class:"form-group"},Ve={class:"form-group"},Ue={class:"form-group"},Ce={class:"input-suffix"},he={class:"form-group"},Ae={key:0,class:"reference-box"},Ie=["disabled"],Se={key:1,class:"form-group"},Oe={class:"persona-actions"},je=["disabled"],Te=["src"],ze={class:"panel feature-panel"},Ee={class:"feature-grid"},Ne=["onUpdate:modelValue"],Pe=["disabled"],Re={key:1,class:"success-msg"},Be={key:2,class:"form-error"},Me={__name:"SettingsView",setup($e){const j=G(),w=i("AI Chat"),y=i(!0),_=i(!1),x=i(!1),c=i(""),T=i(null),V=i(!1),U=i(!1),b=i(""),C=i(null),p=i(""),g=i(!1),k=M({markdown:!0,image:!0,video:!0,voice:!0,document:!0,emoji:!0,upload_image:!0,upload_video:!0,upload_file:!0,paste_image:!0,short_drama:!0}),o=M({enabled:!0,name:"小暖",greeting:"您好,我是 AI 客服小暖,请问有什么可以帮您?",role_prompt:"温暖、专业、耐心,像经验丰富的真人客服一样理解用户的真实诉求。",base_url:"http://127.0.0.1:50000",mode:"sft",speaker:"中文女",instruct_text:"请用温暖、自然、耐心的中文客服语气表达,语速适中,停顿真实,避免播音腔和夸张情绪。",prompt_text:"",prompt_wav:"",prompt_wav_name:"",sample_rate:22050,fallback_voice:"marin",connect_timeout_ms:800,timeout_seconds:8,failure_ttl:20}),H=[{id:"warm",label:"温柔耐心",name:"小暖",speaker:"中文女",fallback_voice:"marin",role_prompt:"温暖、专业、耐心,善于安抚情绪,像经验丰富的真人客服一样理解用户的真实诉求。",instruct_text:"请用温暖、自然、耐心的中文客服语气表达,语速适中,停顿真实,避免播音腔和夸张情绪。"},{id:"steady",label:"沉稳专业",name:"阿诚",speaker:"中文男",fallback_voice:"cedar",role_prompt:"沉稳、可靠、专业,回答简洁清楚,先解决问题再补充必要信息。",instruct_text:"请用沉稳可信、自然克制的中文男声表达,语速稍慢,停顿从容,不要播音腔。"},{id:"bright",label:"亲切活力",name:"小晴",speaker:"中文女",fallback_voice:"coral",role_prompt:"亲切、积极、有活力,表达轻松但不过度热情,快速抓住用户重点。",instruct_text:"请用亲切明亮、自然轻松的中文语气表达,节奏轻快,保留真实呼吸与停顿。"}],L={markdown:"Markdown 解析",image:"图片解析",video:"视频解析",voice:"语音解析",document:"文档解析",emoji:"表情",upload_image:"上传图片",upload_video:"上传视频",upload_file:"上传文件",paste_image:"粘贴图片",short_drama:"短剧工坊入口(登录用户)"},D=$(()=>(o.name||"AI").trim().slice(0,1).toUpperCase()),F=$(()=>["zero_shot","cross_lingual","instruct2"].includes(o.mode));K(W),Q(E);async function W(){var s,e;c.value="";try{const l=(await I.get("/admin/settings")).data.data||{};l.site_name&&(w.value=l.site_name.value),l.allow_register&&(y.value=l.allow_register.value===!0||l.allow_register.value==="true"),(s=l.features)!=null&&s.value&&Object.assign(k,l.features.value),(e=l.voice_persona)!=null&&e.value&&Object.assign(o,l.voice_persona.value)}catch(a){c.value=a.message||"设置加载失败"}}function q(s){Object.assign(o,{name:s.name,speaker:s.speaker,fallback_voice:s.fallback_voice,role_prompt:s.role_prompt,instruct_text:s.instruct_text}),p.value=`已应用“${s.label}”模板,保存后生效`,g.value=!1}async function z(s=!0){_.value=!0,x.value=!1,c.value="";try{return await I.put("/admin/settings",{site_name:w.value,allow_register:y.value?"true":"false",features:{...k},voice_persona:{...o}}),s!==!1&&(x.value=!0,setTimeout(()=>{x.value=!1},3e3)),!0}catch(e){return c.value=e.message||"保存失败",!1}finally{_.value=!1}}async function J(s){var a;const e=(a=s.target.files)==null?void 0:a[0];if(s.target.value="",!!e){V.value=!0,p.value="";try{const l=new FormData;l.append("file",e);const f=await I.post("/admin/voice/reference",l,{headers:{"Content-Type":"multipart/form-data"}});Object.assign(o,{prompt_wav:f.data.data.path,prompt_wav_name:f.data.data.name}),p.value=`音色样本“${f.data.data.name}”已上传`,g.value=!1}catch(l){p.value=l.message||"音色样本上传失败",g.value=!0}finally{V.value=!1}}}async function Y(){var s;U.value=!0,p.value="",g.value=!1,E();try{if(!await z(!1))throw new Error(c.value||"请先修正配置");const a=localStorage.getItem("admin_token"),l=await fetch("/api/admin/voice/preview",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify({text:`${o.greeting} 我会认真听取您的问题,并尽快为您处理。`})});if(!l.ok){const A=await l.json().catch(()=>null);throw new Error((A==null?void 0:A.message)||`试听生成失败(HTTP ${l.status}`)}const f=await l.blob();b.value=URL.createObjectURL(f),p.value="试听已生成,正在播放",await te();const h=(s=C.value)==null?void 0:s.play();await(h==null?void 0:h.catch(()=>{}))}catch(e){p.value=e.message||"试听生成失败",g.value=!0}finally{U.value=!1}}function E(){var s;(s=C.value)==null||s.pause(),b.value&&URL.revokeObjectURL(b.value),b.value=""}return(s,e)=>(u(),r("div",null,[e[39]||(e[39]=t("div",{class:"page-header"},[t("h2",null,"系统设置"),t("p",null,"配置站点功能、AI 客服人物与真人感音色")],-1)),t("div",ae,[e[17]||(e[17]=t("h3",{class:"section-title"},"站点配置",-1)),t("div",oe,[e[15]||(e[15]=t("label",null,"站点名称",-1)),n(t("input",{"onUpdate:modelValue":e[0]||(e[0]=a=>w.value=a),class:"form-input"},null,512),[[v,w.value]])]),t("div",le,[t("label",se,[n(t("input",{"onUpdate:modelValue":e[1]||(e[1]=a=>y.value=a),type:"checkbox"},null,512),[[S,y.value]]),e[16]||(e[16]=X(" 允许用户注册 ",-1))])])]),t("div",ne,[t("div",re,[t("div",ie,[t("span",ue,d(D.value),1),t("div",null,[e[18]||(e[18]=t("span",{class:"persona-kicker"},"AI CUSTOMER PERSONA",-1)),t("h3",null,d(o.name||"未命名客服"),1),t("p",null,d(o.greeting||"设置一句自然的客服开场语"),1)])]),t("label",de,[n(t("input",{"onUpdate:modelValue":e[2]||(e[2]=a=>o.enabled=a),type:"checkbox"},null,512),[[S,o.enabled]]),t("span",null,d(o.enabled?"CosyVoice 已启用":"使用备用音色"),1)])]),t("div",pe,[e[19]||(e[19]=t("span",null,"人物模板",-1)),(u(),r(N,null,P(H,a=>t("button",{key:a.id,type:"button",class:"preset-btn",onClick:l=>q(a)},d(a.label),9,ve)),64))]),t("div",me,[t("div",ce,[e[20]||(e[20]=t("label",null,"客服人物名称",-1)),n(t("input",{"onUpdate:modelValue":e[3]||(e[3]=a=>o.name=a),class:"form-input",maxlength:"40",placeholder:"例如:小暖"},null,512),[[v,o.name,void 0,{trim:!0}]])]),t("div",fe,[e[21]||(e[21]=t("label",null,"内置说话人",-1)),n(t("input",{"onUpdate:modelValue":e[4]||(e[4]=a=>o.speaker=a),class:"form-input",maxlength:"80",placeholder:"中文女 / 中文男"},null,512),[[v,o.speaker,void 0,{trim:!0}]])])]),t("div",be,[e[22]||(e[22]=t("label",null,"前台开场语",-1)),n(t("input",{"onUpdate:modelValue":e[5]||(e[5]=a=>o.greeting=a),class:"form-input",maxlength:"200",placeholder:"您好,我是 AI 客服小暖,请问有什么可以帮您?"},null,512),[[v,o.greeting,void 0,{trim:!0}]])]),t("div",ge,[e[23]||(e[23]=t("label",null,"人物性格与客服规则",-1)),n(t("textarea",{"onUpdate:modelValue":e[6]||(e[6]=a=>o.role_prompt=a),class:"form-input persona-textarea",rows:"3",maxlength:"2000",placeholder:"描述人物性格、服务方式、用词习惯和需要避免的表达"},null,512),[[v,o.role_prompt,void 0,{trim:!0}]])]),t("div",_e,[t("div",ke,[e[24]||(e[24]=t("label",null,"CosyVoice 服务地址",-1)),n(t("input",{"onUpdate:modelValue":e[7]||(e[7]=a=>o.base_url=a),class:"form-input",placeholder:"http://127.0.0.1:50000"},null,512),[[v,o.base_url,void 0,{trim:!0}]])]),t("div",we,[e[26]||(e[26]=t("label",null,"合成模式",-1)),n(t("select",{"onUpdate:modelValue":e[8]||(e[8]=a=>o.mode=a),class:"form-select"},[...e[25]||(e[25]=[R('<option value="sft" data-v-c3e4aba4>SFT 内置音色</option><option value="instruct" data-v-c3e4aba4>Instruct 情绪控制</option><option value="zero_shot" data-v-c3e4aba4>Zero-shot 克隆音色</option><option value="cross_lingual" data-v-c3e4aba4>跨语种克隆</option><option value="instruct2" data-v-c3e4aba4>Instruct2 克隆 + 情绪</option>',5)])],512),[[O,o.mode]])])]),t("div",ye,[t("div",xe,[e[28]||(e[28]=t("label",null,"输出采样率",-1)),n(t("select",{"onUpdate:modelValue":e[9]||(e[9]=a=>o.sample_rate=a),class:"form-select"},[...e[27]||(e[27]=[t("option",{value:22050},"22050 Hz · CosyVoice 1",-1),t("option",{value:24e3},"24000 Hz · CosyVoice 2/3",-1),t("option",{value:16e3},"16000 Hz",-1),t("option",{value:44100},"44100 Hz",-1),t("option",{value:48e3},"48000 Hz",-1)])],512),[[O,o.sample_rate,void 0,{number:!0}]])]),t("div",Ve,[e[30]||(e[30]=t("label",null,"备用神经音色",-1)),n(t("select",{"onUpdate:modelValue":e[10]||(e[10]=a=>o.fallback_voice=a),class:"form-select"},[...e[29]||(e[29]=[R('<option value="marin" data-v-c3e4aba4>Marin · 自然女声</option><option value="cedar" data-v-c3e4aba4>Cedar · 自然男声</option><option value="coral" data-v-c3e4aba4>Coral · 亲切明亮</option><option value="nova" data-v-c3e4aba4>Nova · 清晰柔和</option><option value="onyx" data-v-c3e4aba4>Onyx · 沉稳男声</option><option value="alloy" data-v-c3e4aba4>Alloy · 中性</option>',6)])],512),[[O,o.fallback_voice]])]),t("div",Ue,[e[32]||(e[32]=t("label",null,"单句超时",-1)),t("div",Ce,[n(t("input",{"onUpdate:modelValue":e[11]||(e[11]=a=>o.timeout_seconds=a),class:"form-input",type:"number",min:"2",max:"60"},null,512),[[v,o.timeout_seconds,void 0,{number:!0}]]),e[31]||(e[31]=t("span",null,"秒",-1))])])]),t("div",he,[e[33]||(e[33]=t("label",null,"音色表达指令",-1)),n(t("textarea",{"onUpdate:modelValue":e[12]||(e[12]=a=>o.instruct_text=a),class:"form-input persona-textarea",rows:"3",maxlength:"1000",placeholder:"例如:温暖自然、语速适中、停顿真实,避免播音腔和夸张情绪"},null,512),[[v,o.instruct_text,void 0,{trim:!0}]]),e[34]||(e[34]=t("p",{class:"field-hint"},"Instruct / Instruct2 模式会直接使用;其他模式也会保留,切换后无需重填。",-1))]),F.value?(u(),r("div",Ae,[e[35]||(e[35]=t("div",null,[t("strong",null,"真人音色参考"),t("p",null,"上传已取得说话人授权、无背景音乐的 WAV,并填写完全一致的逐字稿。")],-1)),t("input",{ref_key:"referenceInput",ref:T,hidden:"",type:"file",accept:".wav,audio/wav",onChange:J},null,544),t("button",{type:"button",class:"btn btn-ghost reference-btn",disabled:V.value,onClick:e[13]||(e[13]=a=>{var l;return(l=T.value)==null?void 0:l.click()})},d(V.value?"上传中...":o.prompt_wav_name||"上传 WAV 音色样本"),9,Ie)])):m("",!0),o.mode==="zero_shot"?(u(),r("div",Se,[e[36]||(e[36]=t("label",null,"参考音频逐字稿",-1)),n(t("textarea",{"onUpdate:modelValue":e[14]||(e[14]=a=>o.prompt_text=a),class:"form-input persona-textarea",rows:"3",maxlength:"1500",placeholder:"CosyVoice 3 示例:You are a helpful assistant.<|endofprompt|>这里填写参考音频逐字稿。"},null,512),[[v,o.prompt_text,void 0,{trim:!0}]])])):m("",!0),t("div",Oe,[B(j).hasButton("btn:settings:save")?(u(),r("button",{key:0,type:"button",class:"btn btn-ghost preview-btn",disabled:_.value||U.value,onClick:Y},d(U.value?"正在生成试听...":"保存并试听音色"),9,je)):m("",!0),b.value?(u(),r("audio",{key:1,ref_key:"previewAudio",ref:C,class:"preview-audio",src:b.value,controls:""},null,8,Te)):m("",!0),p.value?(u(),r("span",{key:2,class:ee(["voice-status",{error:g.value}])},d(p.value),3)):m("",!0)])]),t("div",ze,[e[37]||(e[37]=t("h3",{class:"section-title"},"功能开关(会员端)",-1)),e[38]||(e[38]=t("p",{class:"section-desc"},"关闭后,会员端对应功能将不可用",-1)),t("div",Ee,[(u(!0),r(N,null,P(k,(a,l)=>(u(),r("label",{key:l,class:"feature-item"},[n(t("input",{"onUpdate:modelValue":f=>k[l]=f,type:"checkbox"},null,8,Ne),[[S,k[l]]]),t("span",null,d(L[l]||l),1)]))),128))]),B(j).hasButton("btn:settings:save")?(u(),r("button",{key:0,class:"btn btn-primary",disabled:_.value,onClick:z},d(_.value?"保存中...":"保存全部设置"),9,Pe)):m("",!0),x.value?(u(),r("p",Re,"保存成功")):m("",!0),c.value?(u(),r("p",Be,d(c.value),1)):m("",!0)])]))}},Le=Z(Me,[["__scopeId","data-v-c3e4aba4"]]);export{Le as default};