Ver código fonte

fix: 更换创建/编辑智能体接口

sheldon 2 meses atrás
pai
commit
2502cb32fa

+ 1 - 1
src/api/index.ts

@@ -86,7 +86,7 @@ api.interceptors.response.use(
     console.log(code, msg, 444444)
     if(code === -90){
       toast.error('Error', {
-        description: '克隆服务异常,请联系管理员',
+        description: msg,
       })
       return Promise.reject(response.data)
     }

+ 54 - 6
src/api/modules/anycallService.ts

@@ -1,6 +1,6 @@
 // anycall
 import { request } from '@/api'
-import type { TAgent } from '@/types/role'
+import type { TAgent, TAgentDetail } from '@/types/role'
 import type { TVoice } from '@/types/voice'
 
 // 角色搜索
@@ -58,12 +58,60 @@ export type TRole = {
   voiceFileName: string;
   voiceId: string;
 };
-export function saveRole(params: TRole){
-  if(params.id){
-    return request(`/anycall/update`, params)
-  }
-  return request(`/anycall/create`, params)
+
+export type TSaveRoleParams = {
+  /**
+   * 角色Id(编辑时指定)
+   */
+  id?: string;
+  /**
+   * 名称
+   */
+  name: string;
+  /**
+   * 选择语音 voiceId
+   */
+  voiceId: string;
+  /**
+   * 语言(当服务端没有对应国际化选项时,使用前端设置)
+   */
+  language: string;
+  /**
+   * 语言码
+   */
+  languageCode: string;
+  /**
+   * 区域名(当服务端没有对应国际化选项时,使用前端设置)
+   */
+  area: string;
+  /**
+   * 区域码
+   */
+  areaCode: string;
+  /**
+   * 简介
+   */
+  description: string;
+  /**
+   * 智能体人设提示词
+   */
+  prompt: string;
+  /**
+   * 头像地址
+   */
+  photo: string;
+  /**
+   * 性别
+   */
+  gender?: number;
+};
+export function saveRole(params: TSaveRoleParams){
+  return request(`/anycall/adm_clone/save`, params)
 }
+export function getRoleDetailById(params: {id: string}){
+  return request<TAgentDetail>(`/anycall/adm_clone/getById`, params)
+}
+
 // 删除智能体
 export function deleteClone(params: {cloneId: string}){
   return request(`/anycall/adm_clone/deleteClone`, params)

+ 12 - 1
src/components/LangSelector.vue

@@ -13,6 +13,7 @@ interface Props {
 // 定义组件事件
 interface Emits {
   (e: 'update:modelValue', value: string | undefined): void
+  (e: 'update:key', value: string | undefined): void
 }
 
 // 设置默认属性
@@ -59,6 +60,16 @@ const fetchData = async () => {
   }
 }
 
+const updateModelValue = (value: string) => {
+  emit('update:modelValue', value)
+  const f = options.value.find(item => item.value === value)
+  if(f){
+    emit('update:key', f.name)
+  }
+
+}
+
+
 onMounted(() => {
   fetchData()
 })
@@ -74,7 +85,7 @@ onMounted(() => {
     :class="className"
     clearable
     filterable
-    @update:model-value="(value) => emit('update:modelValue', value)"
+    @update:model-value="updateModelValue"
   >
     <el-option v-for="item in options" :key="item.value" :label="item.name" :value="item.value" />
   </el-select>

+ 11 - 1
src/components/NationalitySelector.vue

@@ -13,6 +13,7 @@ interface Props {
 // 定义组件事件
 interface Emits {
   (e: 'update:modelValue', value: string | undefined): void
+  (e: 'update:key', value: string | undefined): void
 }
 
 // 设置默认属性
@@ -63,6 +64,15 @@ onMounted(() => {
   fetchData()
 })
 
+const updateModelValue = (value: string) => {
+  emit('update:modelValue', value)
+  const f = options.value.find(item => item.value === value)
+  if(f){
+    emit('update:key', f.name)
+  }
+
+}
+
 </script>
 
 <template>
@@ -74,7 +84,7 @@ onMounted(() => {
     :class="className"
     clearable
     filterable
-    @update:model-value="(value) => emit('update:modelValue', value)"
+    @update:model-value="updateModelValue"
   >
     <el-option v-for="item in options" :key="item.value" :label="item.name" :value="item.value" />
   </el-select>

+ 87 - 0
src/types/role.ts

@@ -13,3 +13,90 @@ export type TAgent = {
   gender: number|string;
   voiceName?: string;
 };
+
+
+export type TAgentDetail = {
+  /**
+   * cloneId (waitgpt表示待创建的标识)
+   */
+  id: string;
+  /**
+   * 名称
+   */
+  name: string;
+  /**
+   * 联系次数
+   */
+  callings: number;
+  /**
+   * 创建人uid
+   */
+  uid: string;
+  /**
+   * 语言(当服务端没有对应国际化选项时,使用前端设置)
+   */
+  language: string;
+  /**
+   * 语言码
+   */
+  languageCode: string;
+  /**
+   * 区域名(当服务端没有对应国际化选项时,使用前端设置)
+   */
+  area: string;
+  /**
+   * 区域码
+   */
+  areaCode: string;
+  /**
+   * 性别
+   */
+  gender: number;
+  /**
+   * 简介
+   */
+  description: string;
+  /**
+   * 智能体人设提示词
+   */
+  prompt: string;
+  /**
+   * 标签
+   */
+  tags: string[];
+  /**
+   * 头像地址
+   */
+  photo: string;
+  /**
+   * 智能体id(aiseek中的智能体Id)
+   */
+  outAgentId: string;
+  /**
+   * 是否在当前用的通讯录
+   */
+  contactFlag: boolean;
+  /**
+   * 是否被推荐
+   */
+  topFlag: boolean;
+  /**
+   * 简化音色对象
+   */
+  simpleVoice: {
+    id: string;
+    name: string;
+    feature: string;
+    gender: number;
+  };
+  /**
+   * 简化用户对象
+   */
+  simpleUser: {
+    id: string;
+    name: string;
+    headImage: string;
+    gender: number;
+    nationality: string;
+  };
+};

+ 1 - 0
src/ui/components/FaImageUpload/index.vue

@@ -127,6 +127,7 @@ function onSelectFile(e: Event) {
       console.log(response.data)
       if(response.data.code === 0){
         const url = response.data.data
+        console.log(url,222)
         // const url = await props.afterUpload?.(response.data.data)
         if (url) {
           images.value.push(url)

+ 40 - 53
src/views/role-management/components/EditForm.vue

@@ -6,20 +6,21 @@ import VoiceSelector from '@/components/VoiceSelector.vue'
 import LangSelector from '@/components/LangSelector.vue'
 import NationalitySelector from '@/components/NationalitySelector.vue'
 import { saveRole } from '@/api/modules/anycallService'
-import type { TRole } from '@/api/modules/anycallService'
+import type { TSaveRoleParams } from '@/api/modules/anycallService'
 // 定义表单数据类型
 type IFormData  = {
+  id?: string
   name: string
-  avatar: string[]
   photo: string
   prompt: string
   language: string
+  languageCode: string
+  area: string
+  areaCode: string
   gender: number
   description: string
-  clonedVoice: boolean
-  voice: string
+  voiceId: string
   voiceName: string
-  nationality?: string
 }
 
 // 定义组件的属性
@@ -43,11 +44,12 @@ const props = withDefaults(defineProps<Props>(), {
   mode: 'create'
 })
 
+const avatars = ref<string[]>([])
+
 const emit = defineEmits<Emits>()
 
 // 创建本地响应式变量用于 v-model 绑定
 const dialogVisible = ref(props.visible)
-const dialogCardSelectorVisible = ref(false)
 
 // 监听 props.visible 变化,更新本地变量
 watch(() => props.visible, (newVisible) => {
@@ -65,15 +67,17 @@ const editFormRef = ref<FormInstance>();
 const currentSelected = ref<any>(null)
 
 // 响应式数据 - 直接定义所有必需字段
-const formData = ref<Partial<IFormData>>({
+const formData = ref<Partial<IFormData> & {voiceId: string, voiceName: string}>({
   name: '',
-  avatar: [],
   photo: '',
   prompt: '',
   language: '',
+  languageCode: '',
+  area: '',
+  areaCode: '',
   description: '',
   gender: 1,
-  voice: '',
+  voiceId: '',
   voiceName: '',
 });
 
@@ -86,6 +90,7 @@ watch(() => props.modelValue, (newModelValue) => {
       ...formData.value,
       ...newModelValue
     };
+    avatars.value = newModelValue.photo ? [newModelValue.photo] : []
   }
 });
 
@@ -120,17 +125,19 @@ function resetForm() {
   if (editFormRef.value) {
     editFormRef.value.resetFields()
   }
+  avatars.value  = [],
   formData.value = {
-    avatar: [],
     photo: '',
     name: '',
     prompt: '',
     description: '',
     language: '',
+    languageCode: '',
+    area: '',
+    areaCode: '',
     gender: 1,
-    voice: '',
+    voiceId: '',
     voiceName: '',
-    nationality: ''
   }
 }
 
@@ -141,23 +148,23 @@ async function handleConfirm() {
   try {
     await editFormRef.value.validate()
     const defaultData = {
-      avatar: "",
-      clonedVoice: false,
-      clonedVoiceFileName: "",
-      description: "",
-      gender: 1,
-      isCloning: false,
-      language: "US",
-      name: "",
-      prompt: "",
-      voice: '',
-      voiceFileName: "",
-      voiceId: "",
+      // Matches TSaveRoleParams
+      id: formData.value.id,
+      name: formData.value.name ?? '',
+      voiceId: formData.value.voiceId ?? '',
+      language: formData.value.language ?? '',
+      languageCode: formData.value.languageCode ?? '',
+      area: formData.value.area ?? '',
+      areaCode: formData.value.areaCode ?? '',
+      description: formData.value.description ?? '',
+      prompt: formData.value.prompt ?? '',
+      photo: '',
+      gender: formData.value.gender ?? 1,
     }
-    const avatar =  formData.value.avatar?.[0] ?? ''
+    const avatar =  avatars.value?.[0] ?? ''
     // 字段全后再对齐字段
     //@ts-ignore
-    const d: TRole = { ...defaultData, ...formData.value, avatar , photo: avatar }
+    const d: TSaveRoleParams = { ...defaultData, photo: avatar }
     console.log(d)
     const { code } = await saveRole(d)
     if (code === 0) {
@@ -220,45 +227,25 @@ const handleRemoveAgent = ()=> {
            <ElInput type="textarea" v-model="formData.description" placeholder="Enter description"  />
         </ElFormItem>
 
-        <!-- <ElFormItem label="use voice" prop="voiceName" label-width="120">
-           <LLMSelector v-model="formData.voiceId" />
-        </ElFormItem> -->
-
-        <ElFormItem label="use voice" prop="voice" label-width="120">
-           <VoiceSelector :gender="formData.gender ?? 1" v-model="formData.voice" v-model:voice-name="formData.voiceName" />
+        <ElFormItem label="音色" prop="voice" label-width="120">
+           <VoiceSelector :gender="formData.gender ?? 1" v-model="formData.voiceId" v-model:voice-name="formData.voiceName" />
         </ElFormItem>
 
-        <ElFormItem label="Avatar" prop="avatar" label-width="120">
+        <ElFormItem label="Avatar" prop="photo" label-width="120">
           <FaImageUpload
-            v-model="formData.avatar"
+            v-model="avatars"
             :max-count="1"
             list-type="avatar"
           />
         </ElFormItem>
 
-        <!-- <ElFormItem label="大模型" prop="agentId" label-width="120">
-          <ElSpace wrap>
-            <ElButton v-if="mode === 'create'" type="primary" @click="dialogCardSelectorVisible = true">选择大模型</ElButton>
-          </ElSpace>
-        </ElFormItem> -->
-        <ElFormItem label="国籍" prop="nationality">
+        <ElFormItem label="区域" prop="区域">
 
-          <NationalitySelector v-model="formData.nationality" placeholder="请选择国籍"></NationalitySelector>
+          <NationalitySelector v-model="formData.areaCode" v-model:key="formData.area" placeholder="区域"></NationalitySelector>
         </ElFormItem>
 
         <ElFormItem label="语言" prop="language">
-          <LangSelector v-model="formData.language" placeholder="请选择语言"></LangSelector>
-        </ElFormItem>
-
-        <ElFormItem label="clonedVoice" prop="clonedVoice">
-          <ElSpace>
-            <ElSwitch
-              v-model="formData.clonedVoice"
-              :active-value="true"
-              :inactive-value="false"
-            />
-            {{formData.clonedVoice === true ? 'true' : 'false'}}
-          </ElSpace>
+          <LangSelector v-model="formData.languageCode" v-model:key="formData.language" placeholder="请选择语言"></LangSelector>
         </ElFormItem>
 
       </ElForm>

+ 49 - 30
src/views/role-management/index.vue

@@ -13,8 +13,8 @@ import EditLLMForm from './components/EditLLMForm.vue'
 import EditCallingsForm from './components/EditCallingsForm.vue'
 import SearchForm from './components/SearchForm.vue'
 
-import type { TAgent } from '@/types/role'
-import { anycallPage, updateTopFlag, type TRole } from '@/api/modules/anycallService'
+import type { TAgent, TAgentDetail } from '@/types/role'
+import { anycallPage, deleteClone, updateTopFlag, type TRole } from '@/api/modules/anycallService'
 import { formatDateGeneral } from '@/utils'
 import { toast } from 'vue-sonner'
 
@@ -134,11 +134,10 @@ const handleCreate = () => {
   currentData.value = null
   editFormVisible.value = true
 }
-const handleEdit = (data: TAgent) => {
+const handleEdit = async (data: TAgentDetail) => {
   editMode.value = 'edit'
-  const gender = data.gender !== 'null' ? Number(data.gender) : 1;
-  currentData.value = {...data, avatar: [data.photo], gender}
-  console.log(currentData.value,33344)
+  const gender = !!data.gender ? data.gender : 1;
+  currentData.value = {...data, avatar: [data.photo], gender, voiceName: data.simpleVoice.name, voiceId: data.simpleVoice.id}
   editFormVisible.value = true
 }
 const handleEditLLM = (data: TAgent) => {
@@ -166,6 +165,16 @@ const handleReset = () => {
   fetchData()
 }
 
+const handleDelete = async (cloneId:string) => {
+  const {code} = await deleteClone({
+    cloneId,
+  })
+  if(code === 0){
+    toast.success('删除成功')
+    fetchData()
+  }
+}
+
 onMounted(async () => {
   await fetchData()
 })
@@ -220,32 +229,42 @@ onMounted(async () => {
             <ElSwitch v-model="row.topFlag" @change="()=> handleRecommend(row.id, row.topFlag)" />
           </template>
         </ElTableColumn>
-        <ElTableColumn label="Language" prop="language" min-width="180" />
-        <ElTableColumn label="voiceName" prop="voiceName" min-width="240" />
+        <ElTableColumn label="Language" prop="language" min-width="180">
+          <template #default="{row}">
+            {{ row.languageCode }} - {{ row.language }}
+          </template>
+        </ElTableColumn>
+        <ElTableColumn label="voiceName" prop="simpleVoice" min-width="240">
+          <template #default="{row}">
+            {{ row?.simpleVoice?.name }}
+          </template>
+        </ElTableColumn>
         <ElTableColumn fixed="right" label="操作" min-width="140">
           <template #default="{row}">
-            <el-dropdown>
-              <span class="el-dropdown-link">
-                Actions
-                <el-icon class="el-icon--right">
-                  <ArrowDown />
-                </el-icon>
-              </span>
-              <template #dropdown>
-                <el-dropdown-menu>
-                  <el-dropdown-item><ElButton link type="primary" size="small" @click="handleEdit(row)">编辑</ElButton></el-dropdown-item>
-                  <el-dropdown-item><ElButton link type="primary" size="small" @click="handleEditLLM(row)">绑定大模型</ElButton></el-dropdown-item>
-                  <el-dropdown-item><ElButton link type="primary" size="small" @click="handleEditCallings(row)">修改 callings</ElButton></el-dropdown-item>
-                </el-dropdown-menu>
-              </template>
-            </el-dropdown>
-            <!-- <ElPopconfirm title="确定推荐吗?" @confirm="handleRecommend(row.id, row.topFlag)">
-              <template #reference>
-                <ElButton link type="primary" size="small">
-                推荐
-                </ElButton>
-              </template>
-            </ElPopconfirm> -->
+            <ElSpace>
+              <el-dropdown>
+                <span class="el-dropdown-link">
+                  Actions
+                  <el-icon class="el-icon--right">
+                    <ArrowDown />
+                  </el-icon>
+                </span>
+                <template #dropdown>
+                  <el-dropdown-menu>
+                    <el-dropdown-item><ElButton link type="primary" size="small" @click="handleEdit(row)">编辑</ElButton></el-dropdown-item>
+                    <el-dropdown-item><ElButton link type="primary" size="small" @click="handleEditLLM(row)">绑定大模型</ElButton></el-dropdown-item>
+                    <el-dropdown-item><ElButton link type="primary" size="small" @click="handleEditCallings(row)">修改 callings</ElButton></el-dropdown-item>
+                  </el-dropdown-menu>
+                </template>
+              </el-dropdown>
+              <ElPopconfirm title="确定删除吗?" @confirm="handleDelete(row.id)">
+                <template #reference>
+                  <ElButton link type="primary" size="small">
+                  删除
+                  </ElButton>
+                </template>
+              </ElPopconfirm>
+            </ElSpace>
           </template>
         </ElTableColumn>
       </ElTable>