<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">角色形象<span>*</span></div> <div class="upload"> <div class="sc"> <div class="imgs" @tap="selectImage"> <template v-if="!resultUrl"> <image class="img1" src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/06/03/0e628447-e818-4f5e-88b4-a2af61b00bbd.png"></image> <image class="img2" src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/06/03/63556f0a-87ac-4d0e-b034-1b3cc65f4d4d.png"></image> </template> <template v-else> <image class="img1" :src="resultUrl"></image> </template> </div> </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="selectVoice"> <div class="mtext" :class="{'active':voiceText!=='请选择音色'}">{{voiceText}}</div> <image src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/06/03/ebf6fcea-a7ba-4ba5-8f20-c70b16c7dc3f.png"></image> </div> </div> <div class="zt_btn" @tap="comfirmSure">{{agentId?'编辑角色':'创建角色'}}</div> <tt-cropper mode="free" :imageUrl="imageUrl" :width="500" :height="500" :radius="90" :delay="300" @cancel="onCancel" @confirm="onConfirm" ></tt-cropper> </view> </template> <script> const baseApi = require('@/http/baseApi.js') export default { data(){ return { type:'', backUrl:'/pages/home', title:'创建角色', voiceText:'请选择音色', fileList:[], 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": "" }, imageUrl:'', resultUrl:'https://transcend.ringzle.com/xiaozhi-app/profile/2025/06/12/c1c0caa2-3f0f-4df3-bfe5-567b0963f0aa.png', scanParams:'' } }, 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' } // this.deviceId = option.deviceId; if(option.agentId){ this.title = '编辑角色'; this.agentId = option.agentId; this.getDetail(); } if(option.scanParams) this.scanParams = JSON.parse(option.scanParams) this.getModelVoiceList(); }, watch:{ "agentDto.systemPrompt":{ handler(newVal,oldVal){ if(newVal!==oldVal&&!newVal){ this.showta = false; this.focus = true; } } } }, methods:{ selectImage(){ uni.chooseImage({ count: 1, sizeType: ['compressed'], success: (res) => { this.imageUrl = res.tempFilePaths[0]; } }); }, onCancel() { this.imageUrl = ""; }, async onConfirm(res) { this.resultUrl = await this.uploadFilePromise(res.tempFilePath); this.imageUrl = ""; }, 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(); } } }) }, selectVoice(){ uni.navigateTo({ url:'/pagesRole/voice', events:{ selectVoice:data=>{ this.agentDto.ttsVoiceId = data.id; this.voiceText = data.name; } } }) }, 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||'请选择音色'; }) }, comfirmSure(){ if(!this.resultUrl) return this.$showToast('请上传角色头像') if(!this.agentDto.agentName) return this.$showToast('请输入角色昵称') if(this.agentDto.voiceText==='请选择音色') return this.$showToast('请选择音色') let dto = JSON.parse(JSON.stringify(this.agentDto)); // if(this.deviceId) 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 = '中文'; dto.vllmModelId = 'VLLM_ChatGLMVLLM'; dto.avatar = this.resultUrl; if(this.scanParams){ this.$api.post('/device/bind',{ ...this.scanParams, "type": "", "userId": 0, "agentId": "" }).then(res=>{ if(res.data.code!==0) return this.$showToast(res.data.msg) dto.deviceId = res.data.data; this.$api.post('/agent',dto).then(res2=>{ if(res2.data.code!==0) return this.$showToast(res2.data.msg) this.$showToast('设备绑定成功'); setTimeout(()=>{ uni.reLaunch({ url:'/pages/home' }) },1500) }) }) }else{ 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.avatar) this.resultUrl = this.agentDto.avatar; if(this.agentDto.systemPrompt) this.showta = true; this.getModelVoiceList(); }) }, } } </script> <style scoped lang="scss"> ::v-deep .desc .u-textarea{ padding: 0 !important; } ::v-deep .u-upload__deletable{ width: 48rpx !important; height: 48rpx !important; } ::v-deep .u-upload__deletable__icon{ transform: scale(2.7) !important; top: 14rpx !important; right: 14rpx !important; } ::v-deep .u-upload__success{ border-width:24rpx !important; } ::v-deep .u-upload__success__icon{ transform: scale(2.7) !important; bottom: -16rpx !important; right: -16rpx !important; } ::v-deep .t-cropper{ left: 0; } .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>