|
|
@@ -1,636 +1,325 @@
|
|
|
<template>
|
|
|
- <div class="app-container">
|
|
|
- <el-form
|
|
|
- ref="form"
|
|
|
- :model="form"
|
|
|
- :rules="rules"
|
|
|
- label-width="120px"
|
|
|
- class="account-form"
|
|
|
+ <div class="app-container outbound-config-form">
|
|
|
+ <el-form
|
|
|
+ ref="form"
|
|
|
+ :model="form"
|
|
|
+ :rules="rules"
|
|
|
+ label-width="120px"
|
|
|
+ class="account-form"
|
|
|
+ >
|
|
|
+ <el-form-item label="配置名称" prop="name">
|
|
|
+ <el-input v-model="form.name" placeholder="请输入外呼配置名称" />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-divider content-position="left">提示词与知识库</el-divider>
|
|
|
+
|
|
|
+ <el-form-item label="大模型提示词" prop="accountJson.llmTips">
|
|
|
+ <el-input
|
|
|
+ v-model="form.accountJson.llmTips"
|
|
|
+ type="textarea"
|
|
|
+ :rows="12"
|
|
|
+ placeholder="请输入外呼对话主提示词"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="FAQ上下文" prop="accountJson.faqContext">
|
|
|
+ <el-input
|
|
|
+ v-model="form.accountJson.faqContext"
|
|
|
+ type="textarea"
|
|
|
+ :rows="12"
|
|
|
+ placeholder="请输入 FAQ 上下文"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="知识库分类" prop="accountJson.kbCatId">
|
|
|
+ <el-select
|
|
|
+ v-model="form.accountJson.kbCatId"
|
|
|
+ placeholder="请选择知识库分类(可选)"
|
|
|
+ style="width: 100%"
|
|
|
+ clearable
|
|
|
+ filterable
|
|
|
>
|
|
|
- <el-form-item label="模型名称" prop="name" required>
|
|
|
- <el-input v-model="form.name" placeholder="请输入账模型名称" />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="实现类" prop="providerClassName" required>
|
|
|
- <el-select
|
|
|
- v-model="form.providerClassName"
|
|
|
- placeholder="请选择实现类"
|
|
|
- @change="handleProviderChange"
|
|
|
- style="width: 100%"
|
|
|
- filterable
|
|
|
- clearable
|
|
|
- >
|
|
|
- <el-option
|
|
|
- v-for="item in providerOptions"
|
|
|
- :key="item"
|
|
|
- :label="item"
|
|
|
- :value="item"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="模型并发数" prop="concurrentNum" required>
|
|
|
- <el-input
|
|
|
- v-model="form.concurrentNum"
|
|
|
- placeholder="请输入并发数"
|
|
|
- @input="handleConcurrentNumInput"
|
|
|
- :disabled="true"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <!-- 动态字段区域 -->
|
|
|
- <template v-if="dynamicFields.length > 0">
|
|
|
- <div v-for="field in dynamicFields" :key="field.name">
|
|
|
- <el-form-item
|
|
|
- :label="field.label"
|
|
|
- :prop="'accountJson.' + field.name"
|
|
|
- :required="field.required"
|
|
|
- >
|
|
|
- <!-- 文本框 -->
|
|
|
- <el-input
|
|
|
- v-if="field.type === 'input'"
|
|
|
- v-model="form.accountJson[field.name]"
|
|
|
- :placeholder="'请输入' + field.label"
|
|
|
- :disabled="!!field.disableProp"
|
|
|
- />
|
|
|
-
|
|
|
- <!-- 文本域 -->
|
|
|
- <el-input
|
|
|
- v-else-if="field.type === 'textarea'"
|
|
|
- v-model="form.accountJson[field.name]"
|
|
|
- type="textarea"
|
|
|
- :rows="field.rows || 3"
|
|
|
- :placeholder="'请输入' + field.label"
|
|
|
- :disabled="!!field.disableProp"
|
|
|
- />
|
|
|
-
|
|
|
- <!-- 下拉框 -->
|
|
|
- <el-select
|
|
|
- v-else-if="field.type === 'select'"
|
|
|
- v-model="form.accountJson[field.name]"
|
|
|
- :placeholder="'请选择' + field.label"
|
|
|
- style="width: 100%"
|
|
|
- @change="(val) => handleSelectChange(field.name, val)"
|
|
|
- filterable
|
|
|
- clearable
|
|
|
- :disabled="!!field.disableProp"
|
|
|
- >
|
|
|
- <el-option
|
|
|
- v-for="option in field.options"
|
|
|
- :key="option.value"
|
|
|
- :label="option.label"
|
|
|
- :value="option.value"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
-
|
|
|
- <!-- 大文本域(30行) -->
|
|
|
- <el-input
|
|
|
- v-else-if="field.type === 'large-textarea'"
|
|
|
- v-model="form.accountJson[field.name]"
|
|
|
- type="textarea"
|
|
|
- :rows="30"
|
|
|
- :placeholder="'请输入' + field.label"
|
|
|
- :disabled="!!field.disableProp"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <!-- Coze的token类型特殊字段 -->
|
|
|
- <template v-if="field.name === 'tokenType' && form.accountJson.tokenType">
|
|
|
- <el-form-item
|
|
|
- v-if="form.accountJson.tokenType === 'oauth'"
|
|
|
- label="OAuth配置"
|
|
|
- prop="accountJson.oauthFields"
|
|
|
- >
|
|
|
- <el-card class="oauth-card">
|
|
|
- <el-form-item label="Client ID" prop="accountJson.oauthClientId" required>
|
|
|
- <el-input v-model="form.accountJson.oauthClientId" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="Private Key" prop="accountJson.oauthPrivateKey" required>
|
|
|
- <el-input v-model="form.accountJson.oauthPrivateKey" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="Public Key ID" prop="accountJson.oauthPublicKeyId" required>
|
|
|
- <el-input v-model="form.accountJson.oauthPublicKeyId" />
|
|
|
- </el-form-item>
|
|
|
- </el-card>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item
|
|
|
- v-if="form.accountJson.tokenType === 'pat'"
|
|
|
- label="PAT Token"
|
|
|
- prop="accountJson.patToken"
|
|
|
- required
|
|
|
- >
|
|
|
- <el-input
|
|
|
- v-model="form.accountJson.patToken"
|
|
|
- type="textarea"
|
|
|
- :rows="3"
|
|
|
- placeholder="请输入PAT Token"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- </template>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
-
|
|
|
- <!-- 中断标志 -->
|
|
|
- <el-form-item label="打断开关" prop="interruptFlag" required>
|
|
|
- <el-select v-model="form.interruptFlag" placeholder="请选择中断标志" style="width: 100%" filterable clearable>
|
|
|
- <el-option label="不打断" :value="0" />
|
|
|
- <el-option label="关键词打断" :value="1" />
|
|
|
- <el-option label="有声音就打断" :value="2" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <!-- 中断关键词 -->
|
|
|
- <el-form-item
|
|
|
- v-if="form.interruptFlag === 1"
|
|
|
- label="打断关键词"
|
|
|
- prop="interruptKeywords"
|
|
|
- >
|
|
|
- <el-input
|
|
|
- v-model="form.interruptKeywords"
|
|
|
- type="textarea"
|
|
|
- :rows="5"
|
|
|
- placeholder="请输入打断关键词"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <!-- 忽略中断关键词 -->
|
|
|
- <el-form-item
|
|
|
- v-if="form.interruptFlag === 1"
|
|
|
- label="忽略打断关键词"
|
|
|
- prop="interruptIgnoreKeywords"
|
|
|
- >
|
|
|
- <el-input
|
|
|
- v-model="form.interruptIgnoreKeywords"
|
|
|
- type="textarea"
|
|
|
- :rows="5"
|
|
|
- placeholder="请输入忽略打断关键词"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <!-- 转人工数字按键 -->
|
|
|
- <el-form-item label="转人工数字按键" prop="transferManualDigit">
|
|
|
- <el-input
|
|
|
- v-model="form.transferManualDigit"
|
|
|
- placeholder="例如: 1"
|
|
|
- maxlength="1"
|
|
|
- @input="handleTransferManualDigitInput"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <!-- 客户意向提示词 -->
|
|
|
- <el-form-item
|
|
|
- v-if="showIntentionTips"
|
|
|
- label="客户意向提示词"
|
|
|
- prop="intentionTips"
|
|
|
- >
|
|
|
- <el-input
|
|
|
- v-model="form.intentionTips"
|
|
|
- type="textarea"
|
|
|
- :rows="5"
|
|
|
- placeholder="请输入客户意向提示词"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
-
|
|
|
- <!-- 底部按钮 -->
|
|
|
- <div class="dialog-footer">
|
|
|
- <el-button @click="handleCancel">取 消</el-button>
|
|
|
- <el-button type="primary" @click="handleSubmit" :loading="submitting">确 定</el-button>
|
|
|
- </div>
|
|
|
+ <el-option
|
|
|
+ v-for="item in kbCatOptions"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="开场白" prop="accountJson.openingRemarks">
|
|
|
+ <el-input
|
|
|
+ v-model="form.accountJson.openingRemarks"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ placeholder="请输入开场白"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="转人工提示词" prop="accountJson.transferToAgentTips">
|
|
|
+ <el-input
|
|
|
+ v-model="form.accountJson.transferToAgentTips"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ placeholder="请输入转人工提示词"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="挂机提示" prop="accountJson.hangupTips">
|
|
|
+ <el-input
|
|
|
+ v-model="form.accountJson.hangupTips"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ placeholder="请输入挂机提示"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="客户不说话提示" prop="accountJson.customerNoVoiceTips">
|
|
|
+ <el-input
|
|
|
+ v-model="form.accountJson.customerNoVoiceTips"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ placeholder="请输入客户不说话时的提示"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="客户意向提示词" prop="intentionTips">
|
|
|
+ <el-input
|
|
|
+ v-model="form.intentionTips"
|
|
|
+ type="textarea"
|
|
|
+ :rows="5"
|
|
|
+ placeholder="请输入客户意向分析提示词"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-divider content-position="left">通话行为</el-divider>
|
|
|
+
|
|
|
+ <el-form-item label="打断开关" prop="interruptFlag">
|
|
|
+ <el-select v-model="form.interruptFlag" placeholder="请选择打断方式" style="width: 100%">
|
|
|
+ <el-option label="不打断" :value="0" />
|
|
|
+ <el-option label="关键词打断" :value="1" />
|
|
|
+ <el-option label="有声音就打断" :value="2" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item
|
|
|
+ v-if="form.interruptFlag === 1"
|
|
|
+ label="打断关键词"
|
|
|
+ prop="interruptKeywords"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="form.interruptKeywords"
|
|
|
+ type="textarea"
|
|
|
+ :rows="4"
|
|
|
+ placeholder="请输入打断关键词"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item
|
|
|
+ v-if="form.interruptFlag === 1"
|
|
|
+ label="忽略打断关键词"
|
|
|
+ prop="interruptIgnoreKeywords"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="form.interruptIgnoreKeywords"
|
|
|
+ type="textarea"
|
|
|
+ :rows="4"
|
|
|
+ placeholder="请输入忽略打断关键词"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="转人工数字按键" prop="transferManualDigit">
|
|
|
+ <el-input
|
|
|
+ v-model="form.transferManualDigit"
|
|
|
+ placeholder="例如: 1"
|
|
|
+ maxlength="1"
|
|
|
+ @input="handleTransferManualDigitInput"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button @click="handleCancel">取 消</el-button>
|
|
|
+ <el-button type="primary" :loading="submitting" @click="handleSubmit">确 定</el-button>
|
|
|
</div>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { add, update, getCidConfig } from '@/api/company/aiModel' // 假设有 update 接口
|
|
|
+import { add, update } from '@/api/company/aiModel'
|
|
|
import { all } from '@/api/company/aiCall'
|
|
|
|
|
|
+const DEFAULT_IGNORE_KEYWORDS = '呃 哦 哦哦 嗯 嗯嗯 嗯好的 好的 对 对对 是的 明白 啊 这样啊 是这样啊这样的 您好 你好'
|
|
|
+
|
|
|
export default {
|
|
|
- name: 'AccountForm',
|
|
|
- props: {
|
|
|
- providerOptions: { type: Array, default: () => [] },
|
|
|
- initialData: {
|
|
|
- type: Object,
|
|
|
- default: () => ({})
|
|
|
- },
|
|
|
- errorMsg: {
|
|
|
- type: String,
|
|
|
- default: ''
|
|
|
- }
|
|
|
+ name: 'OutboundAiConfigForm',
|
|
|
+ props: {
|
|
|
+ initialData: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({})
|
|
|
},
|
|
|
- data() {
|
|
|
- return {
|
|
|
- submitting: false,
|
|
|
- // 表单数据
|
|
|
- form: {
|
|
|
- id: undefined,
|
|
|
- name: '',
|
|
|
- providerClassName: '',
|
|
|
- concurrentNum: '',
|
|
|
- interruptFlag: 0,
|
|
|
- interruptKeywords: '',
|
|
|
- interruptIgnoreKeywords: '呃 哦 哦哦 嗯 嗯嗯 嗯好的 好的 对 对对 是的 明白 啊 这样啊 是这样啊这样的 您好 你好',
|
|
|
- transferManualDigit: '',
|
|
|
- intentionTips: '',
|
|
|
- accountJson: {}
|
|
|
- },
|
|
|
- // 知识库分类选项
|
|
|
- kbCatOptions: [],
|
|
|
- // 动态字段配置
|
|
|
- dynamicFields: [],
|
|
|
- // 是否显示客户意向提示词
|
|
|
- showIntentionTips: false,
|
|
|
- cidConf:{}
|
|
|
+ sceneInfo: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({})
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ submitting: false,
|
|
|
+ kbCatOptions: [],
|
|
|
+ form: {
|
|
|
+ id: undefined,
|
|
|
+ name: '',
|
|
|
+ interruptFlag: 0,
|
|
|
+ interruptKeywords: '',
|
|
|
+ interruptIgnoreKeywords: DEFAULT_IGNORE_KEYWORDS,
|
|
|
+ transferManualDigit: '',
|
|
|
+ intentionTips: '',
|
|
|
+ accountJson: {
|
|
|
+ llmTips: '',
|
|
|
+ faqContext: '',
|
|
|
+ kbCatId: undefined,
|
|
|
+ openingRemarks: '',
|
|
|
+ transferToAgentTips: '',
|
|
|
+ hangupTips: '',
|
|
|
+ customerNoVoiceTips: ''
|
|
|
}
|
|
|
- },
|
|
|
- computed: {
|
|
|
- // 表单验证规则
|
|
|
- rules() {
|
|
|
- const baseRules = {
|
|
|
- name: [
|
|
|
- { required: true, message: '请输入模型名称', trigger: 'blur' }
|
|
|
- ],
|
|
|
- providerClassName: [
|
|
|
- { required: true, message: '请选择实现类', trigger: 'change' }
|
|
|
- ],
|
|
|
- concurrentNum: [
|
|
|
- { 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
|
|
|
+ },
|
|
|
+ rules: {
|
|
|
+ name: [{ required: true, message: '请输入配置名称', trigger: 'blur' }],
|
|
|
+ 'accountJson.llmTips': [{ required: true, message: '请输入大模型提示词', trigger: 'blur' }],
|
|
|
+ 'accountJson.faqContext': [{ required: true, message: '请输入 FAQ 上下文', trigger: 'blur' }],
|
|
|
+ 'accountJson.openingRemarks': [{ required: true, message: '请输入开场白', trigger: 'blur' }],
|
|
|
+ 'accountJson.transferToAgentTips': [{ required: true, message: '请输入转人工提示词', trigger: 'blur' }],
|
|
|
+ 'accountJson.hangupTips': [{ required: true, message: '请输入挂机提示', trigger: 'blur' }],
|
|
|
+ 'accountJson.customerNoVoiceTips': [{ required: true, message: '请输入客户不说话提示', trigger: 'blur' }],
|
|
|
+ interruptFlag: [{ required: true, message: '请选择打断方式', trigger: 'change' }],
|
|
|
+ transferManualDigit: [{ pattern: /^[0-9]?$/, message: '请输入单个数字 0-9', trigger: 'blur' }]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ initialData: {
|
|
|
+ handler(val) {
|
|
|
+ if (val && Object.keys(val).length > 0) {
|
|
|
+ this.initFormData(val)
|
|
|
}
|
|
|
+ },
|
|
|
+ deep: true,
|
|
|
+ immediate: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.loadKbCatOptions()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ initFormData(data) {
|
|
|
+ this.form.id = data.id
|
|
|
+ this.form.name = data.name || ''
|
|
|
+ this.form.interruptFlag = data.interruptFlag != null ? data.interruptFlag : 0
|
|
|
+ this.form.interruptKeywords = data.interruptKeywords || ''
|
|
|
+ this.form.interruptIgnoreKeywords = data.interruptIgnoreKeywords || DEFAULT_IGNORE_KEYWORDS
|
|
|
+ this.form.transferManualDigit = data.transferManualDigit || ''
|
|
|
+ this.form.intentionTips = data.intentionTips || ''
|
|
|
+
|
|
|
+ let parsed = {}
|
|
|
+ try {
|
|
|
+ parsed = data.accountJson
|
|
|
+ ? (typeof data.accountJson === 'string' ? JSON.parse(data.accountJson) : data.accountJson)
|
|
|
+ : {}
|
|
|
+ } catch (e) {
|
|
|
+ parsed = {}
|
|
|
+ }
|
|
|
+
|
|
|
+ const kbCatId = parsed.kbCatId != null && parsed.kbCatId !== -1
|
|
|
+ ? parsed.kbCatId
|
|
|
+ : (data.kbCatId != null && data.kbCatId !== -1 ? data.kbCatId : undefined)
|
|
|
+
|
|
|
+ this.form.accountJson = {
|
|
|
+ llmTips: parsed.llmTips || '',
|
|
|
+ faqContext: parsed.faqContext || '',
|
|
|
+ kbCatId,
|
|
|
+ openingRemarks: parsed.openingRemarks || '',
|
|
|
+ transferToAgentTips: parsed.transferToAgentTips || '',
|
|
|
+ hangupTips: parsed.hangupTips || '',
|
|
|
+ customerNoVoiceTips: parsed.customerNoVoiceTips || ''
|
|
|
+ }
|
|
|
},
|
|
|
- watch: {
|
|
|
- // 监听初始数据变化
|
|
|
- initialData: {
|
|
|
- handler(val) {
|
|
|
- if (val && Object.keys(val).length > 0) {
|
|
|
- this.initFormData(val)
|
|
|
- }
|
|
|
- },
|
|
|
- deep: true,
|
|
|
- immediate: true
|
|
|
- },
|
|
|
- // 监听错误信息
|
|
|
- errorMsg: {
|
|
|
- handler(val) {
|
|
|
- if (val) {
|
|
|
- this.$alert(val, '错误', {
|
|
|
- type: 'error',
|
|
|
- confirmButtonText: '确定'
|
|
|
- })
|
|
|
- }
|
|
|
- },
|
|
|
- immediate: true
|
|
|
- }
|
|
|
+ handleTransferManualDigitInput(value) {
|
|
|
+ let val = value.replace(/[^0-9]/g, '')
|
|
|
+ if (val.length > 1) val = val.substring(0, 1)
|
|
|
+ this.$set(this.form, 'transferManualDigit', val)
|
|
|
},
|
|
|
- created() {
|
|
|
- this.loadKbCatOptions();
|
|
|
- this.initCidConfData();
|
|
|
+ async loadKbCatOptions() {
|
|
|
+ try {
|
|
|
+ const response = await all()
|
|
|
+ const list = response?.data ?? []
|
|
|
+ this.kbCatOptions = Array.isArray(list)
|
|
|
+ ? list.map(item => ({ label: item.cat, value: item.id }))
|
|
|
+ : []
|
|
|
+ } catch (error) {
|
|
|
+ this.kbCatOptions = []
|
|
|
+ }
|
|
|
},
|
|
|
- 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
|
|
|
- this.form.name = data.name || ''
|
|
|
- this.form.providerClassName = data.providerClassName || ''
|
|
|
- this.form.concurrentNum = data.concurrentNum || ''
|
|
|
- this.form.interruptFlag = data.interruptFlag || 0
|
|
|
- this.form.interruptKeywords = data.interruptKeywords || ''
|
|
|
- this.form.interruptIgnoreKeywords = data.interruptIgnoreKeywords || ''
|
|
|
- this.form.transferManualDigit = data.transferManualDigit || ''
|
|
|
- this.form.intentionTips = data.intentionTips || ''
|
|
|
- // 解析accountJson
|
|
|
- try {
|
|
|
- this.form.accountJson = data.accountJson ?
|
|
|
- (typeof data.accountJson === 'string' ? JSON.parse(data.accountJson) : data.accountJson)
|
|
|
- : {}
|
|
|
- } catch (e) {
|
|
|
- console.error('解析accountJson失败:', e)
|
|
|
- this.form.accountJson = {}
|
|
|
- }
|
|
|
-
|
|
|
- // 根据providerClassName更新动态字段
|
|
|
- if (this.form.providerClassName) {
|
|
|
- this.updateDynamicFields(this.form.providerClassName,true)
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 并发数输入处理
|
|
|
- handleConcurrentNumInput(value) {
|
|
|
- // 只保留数字
|
|
|
- let val = value.replace(/[^0-9]/g, '')
|
|
|
- // 去掉前导0
|
|
|
- val = val.replace(/^0+(\d)/, '$1')
|
|
|
- // 上限200
|
|
|
- if (parseInt(val) > 200) val = '200'
|
|
|
- this.$set(this.form, 'concurrentNum', val)
|
|
|
- },
|
|
|
-
|
|
|
- // 转人工数字按键输入处理
|
|
|
- handleTransferManualDigitInput(value) {
|
|
|
- // 只保留0-9的数字,并限制为单个数字
|
|
|
- let val = value.replace(/[^0-9]/g, '')
|
|
|
- if (val.length > 1) val = val.substring(0, 1)
|
|
|
- this.$set(this.form, 'transferManualDigit', val)
|
|
|
- },
|
|
|
-
|
|
|
- // 并发数验证器
|
|
|
- validateConcurrentNum(rule, value, callback) {
|
|
|
- const num = parseInt(value)
|
|
|
- if (isNaN(num) || num < 0 || num > 200) {
|
|
|
- callback(new Error('请输入0-200之间的整数'))
|
|
|
- } else {
|
|
|
- callback()
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 实现类变更处理
|
|
|
- handleProviderChange(val) {
|
|
|
- this.updateDynamicFields(val)
|
|
|
- },
|
|
|
-
|
|
|
- // 更新动态字段
|
|
|
- updateDynamicFields(providerClassName,isUpdate) {
|
|
|
- this.dynamicFields = []
|
|
|
- this.showIntentionTips = false
|
|
|
-
|
|
|
- // 清空accountJson中不相关的字段
|
|
|
- const newAccountJson = {}
|
|
|
-
|
|
|
- if (['DeepSeekChat', 'ChatGpt4o', 'JiutianChat'].includes(providerClassName)) {
|
|
|
- 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 }
|
|
|
- ]
|
|
|
- this.showIntentionTips = true
|
|
|
- }
|
|
|
-
|
|
|
- if (['LocalLlmChat'].includes(providerClassName)) {
|
|
|
- this.dynamicFields = [
|
|
|
- { name: 'serverUrl', label: '服务地址', type: 'input', required: true },
|
|
|
- { name: 'modelName', label: '模型名称', type: 'input', required: true },
|
|
|
- { 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 }
|
|
|
- ]
|
|
|
- }
|
|
|
-
|
|
|
- if (['LocalNlpChat'].includes(providerClassName)) {
|
|
|
- this.dynamicFields = [
|
|
|
- { name: 'serverUrl', label: '服务地址', type: 'input', required: true },
|
|
|
- { name: 'botId', label: 'botId', type: 'input', required: true },
|
|
|
- { 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 }
|
|
|
- ]
|
|
|
- }
|
|
|
-
|
|
|
- if (['Coze'].includes(providerClassName)) {
|
|
|
- this.dynamicFields = [
|
|
|
- { name: 'serverUrl', label: '服务地址', type: 'input', required: true },
|
|
|
- { name: 'botId', label: 'botId', type: 'input', required: true },
|
|
|
- {
|
|
|
- name: 'tokenType',
|
|
|
- label: 'Token类型',
|
|
|
- type: 'select',
|
|
|
- required: true,
|
|
|
- options: [
|
|
|
- { label: 'OAuth', value: 'oauth' },
|
|
|
- { label: 'PAT', value: 'pat' }
|
|
|
- ]
|
|
|
- },
|
|
|
- { 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 }
|
|
|
- ]
|
|
|
- }
|
|
|
-
|
|
|
- if (['MaxKB', 'Dify'].includes(providerClassName)) {
|
|
|
- this.dynamicFields = [
|
|
|
- { name: 'serverUrl', label: '服务地址', type: 'input', required: true },
|
|
|
- { name: 'apiKey', label: 'apiKey', type: 'input', required: true },
|
|
|
- { 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 }
|
|
|
- ]
|
|
|
- }
|
|
|
-
|
|
|
- if (['JiutianWorkflow', 'JiutianAgent'].includes(providerClassName)) {
|
|
|
- this.dynamicFields = [
|
|
|
- { name: 'serverUrl', label: '服务地址', type: 'input', required: true },
|
|
|
- { name: 'apiKey', label: 'apiKey', type: 'input', required: true },
|
|
|
- { name: 'botId', label: 'botId', type: 'input', required: true },
|
|
|
- { 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 }
|
|
|
- ]
|
|
|
- }
|
|
|
-
|
|
|
- // 保留原有数据
|
|
|
- this.dynamicFields.forEach(field => {
|
|
|
- if (this.form.accountJson[field.name]) {
|
|
|
- newAccountJson[field.name] = this.form.accountJson[field.name]
|
|
|
- }
|
|
|
- })
|
|
|
-
|
|
|
- // 合并数据
|
|
|
- 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 }
|
|
|
- ]
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 下拉框变更处理
|
|
|
- handleSelectChange(fieldName, value) {
|
|
|
- if (fieldName === 'tokenType') {
|
|
|
- // 清空相关字段
|
|
|
- delete this.form.accountJson.oauthClientId
|
|
|
- delete this.form.accountJson.oauthPrivateKey
|
|
|
- delete this.form.accountJson.oauthPublicKeyId
|
|
|
- delete this.form.accountJson.patToken
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 加载知识库分类选项
|
|
|
- async loadKbCatOptions() {
|
|
|
- try {
|
|
|
- const response = await all();
|
|
|
- const list = response?.data ?? [];
|
|
|
- this.kbCatOptions = Array.isArray(list)
|
|
|
- ? list.map(item => ({ label: item.cat, value: item.id }))
|
|
|
- : [];
|
|
|
- } catch (error) {
|
|
|
- console.error('加载知识库分类失败:', error);
|
|
|
- this.kbCatOptions = [];
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 取消
|
|
|
- handleCancel() {
|
|
|
- this.$emit('cancel')
|
|
|
- },
|
|
|
-
|
|
|
- // 提交
|
|
|
- async handleSubmit() {
|
|
|
- await this.$refs.form.validate(async (valid) => {
|
|
|
- if (valid) {
|
|
|
- this.submitting = true
|
|
|
-
|
|
|
- try {
|
|
|
- if (!this.showIntentionTips) {
|
|
|
- this.form.intentionTips = ''
|
|
|
- }
|
|
|
-
|
|
|
- const submitData = {
|
|
|
- id: this.form.id,
|
|
|
- name: this.form.name,
|
|
|
- providerClassName: this.form.providerClassName,
|
|
|
- concurrentNum: this.form.concurrentNum,
|
|
|
- interruptFlag: this.form.interruptFlag,
|
|
|
- interruptKeywords: this.form.interruptKeywords || '',
|
|
|
- interruptIgnoreKeywords: this.form.interruptIgnoreKeywords || '',
|
|
|
- transferManualDigit: this.form.transferManualDigit || '',
|
|
|
- intentionTips: this.form.intentionTips || ''
|
|
|
- }
|
|
|
-
|
|
|
- if (this.form.accountJson) {
|
|
|
- Object.keys(this.form.accountJson).forEach(key => {
|
|
|
- submitData[key] = this.form.accountJson[key]
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- submitData.accountJson = JSON.stringify(this.form.accountJson)
|
|
|
-
|
|
|
- let response;
|
|
|
- if (submitData.id) {
|
|
|
- // 修改操作
|
|
|
- response = await update(submitData);
|
|
|
- } else {
|
|
|
- // 新增操作
|
|
|
- delete submitData.id;
|
|
|
- response = await add(submitData);
|
|
|
- }
|
|
|
-
|
|
|
- this.$message.success('操作成功!')
|
|
|
- this.$emit('success', response)
|
|
|
- } catch (error) {
|
|
|
- console.error('提交失败:', error)
|
|
|
- this.$message.error('操作失败')
|
|
|
- } finally {
|
|
|
- this.submitting = false
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
+ handleCancel() {
|
|
|
+ this.$emit('cancel')
|
|
|
+ },
|
|
|
+ handleSubmit() {
|
|
|
+ this.$refs.form.validate(async valid => {
|
|
|
+ if (!valid) return
|
|
|
+ if (!this.sceneInfo.models || !this.sceneInfo.models.length) {
|
|
|
+ this.$message.warning('总后台尚未配置外呼场景模型,无法保存')
|
|
|
+ return
|
|
|
}
|
|
|
+ this.submitting = true
|
|
|
+ try {
|
|
|
+ const accountJson = { ...this.form.accountJson }
|
|
|
+ const submitData = {
|
|
|
+ id: this.form.id,
|
|
|
+ name: this.form.name,
|
|
|
+ interruptFlag: this.form.interruptFlag,
|
|
|
+ interruptKeywords: this.form.interruptKeywords || '',
|
|
|
+ interruptIgnoreKeywords: this.form.interruptIgnoreKeywords || '',
|
|
|
+ transferManualDigit: this.form.transferManualDigit || '',
|
|
|
+ intentionTips: this.form.intentionTips || '',
|
|
|
+ kbCatId: accountJson.kbCatId != null ? accountJson.kbCatId : -1,
|
|
|
+ accountJson: JSON.stringify(accountJson)
|
|
|
+ }
|
|
|
+ const response = submitData.id ? await update(submitData) : await add(submitData)
|
|
|
+ if (response.code === 200) {
|
|
|
+ this.$message.success('保存成功')
|
|
|
+ this.$emit('success', response)
|
|
|
+ } else {
|
|
|
+ this.$message.error(response.msg || '保存失败')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(error.msg || error.message || '保存失败')
|
|
|
+ } finally {
|
|
|
+ this.submitting = false
|
|
|
+ }
|
|
|
+ })
|
|
|
}
|
|
|
+ }
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-.app-container {
|
|
|
- padding: 20px;
|
|
|
-}
|
|
|
-
|
|
|
-.account-form {
|
|
|
- max-width: 800px;
|
|
|
- margin: 0 auto;
|
|
|
+.outbound-config-form {
|
|
|
+ padding: 16px 20px 72px;
|
|
|
+ overflow-y: auto;
|
|
|
+ flex: 1;
|
|
|
}
|
|
|
|
|
|
.dialog-footer {
|
|
|
- text-align: center;
|
|
|
- padding-top: 20px;
|
|
|
- border-top: 1px solid #eee;
|
|
|
- margin-top: 20px;
|
|
|
-}
|
|
|
-
|
|
|
-.oauth-card {
|
|
|
- margin-bottom: 20px;
|
|
|
-}
|
|
|
-
|
|
|
-.oauth-card :deep(.el-card__body) {
|
|
|
- padding: 20px;
|
|
|
+ position: absolute;
|
|
|
+ bottom: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ padding: 12px 20px;
|
|
|
+ background: #fff;
|
|
|
+ border-top: 1px solid #f0f2f5;
|
|
|
+ text-align: right;
|
|
|
+ z-index: 1;
|
|
|
}
|
|
|
</style>
|