* add prompt preview.

This commit is contained in:
dingyongliang
2024-02-19 14:35:37 +08:00
parent cd5a3fa44b
commit 26b4cfd5ed
12 changed files with 484 additions and 128 deletions
+5 -4
View File
@@ -15941,8 +15941,9 @@ CREATE TABLE IF NOT EXISTS `zt_miniprogram` (
`createdDate` datetime NOT NULL,
`editedBy` varchar(30) NOT NULL,
`editedDate` datetime NOT NULL,
`tmp` enum('0','1') NOT NULL DEFAULT '1',
`publish` enum('0','1') NOT NULL DEFAULT '0',
`deleted` enum('0','1') NOT NULL DEFAULT '0',
`prompt` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
@@ -15950,9 +15951,9 @@ CREATE TABLE IF NOT EXISTS `zt_miniprogramfields` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`appid` mediumint(8) unsigned NOT NULL,
`name` varchar(30) NOT NULL,
`type` enum('radio', 'checkbox', 'input', 'template') DEFAULT 'input',
`value` text NOT NULL,
`optional` varchar(30) DEFAULT NULL,
`type` enum('radio', 'checkbox', 'text', 'textarea') DEFAULT 'text',
`placeholder` text DEFAULT NULL,
`options` text DEFAULT NULL,
`required` enum('0', '1') DEFAULT '1',
PRIMARY KEY (`id`),
FOREIGN KEY (`appid`) REFERENCES `zt_miniprogram`(`id`)
+11 -2
View File
@@ -155,13 +155,17 @@ class ai extends control
{
if(!empty($_POST))
{
$requiredFields = array('category' => $this->lang->ai->miniPrograms->category, 'model' => $this->lang->prompt->model, 'name' => $this->lang->prompt->name);
$toNext = $_POST['toNext'];
unset($_POST['toNext']);
$requiredFields = array('category' => $this->lang->ai->miniPrograms->category, 'model' => $this->lang->prompt->model, 'name' => $this->lang->prompt->name, 'desc' => $this->lang->ai->miniPrograms->desc);
$errors = $this->ai->verifyRequiredFields($requiredFields);
if($errors !== false) return $this->sendError($errors);
$isDuplicated = $this->ai->checkDuplicatedAppName($_POST['name']);
if($isDuplicated) return $this->sendError(array('name' => $this->lang->ai->miniPrograms->field->duplicatedNameTip));
$id = $this->ai->createTmpMiniProgram();
if($id !== false) return $this->sendSuccess(array('message' => $this->lang->saveSuccess, 'locate' => $this->createLink('ai', 'configuredMiniProgram', "appID=$id")));
if($id === false) return $this->sendError(array('message' => $this->lang->fail));
if($toNext === '1') return $this->sendSuccess(array('message' => $this->lang->saveSuccess, 'locate' => $this->createLink('ai', 'configuredMiniProgram', "appID=$id")));
return $this->sendSuccess(array('message' => $this->lang->saveSuccess));
}
$this->view->iconName = 'writinghand';
$this->view->iconTheme = 7;
@@ -179,7 +183,12 @@ class ai extends control
public function configuredMiniProgram($appID)
{
if(!is_numeric($appID)) return $this->locate('ai', 'createMiniProgram');
if(!empty($_POST))
{
$this->ai->saveMiniProgramFields($appID);
}
$this->view->title = $this->lang->ai->miniPrograms->common;
$this->view->appid = $appID;
$this->display();
}
+24 -1
View File
@@ -2,8 +2,31 @@
.field-configuration .drag-area > div {display: inline-flex; align-items: center; gap: 4px;}
.options-container {display: flex; flex-direction: column; gap: 16px;}
#mainContent {position: fixed; left: 20px; right: 20px; top: 70px; bottom: 0; display: flex; background: #f0f2f5; padding: 0;}
#mainContent {position: fixed; left: 20px; right: 20px; top: 70px; bottom: 56px; display: flex; background: #f0f2f5; padding: 0;}
#mainContent > div {height: 100%; background: #fff; display: flex; flex-direction: column;}
#mainContent > div > header {background: #f8f8fa; height: 40px; display: flex; align-items: center; padding-left: 24px;}
#mainContent > div > header > strong {font-size: 16px;}
#mainContent > div > main {flex-grow: 1; overflow-y: auto;}
.modal-body {padding-top: 0; padding-bottom: 0;}
.area-title {display: flex; align-items: center; padding-bottom: 20px; overflow: hidden;}
.area-title > strong {font-size: 14px; text-wrap: nowrap;}
.area-title > span {font-size: 12px;}
.area-title > i {margin-left: 16px; margin-right: 4px;}
.content-debug-area,
.prompt-design-area,
.prompt-preview-area,
.prompt-result-area,
.field-configuration-main {padding: 24px;}
.prompt-preview-area .preview-container,
.prompt-result-area .preview-container {border: 1px solid #D8DBDE; background: #f8f8f8; position: absolute; left: 24px; right: 24px; bottom: 24px; padding: 8px;}
.prompt-preview-area .preview-container {top: 64px;}
.prompt-result-area .preview-container {top: 34px;}
.table-form > tbody > tr > th {width: 80px; padding-left: 0; text-wrap: nowrap; overflow: hidden;}
.text-muted {flex-shrink: 1;}
+223 -35
View File
@@ -1,4 +1,9 @@
let $editingField = null;
let fieldIndex = 0;
/**
* @type {Map<string, string>}
*/
const words = new Map();
/**
* Handle the field type change events.
@@ -58,9 +63,9 @@ function handleRemoveOptionClick(event)
*/
function handleAddOptionClick(event)
{
const $newField = $(event.target).closest('.input-group').clone();
$newField.find('input').prop('value', '');
$(event.target).closest('.options-container').append($newField);
const template = document.getElementById('option-template');
const $clone = $(document.importNode(template.content, true));
$(event.target).closest('.options-container').append($clone);
fixOptionsOrder();
}
@@ -78,6 +83,7 @@ function revertOptions()
const $optionStr = $optionsContainer.html();
$optionsContainer.append($optionStr);
$optionsContainer.append($optionStr);
$optionsContainer.find('.input-group').first().find('button').last().attr('disabled', 'disabled');
$('.field-options')
.children('td')
@@ -199,11 +205,65 @@ function handleEditFieldClick(event)
function handleRemoveFieldClick(event)
{
const result = window.confirm(deleteTip);
if(result) $(event.target).closest('tr').remove();
const $button = $('.field-configuration-main > div');
const $table = $('.field-configuration-main > table');
if($button.hasClass('hidden')) $button.removeClass('hidden');
if(!$table.hasClass('hidden')) $table.addClass('hidden');
const $tr = $(event.target).closest('tr');
const fieldID = $tr.attr('data-id');
if(result) $tr.remove();
$('.field-content').find(`[data-id="${fieldID}"]`).remove();
if($('.field-configuration').children().length === 0) $('.field-configuration').parent().addClass('hidden').prev().removeClass('hidden');
words.delete($tr.find('th').prop('title'));
const innerHTML = $('#autocomplete-textarea').html();
$('#autocomplete-textarea').html(innerHTML.replace(/<strong class="text-primary">/g, '').replace(/<\/strong>/g, ''));
updatePromptPreview();
}
/**
* Check for duplicate field names.
*
* @param {string} name
* @returns {boolean} result
*/
const checkDuplicatedFieldName = (name) => Array.from(words.keys()).includes(name);
/**
* Show field name error if error exists.
*
* @param {*} $required
* @param {HTMLFormElement} formData
* @returns {boolean} result
*/
function showFieldNameErrorIfExist($required, form)
{
const $form = $(form);
const formData = new FormData(form);
const name = formData.get('field-name');
$form.find('.text-danger.help-text').remove();
$form.find('.has-error').removeClass('has-error');
if(!name)
{
$required
.addClass('has-error')
.append(`<div class="text-danger help-text">${emptyWarning}</div>`);
return true;
}
if(checkDuplicatedFieldName(name))
{
$required
.addClass('has-error')
.append(`<div class="text-danger help-text">${duplicatedWarning}</div>`);
return true;
}
if(['radio', 'checkbox'].includes(formData.get('field-type')) && formData.getAll('option[]').filter(x => !!x).length === 0)
{
$form.find('.options-container').children().first().addClass('has-error').after(`<div class="text-danger help-text">${emptyOptionWarning}</div>`);
return true;
}
return false;
}
/**
@@ -213,25 +273,35 @@ function handleRemoveFieldClick(event)
*/
function handleSaveFieldClick()
{
const formData = new FormData(document.querySelector('#add-field-modal form'));
const form = document.querySelector('#add-field-modal form');
const formData = new FormData(form);
const name = formData.get('field-name');
if(!name)
{
const template = document.getElementById('text-danger-template');
const $clone = $(document.importNode(template.content, true));
$('#add-field-modal input[name="field-name"]')
.closest('.required')
.append($clone);
return;
}
const $required = $('#add-field-modal input[name="field-name"]').closest('.required');
if(showFieldNameErrorIfExist($required, form)) return;
$('#add-field-modal').modal('hide');
const $fieldView = createFieldView(formData);
$fieldView.attr('data-id', `field-${++fieldIndex}`);
$('#sortable-list').append($fieldView);
$fieldView.find('.picker').addClass('disabled');
const $button = $('.field-configuration-main > div');
const $table = $('.field-configuration-main > table');
if(!$button.hasClass('hidden')) $button.addClass('hidden');
if($table.hasClass('hidden')) $table.removeClass('hidden');
const $fieldViewClone = createFieldView(formData);
$fieldViewClone.attr('data-id', $fieldView.attr('data-id'));
$fieldViewClone.children().last().remove();
$fieldViewClone.find('.drag-area').removeClass('darg-area');
$fieldViewClone.removeClass('srotable-item');
$fieldViewClone.find('.icon-move').remove();
$fieldViewClone.find('[readonly]').removeAttr('readonly');
$fieldViewClone.find('.field-type').on('change', updatePromptPreview);
$('.field-content').append($fieldViewClone);
words.set(name, $fieldView.attr('data-id'));
}
/**
@@ -241,21 +311,31 @@ function handleSaveFieldClick()
*/
function handleSaveEditedFieldClick()
{
const formData = new FormData(document.querySelector('#edit-field-modal form'));
const oldName = $editingField.find('.field-name').html();
const form = document.querySelector('#edit-field-modal form');
const formData = new FormData(form);
const name = formData.get('field-name');
if(!name)
{
const template = document.getElementById('text-danger-template');
const $clone = $(document.importNode(template.content, true));
$('#edit-field-modal input[name="field-name"]')
.closest('.required')
.append($clone);
return;
}
const $required = $('#edit-field-modal input[name="field-name"]').closest('.required');
if(showFieldNameErrorIfExist($required, form)) return;
$('#edit-field-modal').modal('hide');
const $fieldView = createFieldView(formData);
$fieldView.attr('data-id', $editingField.attr('data-id'));
$editingField.replaceWith($fieldView);
$fieldView.find('.picker').addClass('disabled');
const $fieldViewClone = createFieldView(formData);
$fieldViewClone.attr('data-id', $fieldView.attr('data-id'));
$fieldViewClone.children().last().remove();
$fieldViewClone.children().first().removeClass('darg-area');
$fieldViewClone.find('.icon-move').remove();
$fieldViewClone.find('[readonly]').removeAttr('readonly');
$fieldViewClone.find('.field-type').on('change', updatePromptPreview);
$(`.field-content [data-id="${$editingField.attr('data-id')}"]`).replaceWith($fieldViewClone);
words.delete(oldName);
words.set(name, $fieldView.attr('data-id'));
}
/**
@@ -282,7 +362,7 @@ function createFieldView(formData)
function createFieldNameView(formData)
{
const name = formData.get('field-name');
return $(`<th class="drag-area">
return $(`<th class="drag-area" title="${name}">
<div>
<i class="icon icon-move"></i>
<span class="field-name">${name}</span>
@@ -300,24 +380,24 @@ function createFieldTypeView(formData)
{
const type = formData.get('field-type');
const required = formData.get('required') === '1';
const $td = $('<td></td>');
const $td = $(`<td data-type="${type}"></td>`);
if(required) $td.addClass('required');
if(type === 'text')
{
const placeholder = formData.get('placeholder') || pleaseInput;
$td.append(`<input type="text" class="form-control field-type" placeholder="${placeholder}" />`);
$td.append(`<input type="text" class="form-control field-type" placeholder="${placeholder}" readonly />`);
return $td;
}
if(type === 'textarea')
{
const placeholder = formData.get('placeholder') || pleaseInput;
$td.append(`<textarea class="form-control field-type" placeholder="${placeholder}" />`);
$td.append(`<textarea class="form-control field-type" placeholder="${placeholder}" readonly />`);
return $td;
}
const options = formData.getAll('option[]');
const $picker = $(`<div class="picker"><input type="text"></div>`);
const $picker = $(`<div class="picker"><input type="text" class="field-type" /></div>`);
if(type === 'checkbox') $picker.attr('data-multi', 'true');
$picker.picker({list: options.map(option => ({text: option, value: option}))});
$picker.attr('data-options', options.join(','));
@@ -332,7 +412,7 @@ function createFieldTypeView(formData)
*/
function createFieldButtons()
{
return $(`<td>
return $(`<td style="width: 100px;">
<button type="button" class="btn btn-link btn-sm btn-icon" onclick="handleAddFieldClick()">
<i class="icon icon-plus"></i>
</button>
@@ -345,4 +425,112 @@ function createFieldButtons()
</td>`);
}
$('#sortable-list').sortable({selector: '.sortable-item', trigger: '.drag-area'});
/**
* format prompt input string.
*
* @param {string} content prompt content.
* @returns {string} result.
*/
function formatContent(content)
{
content = content.replace(/ /g, '&nbsp;');
if(!content.includes('&nbsp;&lt;')) return false;
Array.from(words.keys()).forEach(word => {content = content.replace(new RegExp(`&nbsp;&lt;${word}&gt;&nbsp;`, 'g'), `&nbsp;<strong class="text-primary">&lt;${word}&gt;</strong>&nbsp;`);});
return content;
}
function updatePromptPreview()
{
const regex = /<strong class="text-primary">&lt;([^>]+)&gt;<\/strong>&nbsp;/g;
let $clone = $('#autocomplete-textarea').clone();
let innerHTML = $clone.html();
const fmtStr = formatContent(innerHTML);
if(typeof fmtStr === 'string')
{
$('#autocomplete-textarea').html(fmtStr);
$clone = $('#autocomplete-textarea').clone();
innerHTML = $clone.html();
}
if(!innerHTML)
{
$('.prompt-preview-area .preview-container').html(innerHTML);
return;
}
const matches = $clone.html().match(regex);
if(!matches) return;
for (let i = 0; i < matches.length; i++)
{
const result = regex.exec($clone.html())[1];
const fieldIndex = words.get(result);
const fieldValue = $(`.field-content [data-id="${fieldIndex}"] .field-type`).prop('value');
if(!fieldValue) continue;
innerHTML = innerHTML.replace(new RegExp(`&lt;${result}&gt;`, 'g'), fieldValue);
}
innerHTML = innerHTML.replace(/&nbsp;/g, ' ')
.replace(/ <strong class="text-primary">/g, '<strong class="text-primary">')
.replace(/<\/strong> /g, '</strong>');
$('.prompt-preview-area .preview-container').html(innerHTML);
}
/**
* Save mini program fields.
*
* @param {'0'|'1'} toPublish
* @returns {void}
*/
function saveMiniProgram(toPublish)
{
const fields = [];
$('.field-content').children().each(function()
{
const field = {};
field.name = $(this).find('.field-name').html();
field.type = $(this).find('td').attr('data-type');
field.required = $(this).find('td').hasClass('required') ? '1' : '0'
field.appid = appid;
if(['text', 'textarea'].includes(field.type)) field.placeholder = $(this).find('.field-type').prop('placeholder');
else field.options = $(this).find('.picker').attr('data-options').split(',');
fields.push(field);
});
const prompt = $('#autocomplete-textarea').html();
$.post(createLink('ai', 'configuredMiniProgram', `appID=${appid}`), {fields, prompt, toPublish}).done(console.log);
}
$(function()
{
$('#sortable-list').sortable(
{
selector: '.sortable-item',
trigger: '.drag-area',
finish: function(e)
{
const $draggedElm = e.element;
const $fieldContent = $('.field-content');
const selector = `[data-id="${$draggedElm.attr('data-id')}"]`;
const prevSelector = `[data-id="${$draggedElm.prev().attr('data-id')}"]`;
const index = $draggedElm.index();
const $draggedElmContent = $fieldContent.find(selector);
if(index === 0) $fieldContent.prepend($draggedElmContent);
else $draggedElmContent.insertAfter($fieldContent.find(prevSelector));
},
});
$('#autocomplete-textarea').textcomplete(
[{
match: /(\s+)(\<\S*)$/,
search(term, callback)
{
callback(Array.from(words.keys()).filter(word => `<${word}>`.indexOf(term) === 0));
},
replace(word)
{
word = `<strong class="text-primary">&lt;${word}&gt;</strong>`;
return `$1${word} `;
}
}]);
$('#autocomplete-textarea').on('blur', updatePromptPreview);
});
+18 -3
View File
@@ -36,6 +36,21 @@ function handleSaveButtonClick()
$('input[name="iconName"]').prop('value', selectedIcon);
}
$('#theme-buttons button').on('click', handleThemeButtonClick);
$('#icon-buttons svg').on('click', handleIconButtonClick);
$('#save-icon-button').on('click', handleSaveButtonClick);
function handleSaveMiniProgramClick()
{
$('[name="toNext"]').prop('value', '0');
}
function handleNextStepClick()
{
$('[name="toNext"]').prop('value', '1');
}
$(function()
{
$('#theme-buttons button').on('click', handleThemeButtonClick);
$('#icon-buttons svg').on('click', handleIconButtonClick);
$('#save-icon-button').on('click', handleSaveButtonClick);
$('#save-miniprogram').on('click', handleSaveMiniProgramClick);
$('#next-step').on('click', handleNextStepClick);
});
+34 -22
View File
@@ -414,37 +414,49 @@ $lang->ai->miniPrograms->category = 'Category';
$lang->ai->miniPrograms->icon = 'Icon';
$lang->ai->miniPrograms->desc = 'Introduction';
$lang->ai->miniPrograms->categoryList = array('writing' => 'writing', 'statistics' => 'statistics', 'creative' => 'creative', 'raiders' => 'Guideline ', 'schedule' => 'schedule');
$lang->ai->miniPrograms->modelList = array(' openai-GPT35 '=> 'openai/GPT-3.5');
$lang->ai->miniPrograms->iconModification = 'Icon modification ';
$lang->ai->miniPrograms->customBackground = 'Custom background Color ';
$lang->ai->miniPrograms->modelList = array('openai-GPT35 '=> 'openai/GPT-3.5');
$lang->ai->miniPrograms->iconModification = 'Icon modification';
$lang->ai->miniPrograms->customBackground = 'Custom background Color';
$lang->ai->miniPrograms->customIcon = 'Custom icon';
$lang->ai->miniPrograms->backToListPage = 'Return to list page';
$lang->ai->miniPrograms->lastStep = 'Previous step';
$lang->ai->miniPrograms->placeholder = new stdClass();
$lang->ai->miniPrograms->placeholder->name = 'Please enter a small program name ';
$lang->ai->miniPrograms->placeholder->desc = 'Please enter a brief introduction to the miniprograms ';
$lang->ai->miniPrograms->placeholder->name = 'Please enter a small program name';
$lang->ai->miniPrograms->placeholder->desc = 'Please enter a brief introduction to the miniprograms';
$lang->ai->miniPrograms->placeholder->default = 'Please fill in the prompt, the default is "please enter"';
$lang->ai->miniPrograms->placeholder->input = 'Please enter ';
$lang->ai->miniPrograms->placeholder->input = 'Please enter';
$lang->ai->miniPrograms->deleteTip = 'Are you sure to delete this field? ';
$lang->ai->miniPrograms->field = new stdClass();
$lang->ai->miniPrograms->field->name = 'Field name ';
$lang->ai->miniPrograms->field->name = 'Field name';
$lang->ai->miniPrograms->field->duplicatedNameTip = 'This name is already used, please try another name';
$lang->ai->miniPrograms->field->type = 'control type ';
$lang->ai->miniPrograms->field->typeList = array(' single-line text ', 'multi-line text ',' single selection ', 'multi-selection ');
$lang->ai->miniPrograms->field->placeholder = 'Fill hints ';
$lang->ai->miniPrograms->field->required = 'Required ';
$lang->ai->miniPrograms->field->isRequired = 'Yes ';
$lang->ai->miniPrograms->field->isNotRequired = 'No ';
$lang->ai->miniPrograms->field->add = 'New field ';
$lang->ai->miniPrograms->field->addTitle = 'New field ';
$lang->ai->miniPrograms->field->addTip = 'Please click here to add field information ';
$lang->ai->miniPrograms->field->edit = 'Edit field ';
$lang->ai->miniPrograms->field->configuration = 'Field Configuration area ';
$lang->ai->miniPrograms->field->debug = 'debug area ';
$lang->ai->miniPrograms->field->preview = 'Preview area ';
$lang->ai->miniPrograms->field->option = 'Options ';
$lang->ai->miniPrograms->field->emptyNameWarning = 'Name cannot be empty. ';
$lang->ai->miniPrograms->field->type = 'control type';
$lang->ai->miniPrograms->field->typeList = array('single-line text', 'multi-line text','single selection', 'multi-selection');
$lang->ai->miniPrograms->field->placeholder = 'Fill hints';
$lang->ai->miniPrograms->field->required = 'Required';
$lang->ai->miniPrograms->field->isRequired = 'Yes';
$lang->ai->miniPrograms->field->isNotRequired = 'No';
$lang->ai->miniPrograms->field->add = 'New field';
$lang->ai->miniPrograms->field->addTitle = 'New field';
$lang->ai->miniPrograms->field->addTip = 'Please click here to add field information';
$lang->ai->miniPrograms->field->edit = 'Edit field';
$lang->ai->miniPrograms->field->configuration = 'Field Configuration area';
$lang->ai->miniPrograms->field->debug = 'debug area';
$lang->ai->miniPrograms->field->preview = 'Preview area';
$lang->ai->miniPrograms->field->option = 'Options';
$lang->ai->miniPrograms->field->contentDebugging = 'Content debugging';
$lang->ai->miniPrograms->field->contentDebuggingTip = 'Please enter the field here to debug.';
$lang->ai->miniPrograms->field->prompterDesign = 'Prompterdesign';
$lang->ai->miniPrograms->field->prompterDesignTip = 'The <> symbol is used to reference the configured field. Space is used before and after <>.';
$lang->ai->miniPrograms->field->prompterPreview = 'Prompterpreview';
$lang->ai->miniPrograms->field->generateResult = 'Generate result';
$lang->ai->miniPrograms->field->resultPreview = 'Result Preview';
$lang->ai->miniPrograms->field->emptyNameWarning = 'Field name cannot be empty';
$lang->ai->miniPrograms->field->duplicatedNameWarning = 'Duplicate field name';
$lang->ai->miniPrograms->field->emptyOptionWarning = 'Please configure at least one option';
$lang->ai->models = new stdclass();
$lang->ai->models->title = 'Language Model Configuration';
+34 -22
View File
@@ -415,36 +415,48 @@ $lang->ai->miniPrograms->icon = 'Icon';
$lang->ai->miniPrograms->desc = 'Introduction';
$lang->ai->miniPrograms->categoryList = array('writing' => 'writing', 'statistics' => 'statistics', 'creative' => 'creative', 'raiders' => 'Guideline ', 'schedule' => 'schedule');
$lang->ai->miniPrograms->modelList = array(' openai-GPT35 '=> 'openai/GPT-3.5');
$lang->ai->miniPrograms->iconModification = 'Icon modification ';
$lang->ai->miniPrograms->customBackground = 'Custom background Color ';
$lang->ai->miniPrograms->iconModification = 'Icon modification';
$lang->ai->miniPrograms->customBackground = 'Custom background Color';
$lang->ai->miniPrograms->customIcon = 'Custom icon';
$lang->ai->miniPrograms->backToListPage = 'Return to list page';
$lang->ai->miniPrograms->lastStep = 'Previous step';
$lang->ai->miniPrograms->placeholder = new stdClass();
$lang->ai->miniPrograms->placeholder->name = 'Please enter a small program name ';
$lang->ai->miniPrograms->placeholder->desc = 'Please enter a brief introduction to the miniprograms ';
$lang->ai->miniPrograms->placeholder->name = 'Please enter a small program name';
$lang->ai->miniPrograms->placeholder->desc = 'Please enter a brief introduction to the miniprograms';
$lang->ai->miniPrograms->placeholder->default = 'Please fill in the prompt, the default is "please enter"';
$lang->ai->miniPrograms->placeholder->input = 'Please enter ';
$lang->ai->miniPrograms->placeholder->input = 'Please enter';
$lang->ai->miniPrograms->deleteTip = 'Are you sure to delete this field? ';
$lang->ai->miniPrograms->deleteTip = 'Are you sure to delete this field?';
$lang->ai->miniPrograms->field = new stdClass();
$lang->ai->miniPrograms->field->name = 'Field name ';
$lang->ai->miniPrograms->field->name = 'Field name';
$lang->ai->miniPrograms->field->duplicatedNameTip = 'This name is already used, please try another name';
$lang->ai->miniPrograms->field->type = 'control type ';
$lang->ai->miniPrograms->field->typeList = array(' single-line text ', 'multi-line text ',' single selection ', 'multi-selection ');
$lang->ai->miniPrograms->field->placeholder = 'Fill hints ';
$lang->ai->miniPrograms->field->required = 'Required ';
$lang->ai->miniPrograms->field->isRequired = 'Yes ';
$lang->ai->miniPrograms->field->isNotRequired = 'No ';
$lang->ai->miniPrograms->field->add = 'New field ';
$lang->ai->miniPrograms->field->addTitle = 'New field ';
$lang->ai->miniPrograms->field->addTip = 'Please click here to add field information ';
$lang->ai->miniPrograms->field->edit = 'Edit field ';
$lang->ai->miniPrograms->field->configuration = 'Field Configuration area ';
$lang->ai->miniPrograms->field->debug = 'debug area ';
$lang->ai->miniPrograms->field->preview = 'Preview area ';
$lang->ai->miniPrograms->field->option = 'Options ';
$lang->ai->miniPrograms->field->emptyNameWarning = 'Name cannot be empty. ';
$lang->ai->miniPrograms->field->type = 'control type';
$lang->ai->miniPrograms->field->typeList = array('single-line text', 'multi-line text', 'single selection', 'multi-selection');
$lang->ai->miniPrograms->field->placeholder = 'Fill hints';
$lang->ai->miniPrograms->field->required = 'Required';
$lang->ai->miniPrograms->field->isRequired = 'Yes';
$lang->ai->miniPrograms->field->isNotRequired = 'No';
$lang->ai->miniPrograms->field->add = 'New field';
$lang->ai->miniPrograms->field->addTitle = 'New field';
$lang->ai->miniPrograms->field->addTip = 'Please click here to add field information';
$lang->ai->miniPrograms->field->edit = 'Edit field';
$lang->ai->miniPrograms->field->configuration = 'Field Configuration area';
$lang->ai->miniPrograms->field->debug = 'debug area';
$lang->ai->miniPrograms->field->preview = 'Preview area';
$lang->ai->miniPrograms->field->option = 'Options';
$lang->ai->miniPrograms->field->contentDebugging = 'Content debugging';
$lang->ai->miniPrograms->field->contentDebuggingTip = 'Please enter the field here to debug.';
$lang->ai->miniPrograms->field->prompterDesign = 'Prompterdesign';
$lang->ai->miniPrograms->field->prompterDesignTip = 'The <> symbol is used to reference the configured field. Space is used before and after <>.';
$lang->ai->miniPrograms->field->prompterPreview = 'Prompterpreview';
$lang->ai->miniPrograms->field->generateResult = 'Generate result';
$lang->ai->miniPrograms->field->resultPreview = 'Result Preview';
$lang->ai->miniPrograms->field->emptyNameWarning = 'Field name cannot be empty';
$lang->ai->miniPrograms->field->duplicatedNameWarning = 'Duplicate field name';
$lang->ai->miniPrograms->field->emptyOptionWarning = 'Please configure at least one option';
$lang->ai->models = new stdclass();
$lang->ai->models->title = 'Language Model Configuration';
+35 -23
View File
@@ -414,37 +414,49 @@ $lang->ai->miniPrograms->category = 'Category';
$lang->ai->miniPrograms->icon = 'Icon';
$lang->ai->miniPrograms->desc = 'Introduction';
$lang->ai->miniPrograms->categoryList = array('writing' => 'writing', 'statistics' => 'statistics', 'creative' => 'creative', 'raiders' => 'Guideline ', 'schedule' => 'schedule');
$lang->ai->miniPrograms->modelList = array(' openai-GPT35 '=> 'openai/GPT-3.5');
$lang->ai->miniPrograms->iconModification = 'Icon modification ';
$lang->ai->miniPrograms->customBackground = 'Custom background Color ';
$lang->ai->miniPrograms->modelList = array('openai-GPT35 ' => 'openai/GPT-3.5');
$lang->ai->miniPrograms->iconModification = 'Icon modification';
$lang->ai->miniPrograms->customBackground = 'Custom background Color';
$lang->ai->miniPrograms->customIcon = 'Custom icon';
$lang->ai->miniPrograms->backToListPage = 'Return to list page';
$lang->ai->miniPrograms->lastStep = 'Previous step';
$lang->ai->miniPrograms->placeholder = new stdClass();
$lang->ai->miniPrograms->placeholder->name = 'Please enter a small program name ';
$lang->ai->miniPrograms->placeholder->desc = 'Please enter a brief introduction to the miniprograms ';
$lang->ai->miniPrograms->placeholder->name = 'Please enter a small program name';
$lang->ai->miniPrograms->placeholder->desc = 'Please enter a brief introduction to the miniprograms';
$lang->ai->miniPrograms->placeholder->default = 'Please fill in the prompt, the default is "please enter"';
$lang->ai->miniPrograms->placeholder->input = 'Please enter ';
$lang->ai->miniPrograms->placeholder->input = 'Please enter';
$lang->ai->miniPrograms->deleteTip = 'Are you sure to delete this field? ';
$lang->ai->miniPrograms->deleteTip = 'Are you sure to delete this field?';
$lang->ai->miniPrograms->field = new stdClass();
$lang->ai->miniPrograms->field->name = 'Field name ';
$lang->ai->miniPrograms->field->name = 'Field name';
$lang->ai->miniPrograms->field->duplicatedNameTip = 'This name is already used, please try another name';
$lang->ai->miniPrograms->field->type = 'control type ';
$lang->ai->miniPrograms->field->typeList = array(' single-line text ', 'multi-line text ',' single selection ', 'multi-selection ');
$lang->ai->miniPrograms->field->placeholder = 'Fill hints ';
$lang->ai->miniPrograms->field->required = 'Required ';
$lang->ai->miniPrograms->field->isRequired = 'Yes ';
$lang->ai->miniPrograms->field->isNotRequired = 'No ';
$lang->ai->miniPrograms->field->add = 'New field ';
$lang->ai->miniPrograms->field->addTitle = 'New field ';
$lang->ai->miniPrograms->field->addTip = 'Please click here to add field information ';
$lang->ai->miniPrograms->field->edit = 'Edit field ';
$lang->ai->miniPrograms->field->configuration = 'Field Configuration area ';
$lang->ai->miniPrograms->field->debug = 'debug area ';
$lang->ai->miniPrograms->field->preview = 'Preview area ';
$lang->ai->miniPrograms->field->option = 'Options ';
$lang->ai->miniPrograms->field->emptyNameWarning = 'Name cannot be empty. ';
$lang->ai->miniPrograms->field->type = 'control type';
$lang->ai->miniPrograms->field->typeList = array('single-line text', 'multi-line text', 'single selection', 'multi-selection');
$lang->ai->miniPrograms->field->placeholder = 'Fill hints';
$lang->ai->miniPrograms->field->required = 'Required';
$lang->ai->miniPrograms->field->isRequired = 'Yes';
$lang->ai->miniPrograms->field->isNotRequired = 'No';
$lang->ai->miniPrograms->field->add = 'New field';
$lang->ai->miniPrograms->field->addTitle = 'New field';
$lang->ai->miniPrograms->field->addTip = 'Please click here to add field information';
$lang->ai->miniPrograms->field->edit = 'Edit field';
$lang->ai->miniPrograms->field->configuration = 'Field Configuration area';
$lang->ai->miniPrograms->field->debug = 'debug area';
$lang->ai->miniPrograms->field->preview = 'Preview area';
$lang->ai->miniPrograms->field->option = 'Options';
$lang->ai->miniPrograms->field->contentDebugging = 'Content debugging';
$lang->ai->miniPrograms->field->contentDebuggingTip = 'Please enter the field here to debug.';
$lang->ai->miniPrograms->field->prompterDesign = 'Prompterdesign';
$lang->ai->miniPrograms->field->prompterDesignTip = 'The <> symbol is used to reference the configured field. Space is used before and after <>.';
$lang->ai->miniPrograms->field->prompterPreview = 'Prompterpreview';
$lang->ai->miniPrograms->field->generateResult = 'Generate result';
$lang->ai->miniPrograms->field->resultPreview = 'Result Preview';
$lang->ai->miniPrograms->field->emptyNameWarning = 'Field name cannot be empty';
$lang->ai->miniPrograms->field->duplicatedNameWarning = 'Duplicate field name';
$lang->ai->miniPrograms->field->emptyOptionWarning = 'Please configure at least one option';
$lang->ai->models = new stdclass();
$lang->ai->models->title = 'Language Model Configuration';
+13 -1
View File
@@ -418,6 +418,8 @@ $lang->ai->miniPrograms->modelList = array('openai-gpt35' => 'OpenAI / G
$lang->ai->miniPrograms->iconModification = '图标修改';
$lang->ai->miniPrograms->customBackground = '自定义背景色';
$lang->ai->miniPrograms->customIcon = '自定义icon';
$lang->ai->miniPrograms->backToListPage = '返回列表页';
$lang->ai->miniPrograms->lastStep = '上一步';
$lang->ai->miniPrograms->placeholder = new stdClass();
$lang->ai->miniPrograms->placeholder->name = '请输入小程序名称';
@@ -444,7 +446,17 @@ $lang->ai->miniPrograms->field->configuration = '字段配置区';
$lang->ai->miniPrograms->field->debug = '调试区';
$lang->ai->miniPrograms->field->preview = '预览区';
$lang->ai->miniPrograms->field->option = '选项';
$lang->ai->miniPrograms->field->emptyNameWarning = '『名称』不能为空。';
$lang->ai->miniPrograms->field->contentDebugging = '内容调试';
$lang->ai->miniPrograms->field->contentDebuggingTip = '请在此处输入字段进行调试。';
$lang->ai->miniPrograms->field->prompterDesign = '提词设计';
$lang->ai->miniPrograms->field->prompterDesignTip = '输入“<>”符号可引用已配置的字段,“<>”前后采用空格进行间隔。';
$lang->ai->miniPrograms->field->prompterPreview = '提词预览';
$lang->ai->miniPrograms->field->generateResult = '生成结果';
$lang->ai->miniPrograms->field->resultPreview = '结果预览';
$lang->ai->miniPrograms->field->emptyNameWarning = '『字段名称』不能为空';
$lang->ai->miniPrograms->field->duplicatedNameWarning = '『字段名称』重复';
$lang->ai->miniPrograms->field->emptyOptionWarning = '请至少配置一个选项';
$lang->ai->models = new stdclass();
$lang->ai->models->title = '语言模型配置';
+18
View File
@@ -327,6 +327,24 @@ class aiModel extends model
return $this->dao->lastInsertID();
}
public function saveMiniProgramFields($appID)
{
$data = fixer::input('post')->get();
$this->dao->update(TABLE_MINIPROGRAM)
->set('prompt')->eq($data->prompt)
->set('publish')->eq($data->toPublish)
->where('id')->eq($appID)
->exec();
$fields = $data->fields;
foreach ($fields as $field)
{
$this->dao->insert(TABLE_MINIPROGRAMFIELDS)
->data($field)
->exec();
}
}
/**
* Check for duplicate names of Mini programs.
*
+60 -12
View File
@@ -13,15 +13,14 @@
<?php include '../../common/view/header.html.php'; ?>
<?php
js::set('pleaseInput', $lang->ai->miniPrograms->placeholder->input);
js::set('deleteTip', $lang->ai->miniPrograms->deleteTip);
js::set('pleaseInput', $lang->ai->miniPrograms->placeholder->input);
js::set('deleteTip', $lang->ai->miniPrograms->deleteTip);
js::set('emptyWarning', $lang->ai->miniPrograms->field->emptyNameWarning);
js::set('duplicatedWarning', $lang->ai->miniPrograms->field->duplicatedNameWarning);
js::set('emptyOptionWarning', $lang->ai->miniPrograms->field->emptyOptionWarning);
js::set('appid', $appid);
?>
<template id="text-danger-template">
<div id="nameLabel" class="text-danger help-text"><?php echo $lang->ai->miniPrograms->field->emptyNameWarning; ?></div>
</template>
<template id="option-template">
<div class="input-group">
<span class="input-group-addon"><?php echo $lang->ai->miniPrograms->field->option; ?>1</span>
@@ -87,7 +86,7 @@
</div>
<div class="modal-body" style="display: flex; gap: 42px; padding-right: 36px;"></div>
<div class="modal-footer" style="display: flex; justify-content: center; border-top: none;">
<button type="button" class="btn btn-wide btn-primary" id="save-add-field-button" onclick="handleSaveFieldClick()"><?php echo $lang->save; ?></button>
<button type="button" class="btn btn-wide btn-primary" onclick="handleSaveFieldClick()"><?php echo $lang->save; ?></button>
</div>
</div>
</div>
@@ -101,7 +100,7 @@
</div>
<div class="modal-body" style="display: flex; gap: 42px; padding-right: 36px;"></div>
<div class="modal-footer" style="display: flex; justify-content: center; border-top: none;">
<button type="button" class="btn btn-wide btn-primary" id="save-add-field-button" onclick="handleSaveEditedFieldClick()"><?php echo $lang->save; ?></button>
<button type="button" class="btn btn-wide btn-primary" onclick="handleSaveEditedFieldClick()"><?php echo $lang->save; ?></button>
</div>
</div>
</div>
@@ -113,7 +112,7 @@
</header>
<main class="field-configuration-main">
<div>
<a onclick="handleAddFieldClick()" style="border: 1px dashed #D8DBDE; border-radius: 2px; margin: 25px 72px; display: flex; align-items: center; flex-direction: column; padding: 12px; gap: 4px;">
<a onclick="handleAddFieldClick()" style="border: 1px dashed #D8DBDE; border-radius: 2px; display: flex; align-items: center; flex-direction: column; padding: 12px; gap: 4px;">
<div style="color: #2E7FFF; display: flex; align-items: center; gap: 4px;"><i class="icon icon-plus"></i><span><?php echo $lang->ai->miniPrograms->field->addTitle; ?></span></div>
<div style="color: #9EA3B0; font-size: 12divx;"><?php echo $lang->ai->miniPrograms->field->addTip; ?></div>
</a>
@@ -128,7 +127,25 @@
<strong><?php echo $lang->ai->miniPrograms->field->debug; ?></strong>
</header>
<main>
<div class="content-debug-area" style="min-height: 50%;">
<div class="area-title">
<strong><?php echo $lang->ai->miniPrograms->field->contentDebugging; ?></strong>
<i title="帮助" class="icon icon-help text-warning"></i>
<span class="text-muted"><?php echo $lang->ai->miniPrograms->field->contentDebuggingTip; ?></span>
</div>
<table class="table table-form">
<tbody class="field-content"></tbody>
</table>
</div>
<div class="prompt-design-area" style="height: 50%; position: relative; padding-top: 0;">
<div class="area-title">
<strong><?php echo $lang->ai->miniPrograms->field->prompterDesign; ?></strong>
<i title="帮助" class="icon icon-help text-warning"></i>
<span class="text-muted"><?php echo $lang->ai->miniPrograms->field->prompterDesignTip; ?></span>
</div>
<div class="form-control" id="autocomplete-textarea" contenteditable="true" style="overflow-y: auto; position: absolute; top: 32px; left: 24px; right: 24px; bottom: 24px; height: auto; width: auto;"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.textcomplete/1.8.5/jquery.textcomplete.min.js"></script>
</div>
</main>
</div>
<div style="flex-basis: 30%;">
@@ -136,8 +153,39 @@
<strong><?php echo $lang->ai->miniPrograms->field->preview; ?></strong>
</header>
<main>
<div class="prompt-preview-area" style="height: 50%; position: relative;">
<div class="area-title" style="display: flex; justify-content: space-between;">
<strong><?php echo $lang->ai->miniPrograms->field->prompterPreview; ?></strong>
<button class="btn btn-link" style="display: flex; align-items: center; gap: 4px; color: #2E7FFF;">
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_669_15822)">
<path d="M12.5418 1.45837L8.66266 12.5417L6.446 7.55421L1.4585 5.33754L12.5418 1.45837Z" stroke="#2E7FFF" stroke-linejoin="round" />
<path d="M12.5416 1.45837L6.4458 7.55421" stroke="#2E7FFF" stroke-linecap="round" stroke-linejoin="round" />
</g>
<defs>
<clipPath id="clip0_669_15822">
<rect width="14" height="14" fill="white" />
</clipPath>
</defs>
</svg>
<?php echo $lang->ai->miniPrograms->field->generateResult; ?>
</button>
</div>
<div class="preview-container"></div>
</div>
<div class="prompt-result-area" style="height: 50%; position: relative; padding-top: 0;">
<div class="area-title">
<strong><?php echo $lang->ai->miniPrograms->field->resultPreview; ?></strong>
</div>
<div class="preview-container"></div>
</div>
</main>
</div>
</div>
<footer style="display: flex; justify-content: center; align-items: center; height: 56px; background: #fff; border-top: 1px solid #eff1f7; position: fixed; bottom: 0; left: 20px; right: 20px; gap: 24px;">
<a href="<?php echo $this->createLink('ai', 'miniPrograms'); ?>" class="btn btn-wide"><?php echo $lang->ai->miniPrograms->backToListPage; ?></a>
<a href="<?php echo $this->createLink('ai', 'createMiniProgram'); ?>" class="btn btn-wide"><?php echo $lang->ai->miniPrograms->lastStep; ?></a>
<a class="btn btn-wide btn-secondary" onclick="saveMiniProgram('0')"><?php echo $lang->save; ?></a>
<a class="btn btn-wide btn-primary" onclick="saveMiniProgram('1')"><?php echo $lang->ai->prompts->action->publish; ?></a>
</footer>
<?php include '../../common/view/footer.html.php'; ?>
+9 -3
View File
@@ -44,7 +44,13 @@
<tr>
<th><?php echo $lang->ai->miniPrograms->desc; ?></th>
<td colspan="2">
<?php echo html::input('desc', $introduction, "class='form-control' placeholder='" . $lang->ai->miniPrograms->placeholder->desc . "'"); ?>
<?php echo html::input('desc', $desc, "class='form-control' required placeholder='" . $lang->ai->miniPrograms->placeholder->desc . "'"); ?>
</td>
</tr>
<tr class="hidden">
<th></th>
<td colspan="2">
<input type="text" name="toNext" />
</td>
</tr>
<tr>
@@ -67,8 +73,8 @@
</tr>
<div style="position: fixed; left: 0; right: 0; bottom: 32px; display: flex; justify-content: center; gap: 24px;">
<?php echo html::a($this->createLink('ai', 'miniPrograms'), $lang->goback, '', "class='btn btn-back btn-wide'"); ?>
<button class="btn btn-wide btn-secondary"><?php echo $lang->save; ?></button>
<button class="btn btn-wide btn-primary" type="submit"><?php echo $lang->ai->nextStep; ?></button>
<button class="btn btn-wide btn-secondary" type="submit" id="save-miniprogram"><?php echo $lang->save; ?></button>
<button class="btn btn-wide btn-primary" type="submit" id="next-step"><?php echo $lang->ai->nextStep; ?></button>
</div>
</table>
</form>