$(document).ready(function() {
initData();
fieldItemActions();
watchFieldTypeChange();
fieldOptionActions();
fieldOptionsChange();
fieldModalSave();
knowledgeLibItemActions();
formFieldChange();
initPromptEditor();
initMarkdown();
generateResult();
actionBackToList();
backModalActions();
actionSave();
actionPublish();
});
// 是否已保存
let isSaved = true;
// Markdown 组件
let markdownComponent = null;
// 表单数据管理
const FieldManager = {
state: {
// ID 列表
fields: [
// id,
],
// 数据存储
fieldsMap: {
// id: field,
},
},
setFields(fields) {
this.state.fields = [];
fields.forEach(field => {
this.state.fields
.push(field.id);
this.state.fieldsMap[field.id] = field;
});
renderList();
},
setFieldIds(fieldIds) {
this.state.fields = fieldIds;
renderList();
},
addField(field, afterId = null) {
// 生成 ID
const id = Date.now();
field.id = id;
const index = afterId
? this.state.fields.indexOf(afterId)
: -1;
if (index === -1) {
this.state.fields
.push(id);
} else {
this.state.fields
.splice(index + 1, 0, id);
}
this.state.fieldsMap[id] = field;
renderList();
},
updateField(field) {
const oldName = this.state.fieldsMap[field.id].name;
const newName = field.name;
this.state.fieldsMap[field.id] = field;
renderList();
if (oldName !== newName) {
updatePromptData({
action: 'update',
id: field.id,
oldName,
newName,
});
}
},
removeField(fieldId) {
const index = this.state.fields.indexOf(fieldId);
if (index > -1) {
const name = this.state.fieldsMap[fieldId].name;
this.state.fields.splice(index, 1);
delete this.state.fieldsMap[fieldId];
renderList();
updatePromptData({
action: 'delete',
id: fieldId,
name,
});
}
},
getField(id) {
return this.state.fieldsMap[id];
},
getFields() {
return this.state.fields
.map(id => this.state.fieldsMap[id]);
},
};
// Modal 数据管理
const ModalManager = {
state: {
action: '',
id: '',
type: '',
options: [],
},
setAction(action) {
this.state.action = action;
},
getAction() {
return this.state.action;
},
setId(id) {
this.state.id = id;
},
getId() {
return this.state.id;
},
setType(type) {
this.state.type = type;
renderOptions();
},
getType() {
return this.state.type;
},
setOptions(options, render = true) {
this.state.options = options;
if (render) {
renderOptions();
}
},
getOptions() {
return this.state.options;
},
setState(state) {
this.state = state;
renderOptions();
},
getState() {
return this.state;
},
};
// 数据初始化
function initData() {
let fields = [];
if (currentFields && currentFields.length > 0) {
fields = currentFields.map((field, index) => ({
...field,
options: field.options
? field.options.split(',')
: [],
}));
}
FieldManager.setFields(fields);
KnowledgeLibManager.setLibs(knowledgeLibs || []);
}
// 渲染列表
function renderList() {
const fields = FieldManager.getFields();
renderFieldList(fields);
renderForm(fields);
}
// 渲染字段列表占位符
function renderFieldListPlaceholder() {
return `
${fieldAdd}
${fieldAddTip}
`;
}
// 渲染字段项
function renderFieldItem(field) {
return `
${field.name}
${field.required === '1' ? '*' : ''}
${field.placeholder || ''}
`;
}
// 渲染字段列表
function renderFieldList(fields) {
const fieldList = $('#field-list');
fieldList.empty();
let html = '';
if (fields.length === 0) {
html = renderFieldListPlaceholder();
} else {
fields.forEach(field => {
html += renderFieldItem(field);
});
}
fieldList.html(html);
// 初始化拖动排序
fieldListSortable();
}
// 字段列表拖动排序
function fieldListSortable() {
if ($('#field-list').data('sortable-initialized')) {
return;
}
new zui.Sortable('#field-list', {
handle: '.field-item',
animation: 150,
ghostClass: 'bg-primary-pale',
onSort: function(event) {
const newOrder = [];
$('#field-list .field-item').each(function() {
const fieldId = $(this).data('id');
newOrder.push(fieldId);
});
FieldManager.setFieldIds(newOrder);
}
});
$('#field-list').data('sortable-initialized', true);
}
/* 知识库数据管理 */
const KnowledgeLibManager = {
state: {
libs: [],
libsMap: {},
action: '',
targetId: '',
},
setLibs(libs)
{
this.state.libs = [];
libs.forEach(lib => {
this.state.libs.push(lib.id);
this.state.libsMap[lib.id] = lib;
});
renderKnowledgeLibs();
},
setLibIds(libIds)
{
this.state.libs = libIds;
renderKnowledgeLibs();
},
addLib(lib, afterId = null)
{
const index = afterId ? this.state.libs.indexOf(afterId) : -1;
if(index === -1)
{
this.state.libs.push(lib.id);
}
else
{
this.state.libs.splice(index + 1, 0, lib.id);
}
this.state.libsMap[lib.id] = lib;
renderKnowledgeLibs();
},
updateLib(lib, targetId)
{
const index = this.state.libs.indexOf(targetId);
if(index === -1) return;
this.state.libs[index] = lib.id;
delete this.state.libsMap[targetId];
this.state.libsMap[lib.id] = lib;
renderKnowledgeLibs();
},
removeLib(libId)
{
const index = this.state.libs.indexOf(libId);
if(index > -1)
{
this.state.libs.splice(index, 1);
delete this.state.libsMap[libId];
renderKnowledgeLibs();
}
},
getLib(id)
{
return this.state.libsMap[id];
},
getLibs()
{
return this.state.libs.map(id => this.state.libsMap[id]);
},
setAction(action, targetId)
{
this.state.action = action;
this.state.targetId = targetId;
},
getAction()
{
const { action, targetId } = this.state;
return { action, targetId };
},
};
/* 渲染字段列表占位符 */
function renderKnowledgeLibsPlaceholder()
{
return `
${knowledgeLibAdd}
${knowledgeLibAddTip}
`;
}
/* 渲染字段项 */
function renderKnowledgeLibItem(lib)
{
const available = lib.published && !lib.deleted;
let badge = '';
if(!lib.published) badge = `${unpublished}`;
if(lib.deleted) badge = `${deleted}`;
return `
${lib.name}
${badge}
`;
}
/* 渲染知识库列表 */
function renderKnowledgeLibs()
{
const libList = $('#knowledge-libs');
libList.empty();
const libs = KnowledgeLibManager.getLibs();
let html = '';
if(libs.length === 0)
{
html = renderKnowledgeLibsPlaceholder();
}
else
{
libs.forEach(lib => {
html += renderKnowledgeLibItem(lib);
});
}
libList.html(html);
}
/* 知识库列表操作 */
function knowledgeLibItemActions()
{
$('#knowledge-libs')
.on('click', 'button', function()
{
const action = $(this).data('action');
const libId = $(this).closest('.field-item').data('id');
switch(action)
{
case 'add':
openKnowledgeLibModal('add', libId);
break;
case 'edit':
openKnowledgeLibModal('edit', libId);
break;
case 'delete':
confirmToDeleteKnowledgeLib(libId);
break;
default:
}
})
.on('click', '#add-knowledge-lib', function()
{
openKnowledgeLibModal('add', null);
});
}
/* 打开知识库 Modal */
function openKnowledgeLibModal(action, libId)
{
KnowledgeLibManager.setAction(action, libId);
const selectedID = KnowledgeLibManager.getLibs().map(lib => lib.id).join(',');
zui.Modal.open({
id: 'selectKnowledgeLibModal',
url: $.createLink('ai', 'selectKnowledgeLib', `selectedID=${selectedID}&callback=knowledgeLibOnSelect&multiple=0&showSelected=1${action === 'edit' ? '¤t=' + libId : ''}`),
size: 'sm',
});
}
/* 知识库选中回调 */
window.knowledgeLibOnSelect = function(libs)
{
const { action, targetId } = KnowledgeLibManager.getAction();
if(action === 'add')
{
KnowledgeLibManager.addLib(libs[0], targetId);
}
else if(action === 'edit')
{
KnowledgeLibManager.updateLib(libs[0], targetId);
}
};
/* 确认并删除知识库 */
function confirmToDeleteKnowledgeLib(libId)
{
zui.Modal.confirm(confirmDelete)
.then(confirmed => {
if(confirmed) KnowledgeLibManager.removeLib(libId);
});
}
// 渲染表单项
function renderFormField(field) {
let fieldHtml = '';
switch (field.type) {
case 'text':
fieldHtml = `
`;
break;
case 'textarea':
fieldHtml = `
`;
break;
case 'radio':
case 'checkbox':
if(field.options && field.options.length > 0)
{
const options = {
items: field.options.map(item => ({
text: item,
value: item,
})),
name: field.name,
limitValueInList: false,
multiple: field.type === 'checkbox',
required: field.required === '1',
};
const pickerOptions = JSON.stringify(options).replace(/"/g, '"');
const dataAttributes = [
`zui-create="picker"`,
`zui-create-picker="${pickerOptions}"`,
].join(' ');
fieldHtml = `
`;
}
break;
default:
return '';
}
return `
${fieldHtml}
`;
}
// 渲染表单
function renderForm(fields) {
const formFields = $('#form-fields');
formFields.empty();
let html = '';
if (fields.length > 0) {
fields.forEach(field => {
html += renderFormField(field);
});
}
formFields.html(html);
}
// 渲染选项
function renderOption(option, index) {
const disabledAttr = index === 0 ? 'disabled' : '';
return `
`;
}
// 渲染选项列表
function renderOptions() {
const type = ModalManager.getType();
const options = ModalManager.getOptions();
if (!['radio', 'checkbox'].includes(type)) {
$('#field-placeholder').removeClass('hidden');
$('#field-options').addClass('hidden');
return;
}
const optionsList = $('#field-options');
optionsList.empty();
let html = '';
options.forEach((option, index) => {
html += renderOption(option, index);
});
optionsList.html(html);
$('#field-placeholder').addClass('hidden');
$('#field-options').removeClass('hidden');
}
// 监听字段类型变化
function watchFieldTypeChange() {
$('#field-modal select[name="field-type"]')
.on('change', function() {
const type = $(this).val();
ModalManager.setType(type);
if (['radio', 'checkbox'].includes(type)) {
let options = ModalManager.getOptions();
if (!options || options.length === 0) {
options = ['', '', ''];
}
ModalManager.setOptions(options);
}
});
}
// 打开 Modal(添加/编辑字段)
function openModal(action, fieldId) {
$('#field-modal .modal-header span')
.text(action === 'add' ? fieldAdd : fieldEdit);
const field = FieldManager.getField(fieldId);
const type = action === 'add'
? 'text'
: field.type;
let options = action === 'add'
? ['', '', '']
: field.options;
if (['radio', 'checkbox'].includes(type)
&& (!options || options.length === 0)) {
options = ['', '', ''];
}
ModalManager.setState({
action,
id: fieldId,
type,
options,
});
$('#field-modal input[name="field-name"]')
.val(action === 'add' ? '' : field.name);
$('#field-modal select[name="field-type"]')
.val(type);
$('#field-modal input[name="field-placeholder"]')
.val(action === 'add' ? '' : field.placeholder);
$('#field-modal input[name="field-required"]')
.val(action === 'add' ? '1' : field.required);
zui.Modal.open({
id: 'field-modal',
});
}
// 确认并删除字段
function confirmToDeleteField(fieldId) {
zui.Modal.confirm(deleteTip)
.then(confirmed => {
if (confirmed) {
FieldManager.removeField(fieldId);
}
});
}
// 字段列表操作
function fieldItemActions() {
$('#field-list')
.on('click', 'button', function() {
const action = $(this).data('action');
const fieldId = $(this).closest('.field-item').data('id');
switch (action) {
case 'add':
openModal('add', fieldId);
break;
case 'edit':
openModal('edit', fieldId);
break;
case 'delete':
confirmToDeleteField(fieldId);
break;
default:
}
})
.on('click', '#add-field', function() {
openModal('add', null);
});
}
// 字段选项操作
function fieldOptionActions() {
$('#field-options')
.on('click', 'button', function() {
const action = $(this).data('action');
const index = $(this).closest('.input-group')
.data('index');
const options = ModalManager.getOptions();
if (action === 'add') {
options.splice(parseInt(index) + 1, 0, '');
} else {
options.splice(parseInt(index), 1);
}
ModalManager.setOptions(options);
});
}
// 字段选项变化
function fieldOptionsChange() {
$('#field-options')
.on('change', 'input', function() {
const options = ModalManager.getOptions();
const value = $(this).val();
const index = $(this).closest('.input-group')
.data('index');
options[parseInt(index)] = value;
ModalManager.setOptions(options, false);
});
}
// field Modal 数据验证
function validateFieldData(id, name, type, options) {
// 1. name 不能为空
if (!name.trim()) {
zui.Messager.show({
content: emptyWarning.replace('%s', fieldName),
type: 'danger',
});
return false;
}
const fields = FieldManager.getFields();
// 2. name 不能重复
if (fields.some(field => field.id !== id && field.name === name)) {
zui.Messager.show({
content: duplicatedWarning.replace('%s', fieldName),
type: 'danger',
});
return false;
}
// 3. options 不能为空
if (['radio', 'checkbox'].includes(type)) {
if (options.length === 0) {
zui.Messager.show({
content: emptyOptionWarning,
type: 'danger',
});
return false;
}
}
return true;
}
// field Modal 保存
function fieldModalSave() {
$('#field-modal-save')
.on('click', function() {
const fieldState = ModalManager.getState();
const name = $('#field-modal input[name="field-name"]')
.val().trim();
const placeholder = $('#field-modal input[name="field-placeholder"]')
.val();
const required = $('#field-modal input[name="field-required"]:checked')
.val();
const type = fieldState.type;
let options = [];
if (['radio', 'checkbox'].includes(type)) {
options = fieldState.options
.filter(option => option.trim());
}
const id = fieldState.action === 'add'
? null
: fieldState.id;
const valid = validateFieldData(id, name, type, options);
if (!valid) {
return;
}
const field = {
id,
name,
type,
required,
};
if (['radio', 'checkbox'].includes(type)) {
field.options = options;
} else {
field.placeholder = placeholder;
}
if (fieldState.action === 'add') {
FieldManager.addField(field, fieldState.id);
} else {
FieldManager.updateField(field);
}
zui.Modal.hide('#field-modal');
});
}
// 表单数据
const FormFieldData = {};
// 表单字段变化
function formFieldChange() {
$('#form-fields')
.on(
'change',
'input, textarea, select',
function() {
const fieldName = $(this).attr('name');
const fieldId = $(this).closest('.form-group')
.data('id');
const name = fieldName.endsWith('[]')
? fieldName.slice(0, -2)
: fieldName;
const value = $(this).val();
FormFieldData[fieldId] = value;
generatePromptPreview();
},
);
}
// 构建输入提示列表
function buildInputSuggestions(query = '') {
const list = FieldManager.getFields()
.map(field => ({
text: field.name,
value: `${field.name}>`,
}));
if (!query) {
return list;
}
return list.filter(item => item.text.includes(query));
}
// 处理 Prompt 文本
function processPromptText(text) {
if (!text) {
return '';
}
return text
.replace(/</g, '<')
.replace(/>/g, '>');
}
/* 将文本转换为 HTML */
function propmtTextToHtml(text)
{
let html = text.replace(//g, '>');
const fields = FieldManager.getFields();
const fieldNames = {};
fields.forEach(field => {
fieldNames[field.name] = field.id;
});
html = html.replace(/<([^&]+)>/g, function(match, fieldName)
{
if(!fieldNames[fieldName]) return match;
return `<${fieldName}>`;
});
const list = html.split('\n');
return list.map((line) => `${line}
`).join('');
}
// Prompt 初始值
const initPrompt = processPromptText(currentPrompt || '');
// Prompt Data
let promptData = {
editor: null,
text: initPrompt,
html: '',
};
// 初始化 Prompt Editor
function initPromptEditor() {
// 是否为初始化
let isInit = true;
promptData.html = propmtTextToHtml(initPrompt);
promptData.editor = new zui.TEditor('#prompt-editor', {
content: promptData.html,
mode: 'plain',
suggestions: {
'<': (query) => buildInputSuggestions(query),
},
onChange: function(content) {
promptData.text = content.replace(/\n\n/g, '\n');
promptData.html = promptData.editor.getHtml();
// 跳过初始化导致的改动
if (isInit) {
isInit = false;
} else {
isSaved = false;
}
generatePromptPreview();
},
});
generatePromptPreview();
}
// 更新 Prompt 数据
function updatePromptData(data) {
switch (data.action) {
case 'update':
promptData.text = promptData.text
.replace(new RegExp(`<${data.oldName}>`, 'g'), `<${data.newName}>`);
break;
case 'delete':
promptData.text = promptData.text
.replace(new RegExp(` <${data.name}> `, 'g'), '');
break;
default:
return;
}
isSaved = false;
promptData.html = propmtTextToHtml(promptData.text);
promptData.editor.setContent(promptData.html);
generatePromptPreview();
}
/* 生成 Prompt 预览 */
function generatePromptPreview()
{
const promptPreview = $('#prompt-preview');
promptPreview.empty();
let html = promptData.text.replace(//g, '>');
const fields = FieldManager.getFields();
fields.forEach(field => {
const { id, name, type } = field;
let value = `<${name}>`;
if(FormFieldData[id])
{
if(type === 'checkbox')
{
const options = FormFieldData[id].filter(item => item);
if(options.length > 0) value = options.join(',');
}
else
{
value = FormFieldData[id];
}
}
const list = html.split(`<${name}>`).map(item => item.trim());
html = list.join(`${value}`);
});
promptPreview.html(html);
}
// 初始化 Markdown 组件
function initMarkdown() {
markdownComponent = new zui.Markdown('#result-preview', {
content: '',
});
}
// 生成结果
function generateResult() {
$('#generate-result')
.on('click', async function() {
if($(this).hasClass('disabled')) return;
if (!hasZaiConfig) {
zui.Messager.show({content: zaiConfigHint, type: 'danger'});
return;
}
const promptPreview = $('#prompt-preview');
markdownComponent.render({
content: '',
});
const text = promptPreview.text();
if (!text) {
zui.Messager.show({
content: promptPlaceholder,
type: 'danger'
});
return;
}
$(this).addClass('disabled');
const res = await zui.AIPanel.shared.store
.postTempMessage({
content: text,
onResponse: (message) => {
if (message.content) {
markdownComponent.render({
content: message.content,
});
}
},
});
markdownComponent.render({
content: res.message.content,
});
$(this).removeClass('disabled');
});
}
// 返回列表操作
function actionBackToList() {
$('#btn-goback')
.on('click', function() {
if (isSaved) {
backToList();
} else {
zui.Modal.open({
id: 'back-modal',
});
}
});
}
// 返回列表
function backToList() {
openUrl($.createLink('ai', 'miniPrograms'));
}
// 返回 Modal 操作
function backModalActions() {
$('#back-modal .modal-footer')
.on('click', 'button', function() {
const action = $(this).data('action');
switch (action) {
case 'save':
saveData('0');
backToList();
break;
case 'cancel':
zui.Modal.hide('#back-modal');
break;
case 'discard':
backToList();
break;
default:
}
});
}
// 获取字段数据
function getFieldsData() {
const fields = FieldManager.getFields();
return fields.map((field, index) => {
const {
name,
type,
required,
placeholder,
options,
} = field;
const fieldData = {
name,
type,
required: required ? '1' : '0',
appID,
};
// 根据字段类型添加相应属性
if (['text', 'textarea'].includes(type)) {
fieldData.placeholder = placeholder || '';
} else if (['radio', 'checkbox'].includes(type)) {
fieldData.options = options ? options.join(',') : '';
}
return fieldData;
});
}
// 发送页面数据
function postData(data) {
$.ajax({
url: $.createLink('ai', 'configuredMiniProgram', `appID=${appID}`),
type: 'POST',
data: JSON.stringify(data),
processData: false,
contentType: 'application/json; charset=UTF-8',
dataType: 'json'
}).done(function(response) {
if (response.result === 'success') {
isSaved = true;
if (response.message) {
zui.Messager.show({
content: response.message,
type: 'success'
});
}
if (data.toPublish === '1') {
backToList();
}
} else {
if (response.message) {
zui.Messager.show({
content: response.message,
type: 'danger'
});
}
}
}).fail(function(xhr, status, error) {
zui.Messager.show({
content: saveFailed + ':' + error,
type: 'danger'
});
});
}
// 保存页面数据
function saveData(toPublish) {
// 准备相关数据
const fields = getFieldsData();
const prompt = promptData.text.trim();
const knowledgeLib = KnowledgeLibManager.getLibs().map(lib => lib.id).join(',');
// 数据验证
if (!prompt) {
zui.Messager.show({
content: promptPlaceholder,
type: 'danger'
});
return;
}
const data = {
fields,
prompt,
knowledgeLib,
toPublish,
};
if (toPublish === '0') {
postData(data);
return;
}
zui.Modal.confirm({
title: publishConfirm[0],
message: publishConfirm[1],
icon: 'icon-exclamation-sign',
iconClass: 'icon-2x modal-icon-warning',
})
.then(confirmed => {
if (confirmed) {
postData(data);
}
});
}
// 保存
function actionSave() {
$('#btn-save')
.on('click', function() {
saveData('0');
});
}
// 发布
function actionPublish() {
$('#btn-publish')
.on('click', function() {
saveData('1');
});
}