| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906 |
- <!DOCTYPE html>
- <html lang="zh" xmlns:th="http://www.thymeleaf.org" >
- <head>
- <th:block th:include="include :: header('修改机器人参数配置')" />
- <base target="_blank">
- </head>
- <body class="white-bg">
- <div class="wrapper wrapper-content animated fadeInRight ibox-content">
- <form class="form-horizontal m" id="form-account-edit" th:object="${ccLlmAgentAccount}">
- <input name="id" th:field="*{id}" type="hidden">
- <input name="rootId" id="rootId" type="hidden">
- <input name="openingRemarksWav" id="openingRemarksWav" type="hidden">
- <input name="customerNoVoiceTipsWav" id="customerNoVoiceTipsWav" type="hidden">
- <input name="hangupTipsWav" id="hangupTipsWav" type="hidden">
- <input name="transferToAgentTipsWav" id="transferToAgentTipsWav" type="hidden">
- <div class="col-xs-12">
- <div class="form-group">
- <label class="col-sm-3 control-label is-required" th:text="#{llmAcount.form.name}"></label>
- <div class="col-sm-8">
- <input name="name" th:field="*{name}" class="form-control" required type="text">
- </div>
- </div>
- </div>
- <div class="col-xs-12">
- <div class="form-group">
- <label class="col-sm-3 control-label is-required" th:text="#{llmAcount.form.providerClassName}"></label>
- <div class="col-sm-8">
- <select name="providerClassName" th:field="*{providerClassName}" class="form-control" required id="providerClassNameSelect">
- <!-- 选项将通过 JavaScript 动态填充 -->
- </select>
- </div>
- </div>
- </div>
- <div class="col-xs-12">
- <div class="form-group">
- <label class="col-sm-3 control-label is-required" th:text="#{llmAcount.form.concurrentNum}"></label>
- <div class="col-sm-8">
- <input name="concurrentNum" th:field="*{concurrentNum}" class="form-control" required type="text">
- </div>
- </div>
- </div>
- <div class="col-xs-12" id="dynamicFieldsContainer">
- <!-- 动态字段将在这里显示 -->
- </div>
- <div class="col-xs-12">
- <div class="form-group">
- <label class="col-sm-3 control-label is-required" th:text="#{llmAcount.form.interruptFlag}"></label>
- <div class="col-sm-8">
- <select name="interruptFlag" class="form-control" required id="interruptFlagSelect" th:field="*{interruptFlag}">
- <option value="0" th:text="#{llmAcount.form.interruptFlag0}"></option>
- <option value="1" th:text="#{llmAcount.form.interruptFlag1}"></option>
- <option value="2" th:text="#{llmAcount.form.interruptFlag2}"></option>
- </select>
- </div>
- </div>
- </div>
- <div class="col-xs-12" id="interruptKeywordsContainer" style="display: none;">
- <div class="form-group">
- <label class="col-sm-3 control-label " th:text="#{llmAcount.form.interruptKeywords}"></label>
- <div class="col-sm-8">
- <textarea name="interruptKeywords" class="form-control" rows="5" th:field="*{interruptKeywords}"></textarea>
- </div>
- </div>
- </div>
- <div class="col-xs-12" id="interruptIgnoreKeywordsContainer" style="display: none;">
- <div class="form-group">
- <label class="col-sm-3 control-label " th:text="#{llmAcount.form.interruptIgnoreKeywords}"></label>
- <div class="col-sm-8">
- <textarea name="interruptIgnoreKeywords" class="form-control" rows="5" th:field="*{interruptIgnoreKeywords}"></textarea>
- </div>
- </div>
- </div>
- <div class="col-xs-12">
- <div class="form-group">
- <label class="col-sm-3 control-label" th:text="#{llmAcount.form.transferManualDigit}"></label>
- <div class="col-sm-8">
- <input name="transferManualDigit" class="form-control" type="text" maxlength="1" placeholder="例如: 1" th:field="*{transferManualDigit}">
- </div>
- </div>
- </div>
- <div class="col-xs-12" id="intentionTipsContainer" >
- <div class="form-group">
- <label class="col-sm-3 control-label" th:text="#{llmAcount.form.intentionTips}"></label>
- <div class="col-sm-8">
- <textarea name="intentionTips" class="form-control" rows="5" th:field="*{intentionTips}"></textarea>
- </div>
- </div>
- </div>
- </form>
- </div>
- <th:block th:include="include :: footer" />
- <script th:inline="javascript">
- var prefix = ctx + "aicall/account"
- var _hideIntentionTipsContainer = true;
- var _accountJson = JSON.parse([[${ccLlmAgentAccount.accountJson}]]);
- var _providerClassName = [[${ccLlmAgentAccount.providerClassName}]];
- var _dynamicFieldsDiv = initDynamicFieldsDiv(_accountJson);
- var _errorMsg = [[${errorMsg}]];
- $(document).ready(function() {
- // 获取实现类下拉框数据
- $.ajax({
- url: ctx + "aicall/provider/all", // 接口地址
- type: "GET",
- success: function(response) {
- // 假设返回的数据是一个数组,例如:[{providerClassName: "Provider1"}, {providerClassName: "Provider2"}]
- var providers = response.data;
- var select = $("#providerClassNameSelect");
- select.empty(); // 清空之前的选项
- providers.forEach(function(provider) {
- select.append($("<option>", {
- value: provider.providerClassName,
- text: provider.providerClassName
- }));
- });
- select.val(_providerClassName);
- // 初始化动态字段
- updateDynamicFields(select.val());
- },
- error: function(xhr, status, error) {
- console.error("获取实现类数据失败:", error);
- }
- });
- // 监听下拉框变化
- $("#providerClassNameSelect").change(function() {
- updateDynamicFields($(this).val());
- });
- // 监听下拉框变化
- $("#providerClassNameSelect").change(function() {
- updateDynamicFields($(this).val());
- });
- //
- $("#interruptFlagSelect").change(function() {
- toggleInterruptFields($(this).val());
- });
- // 初始化时根据当前值设置显示状态
- toggleInterruptFields($("#interruptFlagSelect").val());
- // 转人工数字按键:只允许输入 0-9 的单个数字
- $('input[name="transferManualDigit"]').on('input propertychange paste', function () {
- let v = this.value;
- // 只保留0-9的数字
- v = v.replace(/[^0-9]/g, '');
- // 限制为单个数字
- if (v.length > 1) v = v.substring(0, 1);
- this.value = v;
- });
- // 绑定文件上传按钮事件(动态生成的元素使用事件委托)
- $(document).on('click', '.btn-file-upload', function() {
- var fieldId = $(this).data('field');
- var uploadType = $(this).data('upload-type') || 'voice';
- openUploadModal(fieldId, uploadType);
- });
- // 初始化Wav隐藏字段值(从accountJson中获取)
- $('#openingRemarksWav').val(_accountJson.openingRemarksWav || '');
- $('#customerNoVoiceTipsWav').val(_accountJson.customerNoVoiceTipsWav || '');
- $('#hangupTipsWav').val(_accountJson.hangupTipsWav || '');
- $('#transferToAgentTipsWav').val(_accountJson.transferToAgentTipsWav || '');
- $('#rootId').val(_accountJson.rootId || '123');
- // 错误处理:如果存在错误消息,则显示错误并禁用所有操作
- if (_errorMsg && _errorMsg !== '' && _errorMsg !== null) {
- // // 禁用表单内所有输入元素
- // $('#form-account-edit').find('input, select, textarea').prop('disabled', true);
- // 禁用确定
- $(window.parent.document).find('.btn-confirm, .layui-layer-btn0').hide();
- // 显示错误消息
- $.modal.alertError(_errorMsg);
- }
- // 音频预览回显 - 如果有已上传的录音文件则显示预览
- setTimeout(function() {
- // 从_accountJson获取文件URL(accountJson的属性)
- var fileUrlMapping = {
- 'openingRemarks': _accountJson.openingRemarksFileUrl || '',
- 'customerNoVoiceTips': _accountJson.customerNoVoiceTipsFileUrl || '',
- 'hangupTips': _accountJson.hangupTipsFileUrl || '',
- 'transferToAgentTips': _accountJson.transferToAgentTipsFileUrl || ''
- };
- Object.keys(fileUrlMapping).forEach(function(fieldName) {
- var fileUrl = fileUrlMapping[fieldName];
- if (fileUrl && fileUrl.endsWith('.wav')) {
- showAudioPreview(fieldName, fileUrl);
- }
- });
- }, 500); // 延迟执行,确保动态字段已渲染
- });
- // 根据选择的实现类动态更新表单字段
- function updateDynamicFields(providerClassName) {
- var container = $("#dynamicFieldsContainer");
- container.empty(); // 清空之前的动态字段
- $("#intentionTipsContainer").hide(); // 客户意向提示词默认隐藏
- _hideIntentionTipsContainer = true;
- if (["DeepSeekChat", "ChatGPT", "ClaudeChat", "JiutianChat"].includes(providerClassName)) {
- // 显示serverUrl、apiKey、modelName、
- // llmTips、faqContext、transferToAgentTips、hangupTips、customerNoVoiceTips、openingRemarks
- container.append(_dynamicFieldsDiv["serverUrl"]);
- container.append(_dynamicFieldsDiv["apiKey"]);
- container.append(_dynamicFieldsDiv["modelName"]);
- container.append(_dynamicFieldsDiv["llmTips"]);
- container.append(_dynamicFieldsDiv["faqContext"]);
- container.append(_dynamicFieldsDiv["kbCatId"]);
- loadKbCatOptions(); // 加载下拉数据
- container.append(_dynamicFieldsDiv["transferToAgentTips"]);
- container.append(_dynamicFieldsDiv["hangupTips"]);
- container.append(_dynamicFieldsDiv["customerNoVoiceTips"]);
- container.append(_dynamicFieldsDiv["openingRemarks"]);
- // intentionTipsContainer
- $("#intentionTipsContainer").show();
- _hideIntentionTipsContainer = false;
- }
- if (["XingWenChat"].includes(providerClassName)) {
- // 显示serverUrl、apiKey、modelName、
- // llmTips、faqContext、transferToAgentTips、hangupTips、customerNoVoiceTips、openingRemarks
- container.append(_dynamicFieldsDiv["serverUrl"]);
- // container.append(_dynamicFieldsDiv["apiKey"]);
- container.append(_dynamicFieldsDiv["modelName"]);
- // container.append(_dynamicFieldsDiv["llmTips"]);
- // container.append(_dynamicFieldsDiv["faqContext"]);
- container.append(_dynamicFieldsDiv["transferToAgentTips"]);
- container.append(_dynamicFieldsDiv["hangupTips"]);
- container.append(_dynamicFieldsDiv["customerNoVoiceTips"]);
- container.append(_dynamicFieldsDiv["openingRemarks"]);
- // // intentionTipsContainer
- // $("#intentionTipsContainer").show();
- // _hideIntentionTipsContainer = false;
- }
- if (["LocalLlmChat"].includes(providerClassName)) {
- // 显示serverUrl、apiKey、modelName、
- // llmTips、faqContext、transferToAgentTips、hangupTips、customerNoVoiceTips、openingRemarks
- container.append(_dynamicFieldsDiv["serverUrl"]);
- container.append(_dynamicFieldsDiv["apiKey"]);
- container.append(_dynamicFieldsDiv["modelName"]);
- container.append(_dynamicFieldsDiv["llmTips"]);
- container.append(_dynamicFieldsDiv["faqContext"]);
- container.append(_dynamicFieldsDiv["transferToAgentTips"]);
- container.append(_dynamicFieldsDiv["hangupTips"]);
- container.append(_dynamicFieldsDiv["customerNoVoiceTips"]);
- container.append(_dynamicFieldsDiv["openingRemarks"]);
- }
- if (["LocalWavFile"].includes(providerClassName)) {
- // 同LocalLlmChat显示的元素,但使用带上传按钮的录音字段
- container.append(_dynamicFieldsDiv["serverUrl"]);
- container.append(_dynamicFieldsDiv["apiKey"]);
- container.append(_dynamicFieldsDiv["modelName"]);
- container.append(_dynamicFieldsDiv["llmTips"]);
- container.append(_dynamicFieldsDiv["faqContext"]);
- container.append(_dynamicFieldsDiv["transferToAgentTipsWav"]);
- container.append(_dynamicFieldsDiv["hangupTipsWav"]);
- container.append(_dynamicFieldsDiv["customerNoVoiceTipsWav"]);
- container.append(_dynamicFieldsDiv["openingRemarksWav"]);
- }
- if (["LocalNlpChat"].includes(providerClassName)) {
- container.append(_dynamicFieldsDiv["serverUrl"]);
- container.append(_dynamicFieldsDiv["botId"]);
- container.append(_dynamicFieldsDiv["transferToAgentTips"]);
- container.append(_dynamicFieldsDiv["hangupTips"]);
- container.append(_dynamicFieldsDiv["customerNoVoiceTips"]);
- }
- if (["Coze"].includes(providerClassName)) {
- // 显示 serverUrl、botId、tokenType、tokenTypeFields、
- // transferToAgentTips、hangupTips、customerNoVoiceTips、openingRemarks
- container.append(_dynamicFieldsDiv["serverUrl"]);
- container.append(_dynamicFieldsDiv["botId"]);
- container.append(_dynamicFieldsDiv["tokenType"]);
- container.append(_dynamicFieldsDiv["tokenTypeFields"]);
- container.append(_dynamicFieldsDiv["transferToAgentTips"]);
- container.append(_dynamicFieldsDiv["hangupTips"]);
- container.append(_dynamicFieldsDiv["customerNoVoiceTips"]);
- container.append(_dynamicFieldsDiv["openingRemarks"]);
- // 监听 tokenType 下拉框变化
- $("#tokenTypeSelect").change(function() {
- var selectedTokenType = $(this).val();
- updateTokenTypeSelect(selectedTokenType);
- });
- $("#tokenTypeSelect").val(_accountJson.tokenType);
- var selectedTokenType = $("#tokenTypeSelect").val();
- updateTokenTypeSelect(selectedTokenType);
- }
- if (["MaxKB"].includes(providerClassName)) {
- // 显示serverUrl、apiKey、
- // transferToAgentTips、hangupTips、customerNoVoiceTips、openingRemarks
- container.append(_dynamicFieldsDiv["serverUrl"]);
- container.append(_dynamicFieldsDiv["apiKey"]);
- container.append(_dynamicFieldsDiv["transferToAgentTips"]);
- container.append(_dynamicFieldsDiv["hangupTips"]);
- container.append(_dynamicFieldsDiv["customerNoVoiceTips"]);
- container.append(_dynamicFieldsDiv["openingRemarks"]);
- }
- if (["Dify"].includes(providerClassName)) {
- // 显示serverUrl、apiKey、
- // transferToAgentTips、hangupTips、customerNoVoiceTips、openingRemarks
- container.append(_dynamicFieldsDiv["serverUrl"]);
- container.append(_dynamicFieldsDiv["apiKey"]);
- container.append(_dynamicFieldsDiv["transferToAgentTips"]);
- container.append(_dynamicFieldsDiv["hangupTips"]);
- container.append(_dynamicFieldsDiv["customerNoVoiceTips"]);
- container.append(_dynamicFieldsDiv["openingRemarks"]);
- }
- if (["JiutianWorkflow", "JiutianAgent"].includes(providerClassName)) {
- // 显示serverUrl、apiKey、
- // transferToAgentTips、hangupTips、customerNoVoiceTips、openingRemarks
- container.append(_dynamicFieldsDiv["serverUrl"]);
- container.append(_dynamicFieldsDiv["apiKey"]);
- container.append(_dynamicFieldsDiv["botId"]);
- container.append(_dynamicFieldsDiv["transferToAgentTips"]);
- container.append(_dynamicFieldsDiv["hangupTips"]);
- container.append(_dynamicFieldsDiv["customerNoVoiceTips"]);
- container.append(_dynamicFieldsDiv["openingRemarks"]);
- }
- }
- function updateTokenTypeSelect(selectedTokenType){
- console.log(selectedTokenType)
- if (selectedTokenType === "oauth") {
- $(".oauthFields").show();
- $(".patFields").hide();
- } else if (selectedTokenType === "pat") {
- $(".patFields").show();
- $(".oauthFields").hide();
- }
- }
- // 实时限制:只能输 0-200 的整数
- $('input[name="concurrentNum"]').on('input propertychange paste', function () {
- let v = this.value;
- // 去掉非数字
- v = v.replace(/[^0-9]/g, '');
- // 去掉前导 0
- v = v.replace(/^0+(\d)/, '$1');
- // 上限 200
- if (v > 200) v = 200;
- this.value = v;
- });
- // 自定义校验方法:0-200 整数
- $.validator.addMethod('range0_200', function (value, element) {
- return this.optional(element) || (/^\d+$/.test(value) && value >= 0 && value <= 200);
- }, '请输入 0-200 之间的整数');
- $.validator.addMethod('singleDigit', function (value, element) {
- return this.optional(element) || (/^[0-9]$/.test(value));
- }, '请输入单个数字 0-9');
- // LocalWavFile录音文件必填校验
- $.validator.addMethod('wavFileRequired', function (value, element) {
- var providerClassName = $("#providerClassNameSelect").val();
- if (providerClassName === "LocalWavFile") {
- return value && value.trim() !== "";
- }
- return true;
- }, '请上传录音文件');
- $("#form-account-edit").validate({
- focusCleanup: true,
- rules: {
- concurrentNum: {
- required: true,
- range0_200: true
- },
- transferManualDigit: {
- singleDigit: true
- }
- },
- messages: {
- concurrentNum: {
- required: '必填',
- range0_200: '请输入 0-200 之间的整数'
- },
- transferManualDigit: {
- singleDigit: '请输入单个数字 0-9'
- }
- }
- });
- function submitHandler() {
- if ($("#form-account-edit").valid()) {
- var providerClassName = $("#providerClassNameSelect").val();
- // LocalWavFile时必须上传录音文件
- if (providerClassName === "LocalWavFile") {
- var requiredWavFields = ['openingRemarksWav', 'customerNoVoiceTipsWav', 'hangupTipsWav', 'transferToAgentTipsWav'];
- var missingFields = [];
- requiredWavFields.forEach(function(fieldName) {
- var fieldValue = $('#' + fieldName).val();
- if (!fieldValue || fieldValue.trim() === "") {
- missingFields.push(fieldName);
- }
- });
- if (missingFields.length > 0) {
- var fieldNameMap = {
- 'openingRemarksWav': '开场白',
- 'customerNoVoiceTipsWav': '客户无声音提示',
- 'hangupTipsWav': '挂机提示',
- 'transferToAgentTipsWav': '转人工提示'
- };
- var missingNames = missingFields.map(function(f) { return fieldNameMap[f] || f; });
- top.layer.msg("以下录音文件必须上传:" + missingNames.join("、"), {icon: 2});
- return;
- }
- }
- // 收集动态字段
- var dynamicFields = {};
- $("#dynamicFieldsContainer").find("input, textarea, select").each(function() {
- var fieldName = $(this).attr("name");
- var fieldValue = $(this).val();
- if (fieldValue === undefined || fieldValue === null) {
- fieldValue = ""; // 如果未填写值,则传递空字符串
- }
- dynamicFields[fieldName] = fieldValue;
- });
- // 收集Wav字段(从隐藏的input中获取)
- var wavFieldMapping = {
- 'openingRemarksWav': $('#openingRemarksWav').val(),
- 'customerNoVoiceTipsWav': $('#customerNoVoiceTipsWav').val(),
- 'hangupTipsWav': $('#hangupTipsWav').val(),
- 'transferToAgentTipsWav': $('#transferToAgentTipsWav').val()
- };
- console.log($('#transferToAgentTipsWav').val())
- Object.keys(wavFieldMapping).forEach(function(fieldName) {
- var fieldValue = wavFieldMapping[fieldName];
- if (fieldValue === undefined || fieldValue === null) {
- fieldValue = "";
- }
- dynamicFields[fieldName] = fieldValue;
- console.log(fieldName)
- console.log(fieldValue)
- });
- // 如果 intentionTipsContainer 不显示,则强制清空 intentionTips
- if (_hideIntentionTipsContainer) {
- $("textarea[name='intentionTips']").val("");
- }
- // 将 JSON 字符串添加到表单数据中
- var formData = $('#form-account-edit').serializeArray();
- formData.push({"name": "accountJson", "value": JSON.stringify(dynamicFields)})
- $.operate.save(prefix + "/edit", formData);
- }
- }
- function toggleInterruptFields(value) {
- if (value === "1") {
- document.getElementById('interruptKeywordsContainer').style.display = 'block';
- document.getElementById('interruptIgnoreKeywordsContainer').style.display = 'block';
- } else {
- document.getElementById('interruptKeywordsContainer').style.display = 'none';
- document.getElementById('interruptIgnoreKeywordsContainer').style.display = 'none';
- }
- }
- function initDynamicFieldsDiv(_accountJson){
- let dynamicFieldsDiv = {};
- // serverUrl
- dynamicFieldsDiv["serverUrl"] = `<div class="form-group">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.serverUrl")}</label>
- <div class="col-sm-8">
- <input name="serverUrl" value="${_accountJson.serverUrl || ''}" class="form-control" type="text" required>
- </div>
- </div>`;
- // apiKey
- dynamicFieldsDiv["apiKey"] = `<div class="form-group">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.apiKey")}</label>
- <div class="col-sm-8">
- <input name="apiKey" value="${_accountJson.apiKey || ''}" class="form-control" type="text" required>
- </div>
- </div>`;
- // modelName
- dynamicFieldsDiv["modelName"] = `<div class="form-group">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.modelName")}</label>
- <div class="col-sm-8">
- <input name="modelName" value="${_accountJson.modelName || ''}" class="form-control" type="text" required>
- </div>
- </div>`;
- // botId
- dynamicFieldsDiv["botId"] = `<div class="form-group">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.botId")}</label>
- <div class="col-sm-8">
- <input name="botId" value="${_accountJson.botId || ''}" class="form-control" type="text" required>
- </div>
- </div>`;
- // tokenType
- dynamicFieldsDiv["tokenType"] = `<div class="form-group">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.tokenType")}</label>
- <div class="col-sm-8">
- <select name="tokenType" value="${_accountJson.tokenType || ''}" class="form-control" required id="tokenTypeSelect">
- <option value="oauth">oauth</option>
- <option value="pat">pat</option>
- </select>
- </div>
- </div>`;
- // tokenTypeFields
- dynamicFieldsDiv["tokenTypeFields"] = `<div id="tokenTypeFields">
- <div class="form-group oauthFields" style="display:none;">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.oauthClientId")}</label>
- <div class="col-sm-8">
- <input name="oauthClientId" value="${_accountJson.oauthClientId || ''}" class="form-control" type="text" required>
- </div>
- </div>
- <div class="form-group oauthFields" style="display:none;">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.oauthPrivateKey")}</label>
- <div class="col-sm-8">
- <input name="oauthPrivateKey" value="${_accountJson.oauthPrivateKey || ''}" class="form-control" type="text" required>
- </div>
- </div>
- <div class="form-group oauthFields" style="display:none;">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.oauthPublicKeyId")}</label>
- <div class="col-sm-8">
- <input name="oauthPublicKeyId" value="${_accountJson.oauthPublicKeyId || ''}" class="form-control" type="text" required>
- </div>
- </div>
- <div class="form-group patFields" style="display:none;">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.patToken")}</label>
- <div class="col-sm-8">
- <textarea name="patToken" class="form-control" rows="3" required>${_accountJson.patToken || ''}</textarea>
- </div>
- </div>
- </div>`;
- // llmTips
- dynamicFieldsDiv["llmTips"] = `<div class="form-group">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.llmTips")}</label>
- <div class="col-sm-8">
- <textarea name="llmTips" class="form-control" rows="30" required>${_accountJson.llmTips || ''}</textarea>
- </div>
- </div>`;
- // faqContext
- dynamicFieldsDiv["faqContext"] = `<div class="form-group">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.faqContext")}</label>
- <div class="col-sm-8">
- <textarea name="faqContext" class="form-control" rows="30" required>${_accountJson.faqContext || ''}</textarea>
- </div>
- </div>`;
- dynamicFieldsDiv["kbCatId"] = `<div class="form-group">
- <label class="col-sm-3 control-label">${i18n("llmAcount.form.kbCatId")}</label>
- <div class="col-sm-8">
- <select name="kbCatId" class="form-control" id="kbCatIdSelect">
- <option value="">${i18n("llmAcount.form.kbCatId.empty")}</option>
- </select>
- </div>
- </div>`;
- // transferToAgentTips
- dynamicFieldsDiv["transferToAgentTips"] = `<div class="form-group">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.transferToAgentTips")}</label>
- <div class="col-sm-8">
- <textarea name="transferToAgentTips" class="form-control" rows="3" required>${_accountJson.transferToAgentTips || ''}</textarea>
- </div>
- </div>`;
- // hangupTips
- dynamicFieldsDiv["hangupTips"] = `<div class="form-group">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.hangupTips")}</label>
- <div class="col-sm-8">
- <textarea name="hangupTips" class="form-control" rows="3" required>${_accountJson.hangupTips || ''}</textarea>
- </div>
- </div>`;
- // customerNoVoiceTips
- dynamicFieldsDiv["customerNoVoiceTips"] = `<div class="form-group">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.customerNoVoiceTips")}</label>
- <div class="col-sm-8">
- <textarea name="customerNoVoiceTips" class="form-control" rows="3" required>${_accountJson.customerNoVoiceTips || ''}</textarea>
- </div>
- </div>`;
- // openingRemarks
- dynamicFieldsDiv["openingRemarks"] = `<div class="form-group">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.openingRemarks")}</label>
- <div class="col-sm-8">
- <textarea name="openingRemarks" class="form-control" rows="3" required>${_accountJson.openingRemarks || ''}</textarea>
- </div>
- </div>`;
- // openingRemarksWav - 带录音上传按钮
- dynamicFieldsDiv["openingRemarksWav"] = `<div class="form-group">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.openingRemarks")}</label>
- <div class="col-sm-8">
- <div class="input-group">
- <textarea name="openingRemarks" class="form-control" rows="3" required>${_accountJson.openingRemarks || ''}</textarea>
- <input type="hidden" name="openingRemarksWav" value="${_accountJson.openingRemarksWav || ''}">
- <input type="hidden" name="openingRemarksWav" value="${_accountJson.openingRemarksWav || ''}">
- <span class="input-group-btn">
- <button type="button" class="btn btn-info btn-file-upload" data-field="openingRemarks" data-upload-type="voice" title="上传录音">
- <i class="fa fa-upload"></i> <span>上传</span>
- </button>
- </span>
- </div>
- <span class="help-block m-b-none">支持上传wav格式的录音文件</span>
- <input type="hidden" id="openingRemarksWavHidden" value="${_accountJson.openingRemarksWav || ''}">
- </div>
- </div>
- <div class="form-group openingRemarksAudioPreview" style="display:none;">
- <label class="col-sm-3 control-label">录音预览</label>
- <div class="col-sm-8">
- <audio controls class="form-control" style="height: 40px;">
- <source src="${_accountJson.openingRemarksFileUrl || ''}" type="audio/wav">
- 您的浏览器不支持音频播放。
- </audio>
- </div>
- </div>`;
- // customerNoVoiceTipsWav - 带录音上传按钮
- dynamicFieldsDiv["customerNoVoiceTipsWav"] = `<div class="form-group">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.customerNoVoiceTips")}</label>
- <div class="col-sm-8">
- <div class="input-group">
- <textarea name="customerNoVoiceTips" class="form-control" rows="3" required>${_accountJson.customerNoVoiceTips || ''}</textarea>
- <input type="hidden" name="customerNoVoiceTipsWav" value="${_accountJson.customerNoVoiceTipsWav || ''}">
- <input type="hidden" name="customerNoVoiceTipsWav" value="${_accountJson.customerNoVoiceTipsWav || ''}">
- <span class="input-group-btn">
- <button type="button" class="btn btn-info btn-file-upload" data-field="customerNoVoiceTips" data-upload-type="voice" title="上传录音">
- <i class="fa fa-upload"></i> <span>上传</span>
- </button>
- </span>
- </div>
- <span class="help-block m-b-none">支持上传wav格式的录音文件</span>
- <input type="hidden" id="customerNoVoiceTipsWavHidden" value="${_accountJson.customerNoVoiceTipsWav || ''}">
- </div>
- </div>
- <div class="form-group customerNoVoiceTipsAudioPreview" style="display:none;">
- <label class="col-sm-3 control-label">录音预览</label>
- <div class="col-sm-8">
- <audio controls class="form-control" style="height: 40px;">
- <source src="${_accountJson.customerNoVoiceTipsFileUrl || ''}" type="audio/wav">
- 您的浏览器不支持音频播放。
- </audio>
- </div>
- </div>`;
- // hangupTipsWav - 带录音上传按钮
- dynamicFieldsDiv["hangupTipsWav"] = `<div class="form-group">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.hangupTips")}</label>
- <div class="col-sm-8">
- <div class="input-group">
- <textarea name="hangupTips" class="form-control" rows="3" required>${_accountJson.hangupTips || ''}</textarea>
- <input type="hidden" name="hangupTipsWav" value="${_accountJson.hangupTipsWav || ''}">
- <input type="hidden" name="hangupTipsWav" value="${_accountJson.hangupTipsWav || ''}">
- <span class="input-group-btn">
- <button type="button" class="btn btn-info btn-file-upload" data-field="hangupTips" data-upload-type="voice" title="上传录音">
- <i class="fa fa-upload"></i> <span>上传</span>
- </button>
- </span>
- </div>
- <span class="help-block m-b-none">支持上传wav格式的录音文件</span>
- <input type="hidden" id="hangupTipsWavHidden" value="${_accountJson.hangupTipsWav || ''}">
- </div>
- </div>
- <div class="form-group hangupTipsAudioPreview" style="display:none;">
- <label class="col-sm-3 control-label">录音预览</label>
- <div class="col-sm-8">
- <audio controls class="form-control" style="height: 40px;">
- <source src="${_accountJson.hangupTipsFileUrlv || ''}" type="audio/wav">
- 您的浏览器不支持音频播放。
- </audio>
- </div>
- </div>`;
- // transferToAgentTipsWav - 带录音上传按钮
- dynamicFieldsDiv["transferToAgentTipsWav"] = `<div class="form-group">
- <label class="col-sm-3 control-label is-required">${i18n("llmAcount.form.transferToAgentTips")}</label>
- <div class="col-sm-8">
- <div class="input-group">
- <textarea name="transferToAgentTips" class="form-control" rows="3" required>${_accountJson.transferToAgentTips || ''}</textarea>
- <input type="hidden" name="transferToAgentTipsWav" value="${_accountJson.transferToAgentTipsWav || ''}">
- <input type="hidden" name="transferToAgentTipsWav" value="${_accountJson.transferToAgentTipsWav || ''}">
- <span class="input-group-btn">
- <button type="button" class="btn btn-info btn-file-upload" data-field="transferToAgentTips" data-upload-type="voice" title="上传录音">
- <i class="fa fa-upload"></i> <span>上传</span>
- </button>
- </span>
- </div>
- <span class="help-block m-b-none">支持上传wav格式的录音文件</span>
- <input type="hidden" id="transferToAgentTipsWavHidden" value="${_accountJson.transferToAgentTipsWav || ''}">
- </div>
- </div>
- <div class="form-group transferToAgentTipsAudioPreview" style="display:none;">
- <label class="col-sm-3 control-label">录音预览</label>
- <div class="col-sm-8">
- <audio controls class="form-control" style="height: 40px;">
- <source src="${_accountJson.transferToAgentTipsFileUrl || ''}" type="audio/wav">
- 您的浏览器不支持音频播放。
- </audio>
- </div>
- </div>`;
- return dynamicFieldsDiv;
- }
- // 加载知识库分类下拉框数据
- function loadKbCatOptions() {
- $.ajax({
- url: ctx + "aicall/kbcat/all",
- type: "GET",
- success: function(response) {
- if (response.code === 0 || response.code === 200) {
- var cats = response.data || [];
- var select = $("#kbCatIdSelect");
- select.empty();
- select.append('<option value="">请选择</option>');
- cats.forEach(function(item) {
- select.append($("<option>", {
- value: item.id,
- text: item.cat
- }));
- });
- // 回显已保存的值
- if (_accountJson && _accountJson.kbCatId) {
- select.val(_accountJson.kbCatId);
- }
- }
- },
- error: function(xhr, status, error) {
- console.error("获取知识库分类数据失败:", error);
- }
- });
- }
- // ========== 文件上传功能 ==========
- // 打开文件上传模态框
- function openUploadModal(fieldId, uploadType) {
- var modalHtml = `
- <div class="modal fade" id="uploadModal" tabindex="-1" role="dialog" aria-labelledby="uploadModalLabel">
- <div class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal" aria-label="Close">
- <span aria-hidden="true">×</span>
- </button>
- <h4 class="modal-title" id="uploadModalLabel">上传录音文件</h4>
- </div>
- <div class="modal-body">
- <form id="uploadForm" enctype="multipart/form-data">
- <div class="form-group">
- <label>选择文件</label>
- <input type="file" id="fileInput" name="file" accept=".wav"
- class="form-control" style="height:auto;">
- <span class="help-block">仅支持wav格式,最大50MB</span>
- </div>
- <div class="form-group" id="uploadProgress" style="display:none;">
- <label>上传进度</label>
- <div class="progress">
- <div class="progress-bar" role="progressbar" style="width: 0%;">
- 0%
- </div>
- </div>
- </div>
- </form>
- </div>
- <div class="modal-footer">
- <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
- <button type="button" class="btn btn-primary" id="confirmUpload">确认上传</button>
- </div>
- </div>
- </div>
- </div>
- `;
- $('#uploadModal').remove();
- $('body').append(modalHtml);
- var $modal = $('#uploadModal');
- var $fileInput = $('#fileInput');
- $modal.modal('show');
- $('#confirmUpload').off('click').on('click', function() {
- var file = $fileInput[0].files[0];
- if (!file) {
- top.layer.msg("请选择文件", {icon: 2});
- return;
- }
- if (!validateAudioFile(file)) {
- return;
- }
- var rootId = $('#rootId').val();
- uploadFileWithProgress(file, fieldId, rootId, function(success, result) {
- $modal.modal('hide');
- if (success) {
- // 更新对应的Wav隐藏字段(使用Hidden ID)
- var wavHiddenId = '#' + fieldId + 'WavHidden';
- $(wavHiddenId).val(result.filePath);
- $('#' + fieldId + 'Wav').val(result.filePath);
- // 显示音频预览
- showAudioPreview(fieldId, result.fileUrl);
- top.layer.msg("上传成功", {icon: 1});
- } else {
- top.layer.msg("上传失败:" + result, {icon: 2});
- }
- });
- });
- $modal.off('hidden.bs.modal').on('hidden.bs.modal', function() {
- $modal.remove();
- });
- }
- // 验证音频文件
- function validateAudioFile(file) {
- if (!file.name.toLowerCase().endsWith('.wav')) {
- top.layer.msg("仅支持wav格式文件", {icon: 2});
- return false;
- }
- var maxSize = 50 * 1024 * 1024; // 50MB
- if (file.size > maxSize) {
- top.layer.msg("文件大小不能超过50MB", {icon: 2});
- return false;
- }
- return true;
- }
- // 带进度条上传
- function uploadFileWithProgress(file, fieldId, rootId, callback) {
- var formData = new FormData();
- formData.append('file', file);
- formData.append('rootId', rootId);
- $('#uploadProgress').show();
- $.ajax({
- url: ctx + 'cc/ivr/uploadVoice', // 复用IVR的上传接口
- type: 'POST',
- data: formData,
- processData: false,
- contentType: false,
- xhr: function() {
- var xhr = $.ajaxSettings.xhr();
- if (xhr.upload) {
- xhr.upload.addEventListener('progress', function(e) {
- if (e.lengthComputable) {
- var percent = Math.floor((e.loaded / e.total) * 100);
- $('.progress-bar').css('width', percent + '%').text(percent + '%');
- }
- }, false);
- }
- return xhr;
- },
- success: function(rsp) {
- if (rsp.code === 0 || rsp.code === 200) {
- callback(true, rsp.data);
- } else {
- callback(false, rsp.msg);
- }
- },
- error: function(xhr, status, error) {
- callback(false, "网络错误:" + error);
- }
- });
- }
- // 显示音频预览
- function showAudioPreview(fieldId, fileUrl) {
- var previewClass = '.' + fieldId + 'AudioPreview';
- var $preview = $(previewClass);
- if ($preview.length && fileUrl) {
- $preview.find('audio source').attr('src', ctx + fileUrl);
- $preview.find('audio')[0].load();
- $preview.show();
- }
- }
- </script>
- </body>
- </html>
|