2 lines
13 KiB
JavaScript
2 lines
13 KiB
JavaScript
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};
|