lmx 7 saat önce
ebeveyn
işleme
31fc37765c

+ 7 - 0
src/api/company/aiModel.js

@@ -40,3 +40,10 @@ export function copy(id) {
         method: 'get'
     })
 }
+
+export function getCidConfig() {
+    return request({
+        url: '/aicall/account/getCidConfig',
+        method: 'get'
+    })
+}

+ 27 - 0
src/api/company/voiceClone.js

@@ -0,0 +1,27 @@
+import request from '@/utils/request'
+
+/**
+ * 上传音频文件并训练声音克隆音色
+ * @param {FormData} formData 包含 file / voice_name / speaker_id / language / model_type
+ */
+export function uploadAndTrain(formData) {
+  return request({
+    url: '/company/voiceClone/uploadAndTrain',
+    method: 'post',
+    data: formData,
+    headers: { 'Content-Type': 'multipart/form-data' }
+  })
+}
+
+/**
+ * TTS 语音合成测试
+ * @param {FormData} formData 包含 speakerId / language / text
+ */
+export function doubaoTtsTest(formData) {
+  return request({
+    url: '/company/voiceClone/doubaoTtsTest',
+    method: 'post',
+    data: formData,
+    headers: { 'Content-Type': 'multipart/form-data' }
+  })
+}

+ 84 - 12
src/views/company/aiModel/account/info.vue

@@ -34,6 +34,7 @@
                     v-model="form.concurrentNum"
                     placeholder="请输入并发数"
                     @input="handleConcurrentNumInput"
+                    :disabled="true"
                 />
             </el-form-item>
 
@@ -50,6 +51,7 @@
                             v-if="field.type === 'input'"
                             v-model="form.accountJson[field.name]"
                             :placeholder="'请输入' + field.label"
+                            :disabled="!!field.disableProp"
                         />
 
                         <!-- 文本域 -->
@@ -59,6 +61,7 @@
                             type="textarea"
                             :rows="field.rows || 3"
                             :placeholder="'请输入' + field.label"
+                             :disabled="!!field.disableProp"
                         />
 
                         <!-- 下拉框 -->
@@ -70,6 +73,7 @@
                             @change="(val) => handleSelectChange(field.name, val)"
                             filterable
                             clearable
+                             :disabled="!!field.disableProp"
                         >
                             <el-option
                                 v-for="option in field.options"
@@ -86,6 +90,7 @@
                             type="textarea"
                             :rows="30"
                             :placeholder="'请输入' + field.label"
+                             :disabled="!!field.disableProp"
                         />
                     </el-form-item>
 
@@ -197,7 +202,7 @@
 </template>
 
 <script>
