<template> <view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}"> <cus-header :title='title' bgColor="transparent" :backUrl="backUrl"></cus-header> <div class="box"> <div class="title adfac">角色形象</div> <div class="upload"> <div class="sc"> <u-upload width="188rpx" height="188rpx" :fileList="fileList" @afterRead="afterRead" @delete="deletePic" :maxCount="1" > <div class="imgs"> <image class="img1" src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/05/29/44f91a62-29c1-4d1f-8064-0d7579da2fb0.png"></image> <image class="img2" src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/05/29/2acaa48e-aa42-4edf-b05b-2bcf6bf0d4f8.png"></image> </div> </u-upload> </div> <div class="text">上传角色形象</div> </div> </div> <div class="box adfacjb"> <div class="title">角色昵称<span>*</span></div> <input type="text" v-model="agentDto.agentName" placeholder="请输入角色昵称" placeholder-class="ph"> </div> <div class="box"> <div class="top adfacjb"> <div class="title">设定描述<span>*</span></div> <div class="tip" @tap="selectModel">角色设定辅助 ></div> </div> <div class="desc"> <u-parse :content="placeholder" @tap="showta=true;focus=true" v-if="!showta" style="color: #A6A6A6;font-size: 28rpx;line-height: 48rpx;"></u-parse> <u-textarea v-model="agentDto.systemPrompt" :focus="focus" autoHeight :maxlength="-1" border="none" v-else></u-textarea> </div> </div> <div class="box adfacjb"> <div class="title">声音<span>*</span></div> <div class="right adfac" @tap="show=true"> <div class="mtext" :class="{'active':voiceText!=='请选择音色'}">{{voiceText}}</div> <image src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/05/29/8feb394d-d9f6-4162-9f50-0e79fcf0864b.png"></image> </div> </div> <div class="zt_btn" @tap="comfirmSure">{{agentId?'编辑角色':'创建角色'}}</div> <u-picker :itemHeight="88" title="角色音色" :show="show" :columns="voiceList" keyName="name" @cancel="show=false" @confirm="confirm" :immediateChange="true" style="height: 500rpx;"> </u-picker> </view> </template> <script> const baseApi = require('@/http/baseApi.js') export default { data(){ return { type:'', backUrl:'/pages/home', title:'创建角色', voiceText:'请选择音色', fileList:[], show:false, voiceList:[], modelMap:new Map(), agentId:'', deviceId:'', showta:false, focus:false, placeholder:`详细描写设定信息,示例:<br> 你是一位经验丰富的英语老师,拥有激发学生学习热情的教学方法。你善于运用幽默和实际应用案例,使对话充满趣味。`, agentDto:{ "agentName": "", "asrModelId": "", "vadModelId": "", "llmModelId": "", "ttsModelId": "", "ttsVoiceId": "", "memModelId": "", "intentModelId": "", "systemPrompt": "", "langCode": "", "language": "", "deviceId": "", "chatHistoryConf": "" } } }, onLoad(option) { if(option?.type){ this.type = option?.type; if(option.type==1) this.backUrl = '/pages/home' else if(option.type==2) this.backUrl = '/pages/role' } if(option.agentId){ this.title = '编辑角色'; this.agentId = option.agentId; this.deviceId = option.deviceId; this.getDetail(); } this.getModelVoiceList(); }, watch:{ "agentDto.systemPrompt":{ handler(newVal,oldVal){ if(newVal!==oldVal&&!newVal){ this.showta = false; this.focus = true; } } } }, methods:{ // 删除图片 deletePic(event) { this.fileList.splice(event.index, 1); }, // 新增图片 async afterRead(event) { // 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式 let lists = [].concat(event.file); let fileListLen = this.fileList.length; lists.map((item) => { this.fileList.push({ ...item, status: "uploading", message: "上传中", }); }); for (let i = 0; i < lists.length; i++) { const result = await this.uploadFilePromise(lists[i].url); let item = this.fileList[fileListLen]; this.fileList.splice( fileListLen, 1, Object.assign(item, { status: "success", message: "", url: result, }) ); fileListLen++; } }, uploadFilePromise(url) { return new Promise((resolve, reject) => { uni.uploadFile({ url: baseApi.BaseApi + '/sys/oss/uploadFile', filePath: url, name: "file", header:{ token: uni.getStorageSync('token') }, success: (res) => { let data = JSON.parse(res.data); if(data){ if(data.code!==0) return setTimeout(() => { resolve(data.data); }, 1000); } }, }); }); }, selectModel(){ uni.navigateTo({ url:'/pagesRole/roleModel', events:{ selectRoleModel:data=>{ this.showta = true; this.agentDto = {...this.agentDto,...data}; this.getModelVoiceList(); } } }) }, getModelVoiceList(){ this.$api.get(`/models/${'TTS_EdgeTTS'}/voices`).then(res=>{ if(res.data.code!==0) return this.$showToast(res.data.msg) this.voiceList = [res.data.data]; this.voiceText = res.data.data.find(v=>v.id===this.agentDto.ttsVoiceId)?.name||'请选择音色'; }) }, confirm(e){ this.show = false; this.agentDto.ttsVoiceId = e.value[0].id; this.voiceText = e.value[0].name; }, comfirmSure(){ if(!this.agentDto.agentName) return this.$showToast('请输入角色昵称') if(this.agentDto.voiceText==='请选择音色') return this.$showToast('请选择音色') let dto = JSON.parse(JSON.stringify(this.agentDto)); if(this.agentId) dto.deviceId = this.deviceId; //默认固定值(暂时) dto.asrModelId = 'ASR_FunASR'; dto.vadModelId = 'VAD_SileroVAD'; dto.llmModelId = 'LLM_ChatGLMLLM'; dto.ttsModelId = 'TTS_EdgeTTS'; dto.memModelId = 'Memory_mem_local_short'; dto.intentModelId = 'Intent_function_call'; dto.chatHistoryConf = 1; dto.langCode = 'zh'; dto.language = '中文'; this.$api.post(this.agentId?`/agent/update/${this.agentId}`:'/agent',dto).then(res=>{ if(res.data.code!==0) return this.$showToast(res.data.msg) this.$showToast(this.agentId?'编辑成功':'创建成功'); setTimeout(()=>{ uni.reLaunch({ url:this.type==1?'/pages/home':'/pages/role' }) },1500) }) }, getDetail(){ this.$api.get(`/agent/${this.agentId}`).then(res=>{ if(res.data.code!==0) return this.$showToast(res.data.msg) this.agentDto = {...this.agentDto,...res.data.data}; if(this.agentDto.systemPrompt) this.showta = true; this.getModelVoiceList(); }) }, } } </script> <style scoped lang="scss"> ::v-deep .desc .u-textarea{ padding: 0 !important; } .ph{ font-family: PingFangSC, PingFang SC; font-weight: 400; font-size: 28rpx; color: #A6A6A6; line-height: 40rpx; text-align: right; } .page{ background: #F7F6F9; padding: 0 30rpx 30rpx; box-sizing: border-box; .box{ background: #FFFFFF; border-radius: 24rpx; margin-top: 20rpx; width: 100%; padding: 40rpx 30rpx; box-sizing: border-box; .title{ font-family: PingFang-SC, PingFang-SC; font-weight: bold; font-size: 30rpx; color: #111111; line-height: 32rpx; span{ font-size: 32rpx; color: #F31616; } } .upload{ margin-top: 12rpx; display: flex; flex-direction: column; align-items: center; .sc{ width: 188rpx; height: 188rpx; } .text{ font-family: PingFangSC, PingFang SC; font-weight: 400; font-size: 26rpx; color: #A6A6A6; line-height: 37rpx; text-align: center; margin-top: 8rpx; } } .right{ .mtext{ font-family: PingFangSC, PingFang SC; font-weight: 400; font-size: 28rpx; color: #A6A6A6; line-height: 40rpx; text-align: right; &.active{ color: #111111; } } image{ width: 36rpx; height: 36rpx; margin-left: 10rpx; } } .desc{ margin-top: 20rpx; // min-height: 200rpx; overflow-y: auto; } .tip{ font-family: PingFangSC, PingFang SC; font-weight: 400; font-size: 28rpx; color: #1B50FF; line-height: 40rpx; text-align: right; } .imgs{ width: 188rpx; height: 188rpx; border-radius: 50%; position: relative; .img1{ width: 188rpx; height: 188rpx; border-radius: 50%; position: absolute; z-index: 2; } .img2{ width: 42rpx; height: 42rpx; position: absolute; right: 0; bottom: 0; z-index: 3; } } } .zt_btn{ margin-top: 54rpx; } input{ border: none; text-align: right; font-size: 28rpx; } } </style>