-import { add, update } from '@/api/company/aiModel'  // 假设有 update 接口
+import { add, update, getCidConfig } from '@/api/company/aiModel'  // 假设有 update 接口
 import { all } from '@/api/company/aiCall'
 
 export default {
@@ -234,28 +239,69 @@ export default {
             // 动态字段配置
             dynamicFields: [],
             // 是否显示客户意向提示词
-            showIntentionTips: false
+            showIntentionTips: false,
+            cidConf:{}
         }
     },
     computed: {
         // 表单验证规则
         rules() {
-            return {
+            const baseRules = {
                 name: [
-                    { required: true, message: '请输入账户名称', trigger: 'blur' }
+                    { required: true, message: '请输入模型名称', trigger: 'blur' }
                 ],
                 providerClassName: [
                     { required: true, message: '请选择实现类', trigger: 'change' }
                 ],
                 concurrentNum: [
-                    { required: true, message: '请输入并发数', trigger: 'blur' },
+                    { required: true, message: '请输入模型并发数', trigger: 'blur' },
                     { pattern: /^\d+$/, message: '请输入数字', trigger: 'blur' },
                     { validator: this.validateConcurrentNum, trigger: 'blur' }
                 ],
                 transferManualDigit: [
                     { pattern: /^[0-9]?$/, message: '请输入单个数字0-9', trigger: 'blur' }
+                ],
+                interruptFlag: [
+                    { required: true, message: '请选择打断开关', trigger: 'change' }
+                ]
+            }
+
+            // 动态字段验证规则
+            this.dynamicFields.forEach(field => {
+                if (field.required) {
+                    const prop = 'accountJson.' + field.name
+                    const isSelect = field.type === 'select'
+                    baseRules[prop] = [
+                        {
+                            required: true,
+                            message: isSelect ? `请选择${field.label}` : `请输入${field.label}`,
+                            trigger: isSelect ? 'change' : 'blur'
+                        }
+                    ]
+                }
+            })
+
+            // Coze OAuth 相关字段验证
+            if (this.form.accountJson.tokenType === 'oauth') {
+                baseRules['accountJson.oauthClientId'] = [
+                    { required: true, message: '请输入Client ID', trigger: 'blur' }
+                ]
+                baseRules['accountJson.oauthPrivateKey'] = [
+                    { required: true, message: '请输入Private Key', trigger: 'blur' }
+                ]
+                baseRules['accountJson.oauthPublicKeyId'] = [
+                    { required: true, message: '请输入Public Key ID', trigger: 'blur' }
+                ]
+            }
+
+            // Coze PAT Token 验证
+            if (this.form.accountJson.tokenType === 'pat') {
+                baseRules['accountJson.patToken'] = [
+                    { required: true, message: '请输入PAT Token', trigger: 'blur' }
                 ]
             }
+
+            return baseRules
         }
     },
     watch: {
@@ -283,9 +329,16 @@ export default {
         }
     },
     created() {
-        this.loadKbCatOptions()
+        this.loadKbCatOptions();
+        this.initCidConfData();
     },
     methods: {
+        initCidConfData(){
+            getCidConfig().then(res=>{
+            console.log(JSON.parse(res.data));
+            this.cidConf = JSON.parse(res.data);
+        }).catch(res=>{})
+        },
         // 初始化表单数据
         initFormData(data) {
             this.form.id = data.id
@@ -297,7 +350,6 @@ export default {
             this.form.interruptIgnoreKeywords = data.interruptIgnoreKeywords || ''
             this.form.transferManualDigit = data.transferManualDigit || ''
             this.form.intentionTips = data.intentionTips || ''
-
             // 解析accountJson
             try {
                 this.form.accountJson = data.accountJson ?
@@ -310,7 +362,7 @@ export default {
 
             // 根据providerClassName更新动态字段
             if (this.form.providerClassName) {
-                this.updateDynamicFields(this.form.providerClassName)
+                this.updateDynamicFields(this.form.providerClassName,true)
             }
         },
 
@@ -349,7 +401,7 @@ export default {
         },
 
         // 更新动态字段
-        updateDynamicFields(providerClassName) {
+        updateDynamicFields(providerClassName,isUpdate) {
             this.dynamicFields = []
             this.showIntentionTips = false
 
@@ -358,9 +410,9 @@ export default {
 
             if (['DeepSeekChat', 'ChatGpt4o', 'JiutianChat'].includes(providerClassName)) {
                 this.dynamicFields = [
-                    { name: 'serverUrl', label: '服务地址', type: 'input', required: true },
-                    { name: 'apiKey', label: 'apiKey', type: 'input', required: true },
-                    { name: 'modelName', label: '模型名称', type: 'input', required: true },
+                    { name: 'serverUrl', label: '服务地址', type: 'input', required: true, disableProp:true  },
+                    { name: 'apiKey', label: 'apiKey', type: 'input', required: true, disableProp:true  },
+                    { name: 'modelName', label: '模型名称', type: 'input', required: true, disableProp:true  },
                     { name: 'llmTips', label: '大模型提示词', type: 'large-textarea', required: true },
                     { name: 'faqContext', label: 'FAQ上下文', type: 'large-textarea', required: true },
                     { name: 'kbCatId', label: '知识库分类', type: 'select', required: false, options: this.kbCatOptions },
@@ -448,6 +500,26 @@ export default {
             this.form.accountJson = {
                 ...newAccountJson,
                 ...this.form.accountJson
+            }   
+
+            //默认跟上配置值
+            if('DeepSeekChat' === providerClassName && !!this.cidConf && !!!isUpdate){
+                this.$set(this.form.accountJson,"serverUrl",this.cidConf.serverAddress);
+                this.$set(this.form.accountJson,"apiKey",this.cidConf.apiKey);
+                this.$set(this.form.accountJson,"modelName",this.cidConf.modelName);
+                this.$set(this.form,"concurrentNum",this.cidConf.concurrency);
+                 this.dynamicFields = [
+                    { name: 'serverUrl', label: '服务地址', type: 'input', required: true, disableProp:true },
+                    { name: 'apiKey', label: 'apiKey', type: 'input', required: true, disableProp:true },
+                    { name: 'modelName', label: '模型名称', type: 'input', required: true, disableProp:true },
+                    { name: 'llmTips', label: '大模型提示词', type: 'large-textarea', required: true },
+                    { name: 'faqContext', label: 'FAQ上下文', type: 'large-textarea', required: true },
+                    { name: 'kbCatId', label: '知识库分类', type: 'select', required: false, options: this.kbCatOptions },
+                    { name: 'transferToAgentTips', label: '转人工提示词', type: 'textarea', rows: 3, required: true },
+                    { name: 'hangupTips', label: '挂机提示', type: 'textarea', rows: 3, required: true },
+                    { name: 'customerNoVoiceTips', label: '客户不说话提示', type: 'textarea', rows: 3, required: true },
+                    { name: 'openingRemarks', label: '开场白', type: 'textarea', rows: 3, required: true }
+                ]
             }
         },
 

+ 7 - 3
src/views/company/aiModel/index.vue

@@ -76,8 +76,8 @@
         <!-- 数据表格 -->
         <el-table v-loading="loading" :data="modelList" border @selection-change="handleSelectionChange">
             <el-table-column type="selection" width="55" align="center" />
-            <el-table-column align="center" label="模型名称" prop="name" width="400" />
-            <el-table-column align="center" label="实现类" prop="providerClassName" width="400" />
+            <el-table-column align="center" label="模型名称" prop="name"  />
+            <el-table-column align="center" label="实现类" prop="providerClassName" />
             <el-table-column
                 align="center"
                 class-name="small-padding fixed-width"
@@ -227,7 +227,11 @@ export default {
         /** 获取所有实现类 */
         getAll() {
             all().then((response) => {
-                this.providerClassNameList = response.data || []
+                //隐藏其他几种大模型
+                if(!!response.data){
+                    response.data =  response.data.filter(a => a.providerClassName == "DeepSeekChat")
+                }
+                this.providerClassNameList = response.data || [];
             })
         },
         /** 新增按钮 */

+ 586 - 0
src/views/company/aiModel/voiceClone/index.vue

@@ -0,0 +1,586 @@
+<template>
+  <div class="app-container">
+    <!-- 注意事项 -->
+    <el-card class="tips-card" shadow="hover">
+      <div class="tips-header">
+        <i class="el-icon-warning-outline tips-icon"></i>
+        <span class="tips-title">语音克隆注意事项</span>
+      </div>
+      <el-row :gutter="32" class="tips-content">
+        <el-col :span="12">
+          <div class="tip-item">
+            <div class="tip-num">1</div>
+            <div class="tip-text">
+              <div class="tip-label">音色限制</div>
+              <div class="tip-desc">
+                <div class="tip-warning-box">
+                  <i class="el-icon-info"></i>
+                  <span>每个音色最多只能上传并训练 <strong>10</strong> 次</span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </el-col>
+        <el-col :span="12">
+          <div class="tip-item">
+            <div class="tip-num">2</div>
+            <div class="tip-text">
+              <div class="tip-label">录制要求</div>
+              <div class="tip-desc">
+                预先录制 <strong>20~30秒</strong> 左右的声音,确保环境安静且无噪音
+              </div>
+            </div>
+          </div>
+          <div class="tip-item tip-sub">
+            <div class="tip-sub-label">推荐录制方式:</div>
+            <div class="tip-sub-list">
+              <span class="tip-tag"><i class="el-icon-monitor"></i> 电脑+耳机,使用自带录音机</span>
+              <span class="tip-tag"><i class="el-icon-mobile-phone"></i> 手机录音后传到电脑</span>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </el-card>
+
+    <!-- 声音克隆表单 -->
+    <el-card class="box-card" style="margin-bottom: 16px;">
+      <div slot="header"><span>声音克隆配置</span></div>
+      <el-form ref="cloneForm" :model="cloneForm" :rules="cloneRules" label-width="120px">
+        <el-form-item label="音色名称" prop="voice_name">
+          <el-input
+            v-model="cloneForm.voice_name"
+            placeholder="请输入音色名称"
+            style="width: 320px;"
+          />
+        </el-form-item>
+
+        <el-form-item label="声音ID" prop="speaker_id">
+          <el-input
+            v-model="cloneForm.speaker_id"
+            placeholder="请输入声音ID"
+            style="width: 320px;"
+          />
+        </el-form-item>
+
+        <el-form-item label="模型类型" prop="model_type">
+          <el-select
+            v-model="cloneForm.model_type"
+            placeholder="请选择模型类型"
+            style="width: 400px;"
+            @change="onModelTypeChange"
+          >
+            <el-option :value="1" label="声音复刻ICL1.0效果" />
+            <!-- <el-option :value="2" label="DiT标准版效果(音色、不还原用户的风格)" />
+            <el-option :value="3" label="DiT还原版效果(音色、还原用户口音、语速等风格)" /> -->
+            <el-option :value="4" label="声音复刻ICL2.0效果" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="语言" prop="language">
+          <el-select
+            v-model="cloneForm.language"
+            placeholder="请选择语言"
+            style="width: 240px;"
+          >
+            <el-option
+              v-for="item in languageOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="音频文件">
+          <el-upload
+            ref="audioUpload"
+            action="#"
+            :auto-upload="false"
+            :on-change="onAudioFileChange"
+            :on-remove="onAudioFileRemove"
+            :file-list="audioFileList"
+            accept=".wav,.mp3,.ogg,.m4a,.aac"
+            :limit="1"
+            :on-exceed="onFileExceed"
+          >
+            <el-button size="small" type="primary" icon="el-icon-upload">
+              选择音频文件
+            </el-button>
+            <div slot="tip" class="el-upload__tip" style="color: #909399;">
+              支持 wav、mp3、ogg、m4a、aac 格式,推荐 20-30 秒录音
+            </div>
+          </el-upload>
+        </el-form-item>
+
+        <el-form-item>
+          <el-button
+            type="success"
+            icon="el-icon-upload2"
+            :loading="uploadLoading"
+            @click="handleUploadAndTrain"
+          >
+            {{ uploadLoading ? '上传训练中,约15-30秒...' : '上传并训练' }}
+          </el-button>
+          <el-button
+            type="primary"
+            icon="el-icon-headset"
+            plain
+            @click="handleOpenTestPanel"
+          >
+            测试已有声音
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+
+    <!-- TTS测试区域(上传训练成功后 或 点击测试按钮后显示) -->
+    <el-card v-if="showTtsArea" class="box-card">
+      <div slot="header" class="tts-card-header">
+        <span>TTS语音合成测试</span>
+        <el-button v-if="isManualTest" type="text" icon="el-icon-close" @click="showTtsArea = false">关闭</el-button>
+      </div>
+      <el-form ref="ttsForm" :model="ttsForm" label-width="120px">
+        <!-- 手动测试时需要输入声音ID -->
+        <el-form-item v-if="isManualTest" label="声音ID" prop="test_speaker_id">
+          <el-input
+            v-model="ttsForm.test_speaker_id"
+            placeholder="请输入要测试的声音ID"
+            style="width: 320px;"
+          />
+        </el-form-item>
+
+        <el-form-item label="测试文本" prop="tts_text">
+          <el-input
+            v-model="ttsForm.tts_text"
+            type="textarea"
+            :rows="5"
+            placeholder="请输入测试语音合成的文本"
+            style="width: 480px;"
+          />
+        </el-form-item>
+
+        <el-form-item label="语言" prop="language_test">
+          <el-select
+            v-model="ttsForm.language_test"
+            placeholder="请选择语言"
+            style="width: 240px;"
+          >
+            <el-option
+              v-for="item in ttsLanguageOptions"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+          <el-button
+            type="primary"
+            icon="el-icon-video-play"
+            style="margin-left: 12px;"
+            :loading="ttsLoading"
+            :disabled="isManualTest && !ttsForm.test_speaker_id"
+            @click="handleTtsTest"
+          >
+            {{ ttsLoading ? '合成中...' : '开始合成' }}
+          </el-button>
+        </el-form-item>
+
+        <el-form-item v-if="audioSrc" label="播放">
+          <audio ref="audioPlayer" controls :src="audioSrc" style="margin-top: 4px;" />
+        </el-form-item>
+      </el-form>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import { uploadAndTrain, doubaoTtsTest } from '@/api/company/voiceClone'
+
+export default {
+  name: 'VoiceClone',
+  data() {
+    return {
+      // 声音克隆表单
+      cloneForm: {
+        voice_name: '',
+        speaker_id: '',
+        model_type: null,
+        language: 0
+      },
+      cloneRules: {
+        voice_name: [{ required: true, message: '请输入音色名称', trigger: 'blur' }],
+        speaker_id: [{ required: true, message: '请输入声音ID', trigger: 'blur' }],
+        model_type: [{ required: true, message: '请选择模型类型', trigger: 'change' }],
+        language: [{ required: true, message: '请选择语言', trigger: 'change' }]
+      },
+
+      // 音频文件列表
+      audioFileList: [],
+      selectedFile: null,
+
+      // 上传训练loading
+      uploadLoading: false,
+
+      // 是否显示TTS测试区域
+      showTtsArea: false,
+
+      // 是否为手动测试模式(区别于上传训练成功后的自动显示)
+      isManualTest: false,
+
+      // TTS测试表单
+      ttsForm: {
+        tts_text: '',
+        language_test: 0,
+        test_speaker_id: '' // 手动测试时的声音ID
+      },
+
+      // TTS testing loading
+      ttsLoading: false,
+
+      // 播放音频src(base64)
+      audioSrc: ''
+    }
+  },
+
+  computed: {
+    // 根据模型类型动态计算语言选项
+    languageOptions() {
+      const modelType = this.cloneForm.model_type
+      const options = [
+        { value: 0, label: '中文' },
+        { value: 1, label: '英文' }
+      ]
+      if (modelType <= 2) {
+        options.push(
+          { value: 2, label: '日语' },
+          { value: 3, label: '西班牙语' },
+          { value: 4, label: '印尼语' },
+          { value: 5, label: '葡萄牙语' }
+        )
+      }
+      if (modelType === 2) {
+        options.push(
+          { value: 6, label: '德语' },
+          { value: 7, label: '法语' }
+        )
+      }
+      return options
+    },
+    // TTS测试语言选项(手动测试时显示全部语言)
+    ttsLanguageOptions() {
+      return [
+        { value: 0, label: '中文' },
+        { value: 1, label: '英文' },
+        { value: 2, label: '日语' },
+        { value: 3, label: '西班牙语' },
+        { value: 4, label: '印尼语' },
+        { value: 5, label: '葡萄牙语' },
+        { value: 6, label: '德语' },
+        { value: 7, label: '法语' }
+      ]
+    }
+  },
+
+  methods: {
+    // 模型类型变更,重置语言为中文
+    onModelTypeChange() {
+      const validValues = this.languageOptions.map(o => o.value)
+      if (!validValues.includes(this.cloneForm.language)) {
+        this.cloneForm.language = 0
+      }
+      if (!validValues.includes(this.ttsForm.language_test)) {
+        this.ttsForm.language_test = 0
+      }
+    },
+
+    // 音频文件选择
+    onAudioFileChange(file) {
+      this.selectedFile = file.raw
+      this.audioFileList = [file]
+    },
+
+    // 音频文件移除
+    onAudioFileRemove() {
+      this.selectedFile = null
+      this.audioFileList = []
+    },
+
+    // 超出文件数量限制
+    onFileExceed() {
+      this.$message.warning('每次只能上传一个音频文件,请先移除已选文件')
+    },
+
+    // 打开手动测试面板
+    handleOpenTestPanel() {
+      this.isManualTest = true
+      this.showTtsArea = true
+      this.ttsForm.test_speaker_id = ''
+      this.ttsForm.tts_text = ''
+      this.ttsForm.language_test = 0
+      this.audioSrc = ''
+    },
+
+    // 上传并训练
+    handleUploadAndTrain() {
+      this.$refs.cloneForm.validate(valid => {
+        if (!valid) return
+        if (!this.selectedFile) {
+          this.$message.error('请选择音频文件')
+          return
+        }
+        this.uploadLoading = true
+
+        const formData = new FormData()
+        formData.append('file', this.selectedFile)
+        formData.append('voice_name', this.cloneForm.voice_name)
+        formData.append('speaker_id', this.cloneForm.speaker_id)
+        formData.append('language', this.cloneForm.language)
+        formData.append('model_type', this.cloneForm.model_type)
+
+        uploadAndTrain(formData).then(res => {
+          if (res.code === 200) {
+            this.$message.success(res.msg || '上传训练成功!')
+            this.isManualTest = false // 上传训练成功后的测试模式
+            this.showTtsArea = true
+          } else {
+            this.$message.error(res.msg || '上传训练失败,请重试')
+          }
+        }).catch(() => {
+          this.$message.error('上传训练失败,请重试')
+        }).finally(() => {
+          this.uploadLoading = false
+        })
+      })
+    },
+
+    // TTS测试
+    handleTtsTest() {
+      if (this.ttsLoading) {
+        this.$message.warning('请等待合成完成!')
+        return
+      }
+      if (!this.ttsForm.tts_text || this.ttsForm.tts_text.trim() === '') {
+        this.$message.error('请输入测试文本')
+        return
+      }
+
+      // 获取声音ID:手动测试用输入框的值,自动测试用表单的值
+      const speakerId = this.isManualTest ? this.ttsForm.test_speaker_id : this.cloneForm.speaker_id
+      if (!speakerId || speakerId.trim() === '') {
+        this.$message.error('请输入声音ID')
+        return
+      }
+
+      this.ttsLoading = true
+
+      const formData = new FormData()
+      formData.append('speakerId', speakerId)
+      formData.append('language', this.ttsForm.language_test)
+      formData.append('text', this.ttsForm.tts_text)
+
+      doubaoTtsTest(formData).then(res => {
+        if (res.code === 200) {
+          this.$message.success('TTS合成成功!')
+          // res.data 是 JSON 字符串 {"code":3000,"data":"...base64..."}
+          try {
+            const audioJson = JSON.parse(res.data)
+            if (audioJson.code === 3000 && audioJson.data) {
+              this.audioSrc = 'data:audio/mp3;base64,' + audioJson.data
+              this.$nextTick(() => {
+                this.$refs.audioPlayer && this.$refs.audioPlayer.play()
+              })
+            }
+          } catch (e) {
+            this.$message.warning('音频解析失败')
+          }
+        } else {
+          this.$message.error(res.msg || 'TTS测试失败,请重试')
+        }
+      }).catch(() => {
+        this.$message.error('TTS测试失败,请重试')
+      }).finally(() => {
+        this.ttsLoading = false
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.box-card {
+  margin-bottom: 16px;
+}
+
+/* TTS测试卡片头部 */
+.tts-card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+/* 注意事项卡片样式 */
+.tips-card {
+  margin-bottom: 20px;
+  border-left: 4px solid #409EFF;
+}
+
+.tips-card /deep/ .el-card__body {
+  padding: 16px 20px;
+}
+
+.tips-header {
+  display: flex;
+  align-items: center;
+  margin-bottom: 16px;
+  padding-bottom: 12px;
+  border-bottom: 1px dashed #EBEEF5;
+}
+
+.tips-icon {
+  font-size: 22px;
+  color: #409EFF;
+  margin-right: 10px;
+}
+
+.tips-title {
+  font-size: 16px;
+  font-weight: 600;
+  color: #303133;
+}
+
+.tips-content {
+  margin: 0;
+}
+
+.tip-item {
+  display: flex;
+  align-items: flex-start;
+  margin-bottom: 12px;
+}
+
+.tip-item:last-child {
+  margin-bottom: 0;
+}
+
+.tip-num {
+  width: 24px;
+  height: 24px;
+  background: linear-gradient(135deg, #409EFF 0%, #66b1ff 100%);
+  border-radius: 50%;
+  color: #fff;
+  font-size: 13px;
+  font-weight: bold;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 12px;
+  flex-shrink: 0;
+}
+
+.tip-text {
+  flex: 1;
+}
+
+.tip-label {
+  font-size: 14px;
+  font-weight: 600;
+  color: #303133;
+  margin-bottom: 4px;
+}
+
+.tip-desc {
+  font-size: 13px;
+  color: #606266;
+  line-height: 1.6;
+}
+
+.tip-desc strong {
+  color: #409EFF;
+}
+
+.link-btn {
+  display: inline-flex;
+  align-items: center;
+  padding: 2px 8px;
+  background: #ecf5ff;
+  border-radius: 4px;
+  color: #409EFF;
+  font-size: 12px;
+  margin: 0 4px;
+  transition: all 0.3s;
+}
+
+.link-btn:hover {
+  background: #409EFF;
+  color: #fff;
+}
+
+.link-btn i {
+  margin-left: 2px;
+}
+
+.tip-warning {
+  display: inline-block;
+  margin-left: 4px;
+  padding: 1px 6px;
+  background: #fef0f0;
+  border-radius: 3px;
+  color: #f56c6c;
+  font-size: 12px;
+}
+
+.tip-warning-box {
+  display: inline-flex;
+  align-items: center;
+  padding: 8px 14px;
+  background: linear-gradient(135deg, #fff6f6 0%, #fef0f0 100%);
+  border: 1px solid #fbc4c4;
+  border-radius: 6px;
+  color: #f56c6c;
+  font-size: 13px;
+}
+
+.tip-warning-box i {
+  margin-right: 8px;
+  font-size: 16px;
+}
+
+.tip-warning-box strong {
+  color: #f56c6c;
+  font-size: 16px;
+  margin: 0 2px;
+}
+
+.tip-sub {
+  margin-top: 12px;
+  padding-left: 36px;
+}
+
+.tip-sub-label {
+  font-size: 13px;
+  color: #909399;
+  margin-bottom: 8px;
+}
+
+.tip-sub-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+
+.tip-tag {
+  text-align: center;
+  line-height: 20px;
+  display: inline-flex;
+  align-items: center;
+  padding: 4px 10px;
+  background: #f4f4f5;
+  border-radius: 4px;
+  font-size: 12px;
+  color: #606266;
+}
+
+.tip-tag i {
+  margin-right: 4px;
+  color: #909399;
+}
+</style>

+ 15 - 15
src/views/company/companyVoiceRobotic/index.vue

@@ -756,9 +756,9 @@ import {
   companyUserList,
   wxList,
   taskRun,
-  getTypes,
+  // getTypes,
   getSmsTempList,
-  getCIDGroupList,
+  // getCIDGroupList,
   getExecRecords,
   getCurrentCompanyId
 } from "@/api/company/companyVoiceRobotic";
@@ -782,7 +782,7 @@ export default {
       currentCompanyId:null,
       // 遮罩层
       loading: true,
-      CIDGroupList:[],
+      // CIDGroupList:[],
       // 选中数组
       ids: [],
       weekList: [
@@ -932,10 +932,10 @@ export default {
     }).catch(res=>{
       console.log(res);
     })
-    getTypes().then(e => {
-      this.robotList = e.robot;
-      this.dialogList = e.dialog;
-    })
+    //getTypes().then(e => {
+      //this.robotList = e.robot;
+      //this.dialogList = e.dialog;
+    //})
     // listAll().then(e => {
     //   this.wxDialogList = e.data;
     // })
@@ -955,14 +955,14 @@ export default {
     this.getList();
     this.getSmsTempDropList();
 
-    getCIDGroupList().then(res=>{
-      console.log("----------------------")
-      console.log(res);
-      this.CIDGroupList = res.data;
-    }).catch(res=>{
-      console.log("catch_____+++++++")
-      console.log(res);
-    });
+    // getCIDGroupList().then(res=>{
+    //   console.log("----------------------")
+    //   console.log(res);
+    //   this.CIDGroupList = res.data;
+    // }).catch(res=>{
+    //   console.log("catch_____+++++++")
+    //   console.log(res);
+    // });
   },
   watch: {
     // 监听添加类型的切换,清空选择器数据