1733 lines
68 KiB
JavaScript
1733 lines
68 KiB
JavaScript
/**
|
|
* Save fields to server
|
|
*
|
|
* @param {boolean} [includeAll] if set to true then export all shown or hidden fields
|
|
* @param {function} [callback] Callback for save complete
|
|
* @param {boolean} [force] Force save and ignore changes status
|
|
* @return {Object[]} Field objects list
|
|
*/
|
|
function saveFields(includeAll, callback, force)
|
|
{
|
|
if(typeof includeAll === 'function')
|
|
{
|
|
callback = includeAll;
|
|
includeAll = false;
|
|
}
|
|
|
|
var fieldsList = [];
|
|
var changedFields = [];
|
|
var revertChanges = function()
|
|
{
|
|
for(var i = 0; i < changedFields.length; ++i)
|
|
{
|
|
changedFields[i].changed = true;
|
|
}
|
|
};
|
|
var errorsCount = 0;
|
|
for(var i = 0; i < fields.length; ++i)
|
|
{
|
|
var originField = fields[i];
|
|
|
|
if(originField.changed)
|
|
{
|
|
originField.changed = false;
|
|
changedFields.push(originField);
|
|
}
|
|
|
|
if(!includeAll && originField.show !== '1') continue;
|
|
|
|
var field = $.extend({}, originField);
|
|
|
|
if(originField.errors && originField.errors.$length) errorsCount++;
|
|
|
|
field.order = i + 1;
|
|
if(field.id[0] === '_') delete field.id;
|
|
if(field.type === 'decimal')
|
|
{
|
|
field.length = field.integerDigits + ',' + field.decimalDigits;
|
|
}
|
|
delete field.unsaved;
|
|
delete field._options;
|
|
delete field.errors;
|
|
delete field.changed;
|
|
delete field.rules;
|
|
delete field.optionClass;
|
|
if($.isArray(field.options))
|
|
{
|
|
var options = {};
|
|
for(var j = 0; j < field.options.length; ++j)
|
|
{
|
|
var option = field.options[j];
|
|
if(option.value) options[option.value] = option.text;
|
|
}
|
|
field.options = options;
|
|
}
|
|
if($.isArray(field.layoutRules))
|
|
{
|
|
field.layoutRules = field.layoutRules.join(',');
|
|
}
|
|
fieldsList.push(field);
|
|
}
|
|
|
|
if(!force && !changedFields.length) return;
|
|
|
|
if(errorsCount)
|
|
{
|
|
revertChanges();
|
|
$('#editor').addClass('highlight-errors');
|
|
setTimeout(function()
|
|
{
|
|
$('#editor').removeClass('highlight-errors');
|
|
}, 5000);
|
|
return new $.zui.Messager(window.validateMessages.failSummary.replace('%s', errorsCount),
|
|
{
|
|
type: 'danger',
|
|
icon: 'bell',
|
|
placement: 'center',
|
|
}).show();
|
|
}
|
|
|
|
var link = createLink('workflow', 'ui', 'module=' + window.flowModule + '&action=' + window.action);
|
|
$.post(link, {fields: JSON.stringify(fieldsList)}, function(response)
|
|
{
|
|
try {response = JSON.parse(response);}
|
|
catch(error)
|
|
{
|
|
response = response ? response : window.unknownError;
|
|
new $.zui.Messager(response,
|
|
{
|
|
type: 'danger',
|
|
icon: 'bell',
|
|
placement: 'center',
|
|
time: 10000
|
|
}).show();
|
|
revertChanges();
|
|
if(callback) callback(false, response);
|
|
return;
|
|
}
|
|
if(response.result == 'fail')
|
|
{
|
|
if(typeof response.message === 'string') bootbox.alert(response.message);
|
|
else if($.isPlainObject(response.message))
|
|
{
|
|
var errors = {};
|
|
if(response.field) errors[response.field] = response.message;
|
|
else errors = response.message;
|
|
window.updateFieldsErrors(errors);
|
|
}
|
|
|
|
revertChanges();
|
|
if(callback) callback(false, response);
|
|
}
|
|
else
|
|
{
|
|
$('#saveBtn').popover(
|
|
{
|
|
trigger: 'manual',
|
|
content: response.message,
|
|
tipClass: 'popover-success popover-form-result',
|
|
placement: 'left'
|
|
}).popover('show');
|
|
|
|
setTimeout(function(){$('#saveBtn').popover('destroy')}, 2000);
|
|
|
|
if(callback) callback(true, response);
|
|
}
|
|
});
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get default options list of specific option type
|
|
*
|
|
* @param {Object} field field object
|
|
* @param {Function} callback callback
|
|
* @return {Object} return default options key-value with a object
|
|
*/
|
|
function getDefaultsOfOption(field, callback)
|
|
{
|
|
/* Fetch default options from server, then call callback on ready */
|
|
var sql = window.btoa(encodeURI(field.sql));
|
|
var control = window.btoa(encodeURI(field.control));
|
|
var optionType = field.options;
|
|
var type = field.type;
|
|
var value = window.btoa(encodeURI(field.defaultValue));
|
|
var link = createLink('workflowfield', 'ajaxGetDefaultControl', 'mode=quick&control=' + control + '&optionType=' + optionType + '&type=' + type + '&sql=' + sql + '&sqlVars=&elementName=&default=' + value);
|
|
$.getJSON(link, callback);
|
|
}
|
|
|
|
function initDefault(field, isCustomOptions)
|
|
{
|
|
var $fieldDefaultValue = $('#fieldDefaultValue');
|
|
var chosen = $fieldDefaultValue.data('chosen');
|
|
var picker = $fieldDefaultValue.data('zui.picker');
|
|
|
|
if($fieldDefaultValue.find('option[value=ajax_search_more]').length == 1)
|
|
{
|
|
$fieldDefaultValue.find('option[value=ajax_search_more]').remove();
|
|
|
|
if(chosen) chosen.destroy();
|
|
|
|
var control = window.btoa(encodeURI(field.control));
|
|
var optionType = field.options;
|
|
var type = field.type;
|
|
var sql = '';
|
|
var url = createLink('workflowfield', 'ajaxGetMoreDefault', 'mode=advanced&control=' + control + '&optionType=' + optionType + '&type=' + type + '&sql=' + sql + '&sqlVars=&search={search}');
|
|
|
|
initPicker($('#fieldDefaultValue'), {remote: url});
|
|
}
|
|
else
|
|
{
|
|
if(picker) picker.destroy();
|
|
if(chosen)
|
|
{
|
|
$fieldDefaultValue.trigger('chosen:updated');
|
|
}
|
|
else
|
|
{
|
|
$fieldDefaultValue.chosen({allow_single_deselect: true});
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get default field properties by control type
|
|
*
|
|
* @param {String} controlType
|
|
* @return {Object}
|
|
*/
|
|
function getDefaultFieldByControl(controlType)
|
|
{
|
|
var field =
|
|
{
|
|
type: fieldDefault.type,
|
|
optionClass: fieldDefault.optionClass,
|
|
integerDigits: fieldDefault.integerDigits,
|
|
decimalDigits: fieldDefault.decimalDigits,
|
|
};
|
|
switch(controlType)
|
|
{
|
|
case 'label' :
|
|
field.type = 'mediumint';
|
|
field.optionClass = 'integer';
|
|
break;
|
|
case 'formula' :
|
|
case 'decimal' :
|
|
field.type = 'decimal';
|
|
field.optionClass = 'decimal';
|
|
break;
|
|
case 'integer' :
|
|
field.type = 'int';
|
|
field.optionClass = 'integer';
|
|
break;
|
|
case 'multi-select' :
|
|
case 'checkbox' :
|
|
case 'textarea' :
|
|
case 'richtext' :
|
|
field.type = 'text';
|
|
field.optionClass = 'text';
|
|
break;
|
|
case 'date' :
|
|
field.type = 'date';
|
|
field.optionClass = 'date';
|
|
break;
|
|
case 'datetime' :
|
|
field.type = 'datetime';
|
|
field.optionClass = 'time';
|
|
break;
|
|
}
|
|
return field;
|
|
}
|
|
|
|
function removeOptionError(field)
|
|
{
|
|
if(field.errors.options)
|
|
{
|
|
delete field.errors.options;
|
|
field.errors.$length --;
|
|
$('#field-' + field.id).toggleClass('has-error', !!(field.errors && field.errors.$length))
|
|
$('#fieldOptionsMessage').hide();;
|
|
}
|
|
}
|
|
|
|
/* https://tc39.github.io/ecma262/#sec-array.prototype.findIndex */
|
|
if (!Array.prototype.findIndex)
|
|
{
|
|
Object.defineProperty(Array.prototype, 'findIndex',
|
|
{
|
|
value: function(predicate)
|
|
{
|
|
if(this == null) throw new TypeError('"this" is null or not defined');
|
|
|
|
var o = Object(this);
|
|
var len = o.length >>> 0;
|
|
|
|
if(typeof predicate !== 'function') throw new TypeError('predicate must be a function');
|
|
|
|
var thisArg = arguments[1];
|
|
var k = 0;
|
|
while(k < len)
|
|
{
|
|
var kValue = o[k];
|
|
if(predicate.call(thisArg, kValue, k, o)) return k;
|
|
k++;
|
|
}
|
|
return -1;
|
|
}
|
|
});
|
|
}
|
|
|
|
$(function()
|
|
{
|
|
var isForm = uiMode === 'form';
|
|
var isView = uiMode === 'view';
|
|
var isBatchForm = uiMode === 'batchForm';
|
|
var canSetWidth = uiMode === 'browse' || isBatchForm;
|
|
var canSetPosition = canSetWidth || uiMode === 'view';
|
|
var $previewBox = $('#editorPreview');
|
|
var $preview = $(isForm ? '#uiPreview' : isView ? '#uiViewWrapper' : '#uiListHeader');
|
|
var $infoPreview = isView ? $('#uiInfoViewPreview') : null;
|
|
var $basicPreview = isView ? $('#uiBasicViewPreview') : null;
|
|
var $listPreview = canSetWidth ? $('#uiListPreviewItems') : null;
|
|
var $filedEditForm = $('#filedEditForm');
|
|
var remoteOptions = window.remoteOptions = fieldDefaultOptions || {};
|
|
var hasActionsInList;
|
|
var activedField;
|
|
|
|
/* Format options to standart, like [{text: 'Today', value: 'today'}, ...] */
|
|
var formatOptions = function(options)
|
|
{
|
|
if(typeof options === 'string')
|
|
{
|
|
options = options.split(',');
|
|
}
|
|
if($.isArray(options) || $.isPlainObject(options))
|
|
{
|
|
var optionsList = [];
|
|
$.each(options, function(value, text)
|
|
{
|
|
optionsList[text.length ? 'push' : 'unshift']({value: value, text: text});
|
|
});
|
|
options = optionsList;
|
|
}
|
|
return options;
|
|
};
|
|
|
|
/* Get keys of a object */
|
|
var getObjectKeys = function(obj, filter)
|
|
{
|
|
var keys = [];
|
|
$.each(obj, function(key)
|
|
{
|
|
if(filter && !filter(key)) return;
|
|
keys.push(key);
|
|
});
|
|
return keys;
|
|
};
|
|
|
|
/* Get field index in list by id or field code */
|
|
var getFieldIndex = function(id)
|
|
{
|
|
if(typeof id === 'number') id = String(id);
|
|
return fields.findIndex(function(field)
|
|
{
|
|
return field.id === id || field.field === id;
|
|
});
|
|
};
|
|
|
|
/* Get field object by id or field code */
|
|
var getField = function(id)
|
|
{
|
|
var index = getFieldIndex(id);
|
|
return index > -1 ? fields[index] : null;
|
|
};
|
|
|
|
/* Check whether the field is buildin */
|
|
var isBuildInField = function(field)
|
|
{
|
|
return field.buildin === '1' || defaultFields[field.field];
|
|
};
|
|
|
|
/* Get field select control type, return 'multi', 'single' or false */
|
|
var getFieldSelectType = function(field)
|
|
{
|
|
if(isBuildInField(field) && field.field !== 'status') return false;
|
|
if(field.control === 'multi-select' || field.control === 'checkbox') return field.options !== 'prevModule' ? 'multi' : 'text';
|
|
else if(field.control === 'select' || field.control === 'radio') return field.options !== 'prevModule' ? 'single' : 'text';
|
|
return false;
|
|
};
|
|
|
|
/* Create preview control element for form mode */
|
|
var createFieldControl = function(field, onlyHtml)
|
|
{
|
|
var controlType = field.control;
|
|
var html = '';
|
|
switch(controlType)
|
|
{
|
|
case 'label':
|
|
html = '<p class="form-control-static"></p>';
|
|
break;
|
|
case 'textarea':
|
|
case 'richtext':
|
|
html = '<textarea class="form-control" rows="' + (isBatchForm ? 1 : 3) + '" style="resize:' + (isBatchForm ? 'none' : 'vertical') + '"></textarea>';
|
|
break;
|
|
case 'select':
|
|
html = '<select class="form-control"></select>';
|
|
break;
|
|
case 'multi-select':
|
|
html = '<select class="form-control" multiple></select>';
|
|
break;
|
|
case 'checkbox':
|
|
html = '<div class="checkbox-list"></div>';
|
|
break;
|
|
case 'radio':
|
|
html = '<div class="radio-list"></div>';
|
|
break;
|
|
case 'file':
|
|
html = '<div class="file-input-list"><div class="file-input-empty"><button type="button" class="btn btn-link file-input-btn"><i class="icon icon-plus"></i> ' + window.langAddFile + '</button></div></div>';
|
|
break;
|
|
default:
|
|
html = '<input type="text" class="form-control">';
|
|
}
|
|
return onlyHtml ? html : $(html);
|
|
};
|
|
|
|
/* Update field preview control for form mode */
|
|
var updateFieldControl = function($wrapper, field)
|
|
{
|
|
if(field.field === 'file') return;
|
|
var controlType = field.control;
|
|
var readonly = field.readonly;
|
|
var defaultValue = field.defaultValue;
|
|
switch(controlType)
|
|
{
|
|
case 'label':
|
|
$wrapper.find('.form-control-static').text(defaultValue);
|
|
break;
|
|
case 'textarea':
|
|
case 'richtext':
|
|
$wrapper.find('textarea').val(defaultValue);
|
|
break;
|
|
case 'select':
|
|
case 'multi-select':
|
|
var $select = $wrapper.find('select');
|
|
$select.empty();
|
|
defaultValue = ',' + defaultValue + ',';
|
|
if(typeof field.options === 'object')
|
|
{
|
|
$.each(field.options, function(_, option)
|
|
{
|
|
var $option = $('<option></option>').attr('value', option.value).text(option.text);
|
|
if(defaultValue.indexOf(',' + option.value + ',') != -1) $option = $option.prop('selected', 'selected');
|
|
$select.append($option);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
if(field.optionsData)
|
|
{
|
|
$.each(field.optionsData, function(value, text)
|
|
{
|
|
var $option = $('<option></option>').attr('value', value).text(text);
|
|
if(defaultValue.indexOf(',' + value + ',') != -1) $option = $option.prop('selected', 'selected');
|
|
$select.append($option);
|
|
});
|
|
}
|
|
}
|
|
if(!$select.data('chosen')) $select.chosen();
|
|
else $select.trigger('chosen:updated');
|
|
break;
|
|
case 'checkbox':
|
|
case 'radio':
|
|
var $list = $wrapper.find('.' + controlType + '-list');
|
|
$list.empty();
|
|
var name = 'field-' + controlType + '-' + field.id;
|
|
var options = typeof field.options === 'object' ? field.options : [{value: '__example', text: ''}];
|
|
$.each(options, function(_, option)
|
|
{
|
|
var $label = $('<label class="'+ controlType + '-inline"></label>');
|
|
var $option = $('<input type="' + controlType + '">').attr({name: name, value: option.value});
|
|
$label.append($option).append($('<span></span>').text(option.text));
|
|
if(option.value === '__example') $label.find('span').addClass('example-text-holder');
|
|
$list.append($label);
|
|
});
|
|
break;
|
|
case 'date':
|
|
var $input = $wrapper.find('.form-control');
|
|
if(defaultValue == 'currentTime') defaultValue = window.currentDate;
|
|
$input.val(defaultValue).attr('readonly', readonly === '1' ? 'readonly' : null);
|
|
if(!$input.data('datetimepicker')) $input.datepicker();
|
|
break;
|
|
case 'datetime':
|
|
var $input = $wrapper.find('.form-control');
|
|
if(defaultValue == 'currentTime') defaultValue = window.currentTime;
|
|
$input.val(defaultValue).attr('readonly', readonly === '1' ? 'readonly' : null);
|
|
if(!$input.data('datetimepicker')) $input.datetimepicker();
|
|
break;
|
|
default:
|
|
$wrapper.find('.form-control').val(defaultValue);
|
|
}
|
|
};
|
|
|
|
/* Update field detail view item for view mode */
|
|
var updateFieldView = function($wrapper, field)
|
|
{
|
|
var $label = $wrapper.find('.field-label');
|
|
$label.text(field.name);
|
|
if(field.position !== $wrapper.closest('.view-dropbox').data('position'))
|
|
{
|
|
var $viewBox = field.position === 'basic' ? $basicPreview : $infoPreview;
|
|
var inserted = false;
|
|
$viewBox.find('.field-view-item').each(function()
|
|
{
|
|
var $field = $(this);
|
|
if(field.order >= $field.data('order'))
|
|
{
|
|
$field.after($wrapper);
|
|
inserted = true;
|
|
return false;
|
|
}
|
|
});
|
|
if(!inserted) $viewBox.append($wrapper);
|
|
}
|
|
};
|
|
|
|
/* Update field preview control for list column */
|
|
var updateFieldColumn = function($wrapper, field)
|
|
{
|
|
var $label = $wrapper.find('.field-col-item-label');
|
|
$label.text(field.name);
|
|
$wrapper.css(
|
|
{
|
|
width: field.width,
|
|
textAlign: field.position
|
|
});
|
|
};
|
|
|
|
var createListPreviewItem = function(field)
|
|
{
|
|
return isBatchForm ? '<div class="field-col-preview-form">' + (field ? createFieldControl(field, true) : '<input type="text" class="form-control" />') + '</div>' : ('<div class="field-col-preview-view"><div class="example-text-holder" data-size="' + (Math.floor(Math.random() * 8) + 2) + '"></div></div>');;
|
|
};
|
|
|
|
/* Update list preview */
|
|
var updateListPreview = function()
|
|
{
|
|
if(!canSetWidth) return;
|
|
var $columns = $listPreview.children('.field-col-preview').addClass('expired');
|
|
var totalWidth = 8;
|
|
$preview.find('.field-col-item').each(function()
|
|
{
|
|
var $colHeader = $(this);
|
|
var colId = $colHeader.data('id');
|
|
var colWidth = $colHeader.outerWidth();
|
|
var $col = $columns.filter('[data-id="' + colId + '"]');
|
|
var field = isBatchForm ? getField(colId) : null;
|
|
if(!$col.length)
|
|
{
|
|
$col = $([
|
|
'<div class="field-col-preview">',
|
|
createListPreviewItem(field), createListPreviewItem(field), createListPreviewItem(field), createListPreviewItem(field), createListPreviewItem(field),
|
|
'</div>'
|
|
].join('')).attr('data-id', colId);
|
|
}
|
|
$col.css('width', colWidth).removeClass('expired').appendTo($listPreview);
|
|
if(isBatchForm) updateFieldControl($col.find('.field-col-preview-form'), field);
|
|
totalWidth += colWidth;
|
|
});
|
|
$listPreview.css('width', totalWidth);
|
|
$columns.filter('.expired').remove();
|
|
};
|
|
|
|
/* Update field preview control */
|
|
var updateField = function(field, relativeField, relativeAsNext)
|
|
{
|
|
var id = field.id;
|
|
var $field = $('#field-' + id);
|
|
if(field.show !== '1')
|
|
{
|
|
if($field.length) $field.remove();
|
|
return;
|
|
}
|
|
if(!$field.length)
|
|
{
|
|
var fieldHtmlLines;
|
|
if(isForm)
|
|
{
|
|
fieldHtmlLines =
|
|
[
|
|
'<div data-field="' + (field.field || '') + '" data-id="' + id + '" id="field-' + id + '" class="field-item">',
|
|
'<div class="field-item-row">',
|
|
'<div class="field-label"></div>',
|
|
'<div class="field-control"></div>',
|
|
'<div class="field-buttons">',
|
|
'<button type="button" class="btn btn-link field-sort-handler" data-type="move"><i class="icon icon-move"></i></button>',
|
|
'<button type="button" class="btn btn-link field-delete-btn" data-type="delete"><i class="icon icon-trash"></i></button>',
|
|
'<button type="button" class="btn btn-link field-copy-btn" data-type="copy"><i class="icon icon-copy"></i></button><button type="button" class="btn btn-link field-error-btn" data-toggle="tooltip"><i class="icon icon-alert text-danger"></i></button>',
|
|
'</div>',
|
|
'</div>',
|
|
'</div>'
|
|
];
|
|
}
|
|
else if (isView)
|
|
{
|
|
fieldHtmlLines =
|
|
[
|
|
'<div data-field="' + (field.field || '') + '" data-id="' + id + '" id="field-' + id + '" class="field-view-item">',
|
|
'<div class="field-item-row">',
|
|
'<div class="field-label"></div>',
|
|
'<div class="field-value"><div class="example-text-holder"></div></div>',
|
|
'<div class="field-buttons">',
|
|
'<button type="button" class="btn btn-link field-sort-handler" data-type="move"><i class="icon icon-move"></i></button>',
|
|
'<button type="button" class="btn btn-link field-delete-btn" data-type="delete"><i class="icon icon-trash"></i></button>',
|
|
'<button type="button" class="btn btn-link field-copy-btn" data-type="copy"><i class="icon icon-copy"></i></button><button type="button" class="btn btn-link field-error-btn" data-toggle="tooltip"><i class="icon icon-alert text-danger"></i></button>',
|
|
'</div>',
|
|
'</div>',
|
|
'</div>'
|
|
];
|
|
}
|
|
else
|
|
{
|
|
fieldHtmlLines =
|
|
[
|
|
'<div data-field="' + field.field + '" data-id="' + id + '" id="field-' + id + '" class="field-col-item">',
|
|
'<i class="icon icon-move field-sort-handler"></i>',
|
|
'<span class="field-col-item-label field-sort-handler"></span>',
|
|
'<i class="icon icon-sort text-primary"></i>',
|
|
'<button type="button" class="btn btn-link field-delete-btn" data-type="delete"><i class="icon icon-trash"></i></button>',
|
|
'<button type="button" class="btn btn-link field-error-btn" data-toggle="tooltip"><i class="icon icon-alert text-danger"></i></button>',
|
|
'</div>'
|
|
];
|
|
}
|
|
$field = $(fieldHtmlLines.join(''));
|
|
if(field.field === 'actions' && !isForm)
|
|
{
|
|
$field.find('.field-col-item-label').removeClass('field-sort-handler');
|
|
}
|
|
|
|
if(relativeField)
|
|
{
|
|
var $relativeField = $('#field-' + relativeField.id);
|
|
if($relativeField.length) $relativeField[relativeAsNext ? 'before' : 'after']($field);
|
|
}
|
|
else if(isView)
|
|
{
|
|
$field.find('.example-text-holder').attr('data-size', Math.floor(Math.random() * 10) + 1);
|
|
$field.appendTo(field.position === 'basic' ? $basicPreview : $infoPreview);
|
|
}
|
|
else $field.appendTo($preview);
|
|
|
|
$field.find('.field-error-btn').tooltip(
|
|
{
|
|
placement: 'top',
|
|
container: 'body',
|
|
tipClass: 'tooltip-danger',
|
|
html: true,
|
|
title: function()
|
|
{
|
|
if(field.errors && field.errors.$length)
|
|
{
|
|
var messages = [];
|
|
$.each(field.errors, function(key, message)
|
|
{
|
|
if(key[0] === '$') return;
|
|
messages.push(message);
|
|
});
|
|
return messages.join('<br>');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
if(isForm)
|
|
{
|
|
var $control = $field.find('.field-control');
|
|
var controlType = field.control;
|
|
if($control.data('type') !== controlType) {
|
|
$control.data('type', controlType).empty().append(createFieldControl(field));
|
|
}
|
|
updateFieldControl($control, field);
|
|
}
|
|
else if(isView) updateFieldView($field, field);
|
|
else updateFieldColumn($field, field);
|
|
|
|
$field.find('.field-label').text(field.name);
|
|
$field.attr('data-order', field.order)
|
|
.attr('data-field', field.field)
|
|
.data('order', field.order)
|
|
.toggleClass('has-error', !!(field.errors && field.errors.$length))
|
|
.toggleClass('active', !!(activedField && activedField.id === id))
|
|
.toggleClass('field-required', (',' + field.layoutRules + ',').indexOf(',' + window.notEmptyRule + ',') > -1);
|
|
|
|
updateListPreview(field);
|
|
};
|
|
|
|
/* Update showed field list at left of page */
|
|
var updateShowedFields = function()
|
|
{
|
|
var showedFields = [];
|
|
for(var i = 0; i < fields.length; ++i)
|
|
{
|
|
var field = fields[i];
|
|
if(field.show === '0' && !field.unsaved) showedFields.push(field);
|
|
}
|
|
showedFields.sort(function(field1, field2)
|
|
{
|
|
return ('' + field1.name).localeCompare(field2.name);
|
|
});
|
|
|
|
var $editorShowedFields = $('#editorShowedFields');
|
|
var $fields = $editorShowedFields.find('.btn').addClass('can-remove');
|
|
|
|
for(var i = 0; i < showedFields.length; ++i)
|
|
{
|
|
var field = showedFields[i];
|
|
var $field = $fields.filter('[data-field="' + field.field + '"]');
|
|
if($field.length) $field.removeClass('can-remove').appendTo($editorShowedFields);
|
|
else $('<button type="button" class="btn btn-field-control" data-field="' + field.field + '">' + field.name + '</button>').appendTo($editorShowedFields);
|
|
}
|
|
|
|
$fields.filter('.can-remove').remove();
|
|
};
|
|
|
|
/* Sort fields by order property */
|
|
var sortFields = function()
|
|
{
|
|
fields.sort(function(x, y){return x.order * 1 - y.order * 1;});
|
|
|
|
var order = 0;
|
|
$.each(fields, function(index, field)
|
|
{
|
|
field.order = order;
|
|
order = order + 2;
|
|
});
|
|
};
|
|
|
|
/* Update all fields */
|
|
var updateFields = function(sort)
|
|
{
|
|
hasActionsInList = false;
|
|
if(sort) sortFields();
|
|
for(var i = 0; i < fields.length; ++i)
|
|
{
|
|
var field = fields[i];
|
|
if(!field.id) field.id = '_' + $.zui.uuid();
|
|
if(sort) field.order = i * 100 + 1;
|
|
if($.isPlainObject(field.options))
|
|
{
|
|
var options = [];
|
|
$.each(field.options, function(value, text)
|
|
{
|
|
options[text.length ? 'push' : 'unshift']({value: value, text: text});
|
|
});
|
|
field.options = options;
|
|
field._options = options;
|
|
}
|
|
else if($.isArray(field.options))
|
|
{
|
|
var options = [];
|
|
$.each(field.options, function(index, option)
|
|
{
|
|
if(typeof option === 'string') option = {value: String(index), text: option};
|
|
else option = $.extend({value: '', text: ''}, option);
|
|
options[option.text.length ? 'push' : 'unshift'](option);
|
|
});
|
|
field.options = options;
|
|
}
|
|
if(field.field === 'file') field.control = 'file';
|
|
if(typeof field.show === 'number') field.show = String(field.show);
|
|
if(field.type === 'decimal')
|
|
{
|
|
var digits = field.length.split(',');
|
|
field.integerDigits = Number.parseInt(digits[0], 10);
|
|
field.decimalDigits = Number.parseInt(digits[1], 10);
|
|
if(Number.isNaN(field.integerDigits)) field.integerDigits = fieldDefault.integerDigits;
|
|
if(Number.isNaN(field.decimalDigits)) field.decimalDigits = fieldDefault.decimalDigits;
|
|
}
|
|
if(field.name === null) field.name = '';
|
|
if(!field.length) field.length = fieldDefault.fieldLength[field.field];
|
|
validateField(field);
|
|
updateField(field);
|
|
|
|
if(!isForm && field.field === 'actions') hasActionsInList = true;
|
|
}
|
|
};
|
|
|
|
/* Validate field properties */
|
|
var validateField = function(field, includeHiddenField)
|
|
{
|
|
if(!field)
|
|
{
|
|
var errorsCount;
|
|
for(var i = 0; i < fields.length; ++i)
|
|
{
|
|
if(validateField(fields[i], includeHiddenField)) {
|
|
errorsCount++;
|
|
}
|
|
}
|
|
return errorsCount;
|
|
}
|
|
|
|
if(typeof field === 'string')
|
|
{
|
|
field = getField(field);
|
|
if(!field) return;
|
|
}
|
|
|
|
if(!includeHiddenField && field.show !== '1') return;
|
|
|
|
var errors = field.errors || {};
|
|
if(!errors.name)
|
|
{
|
|
if(typeof field.name !== 'string' || !field.name.length) errors.name = window.validateMessages.nameRequired;
|
|
else
|
|
{
|
|
for(var i = 0; i < fields.length; ++i)
|
|
{
|
|
var thisField = fields[i];
|
|
if(thisField.id !== field.id && thisField.name === field.name)
|
|
{
|
|
errors.name = window.validateMessages.nameDuplicated;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var buildin = field.buildin === '1' || defaultFields[field.field];
|
|
var isTypeLengthRequired = field.type === 'char' || field.type === 'varchar';
|
|
var isDecimalType = field.type === 'decimal';
|
|
var isCustomOptions = typeof field.options === 'object';
|
|
if(!buildin)
|
|
{
|
|
if(!errors.field)
|
|
{
|
|
if(typeof field.field !== 'string' || !field.field.length) errors.field = window.validateMessages.fieldRequired;
|
|
else if(!/^[a-zA-Z]+$/.test(field.field)) errors.field = window.validateMessages.fieldInvalid;
|
|
else
|
|
{
|
|
for(var i = 0; i < fields.length; ++i)
|
|
{
|
|
var thisField = fields[i];
|
|
if(thisField.id !== field.id && thisField.field === field.field)
|
|
{
|
|
errors.field = window.validateMessages.fieldDuplicated.replace('%s', thisField.name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(!errors.length && isTypeLengthRequired)
|
|
{
|
|
if(typeof field.length !== 'string' || !field.length.length) errors.length = window.validateMessages.lengthRequired;
|
|
}
|
|
|
|
if(!errors.defaultValue && typeof field.defaultValue === 'string' && field.defaultValue.length)
|
|
{
|
|
var fieldSelectType = getFieldSelectType(field);
|
|
if(fieldSelectType)
|
|
{
|
|
var isCustomOption = typeof field.options === 'object';
|
|
var optionsKey = field.module + '_' + field.field + '_' + field.options;
|
|
var fieldOptions = isCustomOption ? field.options : remoteOptions[optionsKey];
|
|
if(fieldOptions)
|
|
{
|
|
var isMultiSelect = fieldSelectType === 'multi';
|
|
var defaultValues = isMultiSelect ? field.defaultValue.split(',') : [field.defaultValue];
|
|
var defaultValueError;
|
|
for(var i = 0; i < defaultValues.length; ++i)
|
|
{
|
|
var defaultValue = defaultValues[i];
|
|
var optionIndex = fieldOptions.findIndex(function(option)
|
|
{
|
|
return option.value == defaultValue;
|
|
});
|
|
if(optionIndex < 0)
|
|
{
|
|
if(fieldOptions.findIndex(function(option){return option.text === defaultValue;}) > 0) defaultValueError = window.validateMessages.defaultNotOptionKey.replace('%s', defaultValue);
|
|
else defaultValueError = window.validateMessages.defaultNotInOptions.replace('%s', defaultValue);
|
|
break;
|
|
}
|
|
}
|
|
if(defaultValueError) errors.defaultValue = defaultValueError;
|
|
}
|
|
else if(!isCustomOption)
|
|
{
|
|
delete errors.defaultValue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!errors.width && canSetWidth && field.width && field.width !== 'auto' && !/^\d+$/.test(field.width))
|
|
{
|
|
errors.width = window.validateMessages.widthInvalid;
|
|
}
|
|
|
|
errors.$length = getObjectKeys(errors, function(key)
|
|
{
|
|
return key[0] !== '$';
|
|
}).length;
|
|
if(activedField && activedField.id === field.id)
|
|
{
|
|
var $fieldNameMessage = $('#fieldNameMessage');
|
|
if(errors.name) $fieldNameMessage.html(errors.name).show();
|
|
else $fieldNameMessage.hide();
|
|
$('#fieldEditName').toggleClass('has-error', !!errors.name);
|
|
|
|
if(!buildin)
|
|
{
|
|
var $fieldFieldMessage = $('#fieldFieldMessage');
|
|
if(errors.field) $fieldFieldMessage.html(errors.field).show();
|
|
else $fieldFieldMessage.hide();
|
|
$('#fieldEditField').toggleClass('has-error', !!errors.field);
|
|
|
|
var $fieldLengthMessage = $('#fieldLengthMessage');
|
|
var lengthError = errors.length || errors.integerDigits || errors.decimalDigits;
|
|
if(lengthError) $fieldLengthMessage.html(lengthError).show();
|
|
else $fieldLengthMessage.hide();
|
|
$('#fieldLength').toggleClass('has-error', !!errors.length);
|
|
$('#integerDigits').toggleClass('has-error', !!errors.integerDigits);
|
|
$('#decimalDigits').toggleClass('has-error', !!errors.decimalDigits);
|
|
$('#fieldEditType').toggleClass('hide-type-length', !isTypeLengthRequired);
|
|
$('#decimalDigitsGroup').toggleClass('hidden', !isDecimalType);
|
|
|
|
var $fieldDefaultValueMessage = $('#fieldDefaultValueMessage');
|
|
if(errors.default) $fieldDefaultValueMessage.html(errors.default).show();
|
|
else $fieldDefaultValueMessage.hide();
|
|
$('#fieldEditDefaultValue').toggleClass('has-error', !!errors.default);
|
|
}
|
|
|
|
if(canSetWidth)
|
|
{
|
|
var $fieldWidthMessage = $('#fieldWidthMessage');
|
|
if(errors.width) $fieldWidthMessage.html(errors.width).show();
|
|
else $fieldWidthMessage.hide();
|
|
$('#widthEditControl').toggleClass('has-error', !!errors.width);
|
|
}
|
|
|
|
if(isCustomOptions)
|
|
{
|
|
var $fieldOptionsMessage = $('#fieldOptionsMessage');
|
|
if(errors.options) $fieldOptionsMessage.html(errors.options).show();
|
|
else $fieldOptionsMessage.hide();
|
|
}
|
|
}
|
|
field.errors = errors;
|
|
updateField(field);
|
|
|
|
return errors.$length;
|
|
};
|
|
|
|
/* Public method to update field errors, usually used for form submit result */
|
|
window.updateFieldsErrors = function(errors)
|
|
{
|
|
var firstField;
|
|
var errorsCount = 0;
|
|
$.each(errors, function(fieldId, errorsMap)
|
|
{
|
|
var field = getField(fieldId);
|
|
if(field)
|
|
{
|
|
field.errors = errorsMap;
|
|
if(field.errors) field.errors.$length = getObjectKeys(field.errors).length;
|
|
updateField(field);
|
|
if(!firstField) firstField = field;
|
|
errorsCount++;
|
|
}
|
|
});
|
|
if(firstField)
|
|
{
|
|
activeField(firstField.id, true);
|
|
$('#editor').addClass('highlight-errors');
|
|
setTimeout(function()
|
|
{
|
|
$('#editor').removeClass('highlight-errors');
|
|
}, 5000);
|
|
|
|
return new $.zui.Messager(window.validateMessages.failSummary.replace('%s', errorsCount),
|
|
{
|
|
type: 'danger',
|
|
icon: 'bell',
|
|
placement: 'center',
|
|
}).show();
|
|
}
|
|
};
|
|
|
|
/* Edit field properties and update editor at right of page */
|
|
var editField = function(field)
|
|
{
|
|
if(typeof field === 'string') field = getField(field);
|
|
var $filedEditTip = $('#filedEditTip');
|
|
if(!field)
|
|
{
|
|
$filedEditTip.show();
|
|
$filedEditForm.hide();
|
|
return;
|
|
}
|
|
$filedEditTip.hide();
|
|
|
|
var isFileOrActions = field.field === 'file' || field.field === 'actions';
|
|
$filedEditForm.find('#fieldName').val(field.name).attr('readonly', isFileOrActions);
|
|
|
|
var errors = field.errors || {};
|
|
var buildin = isBuildInField(field);
|
|
var $fieldNameMessage = $('#fieldNameMessage');
|
|
|
|
if(errors.name) $fieldNameMessage.html(errors.name).show();
|
|
else $fieldNameMessage.hide();
|
|
$('#fieldEditName').toggleClass('has-error', !!errors.name).toggleClass('required', !isFileOrActions);
|
|
$('#fieldControl').val(field.control).change();
|
|
if(!buildin && !isFileOrActions)
|
|
{
|
|
$('#fieldField').val(field.field);
|
|
var $fieldFieldMessage = $('#fieldFieldMessage');
|
|
if(errors.field) $fieldFieldMessage.html(errors.field).show();
|
|
else $fieldFieldMessage.hide();
|
|
$('#fieldEditField').toggleClass('has-error', !!errors.field);
|
|
|
|
$('#fieldType').val(field.type);
|
|
$('#fieldLength').val(field.length);
|
|
|
|
var defaultValue = field.defaultValue;
|
|
if(field.control == 'datetime' && defaultValue == 'currentTime') defaultValue = window.currentTime;
|
|
if(field.control == 'date' && defaultValue == 'currentTime') defaultValue = window.currentDate;
|
|
|
|
$('#fieldDefaultValue').val(defaultValue);
|
|
$('#fieldRules').val((typeof field.layoutRules === 'string' && field.layoutRules.length) ? field.layoutRules.split(',') : field.layoutRules).trigger('chosen:updated');
|
|
$('#fieldSql').val(field.sql);
|
|
$('#integerDigits').val(field.integerDigits);
|
|
$('#decimalDigits').val(field.decimalDigits);
|
|
|
|
var $fieldLengthMessage = $('#fieldLengthMessage');
|
|
if(errors.length) $fieldLengthMessage.html(errors.length).show();
|
|
else $fieldLengthMessage.hide();
|
|
|
|
$('#fieldLength').toggleClass('has-error', !!errors.length);
|
|
|
|
var $fieldDefaultValueMessage = $('#fieldDefaultValueMessage');
|
|
if(errors.default) $fieldDefaultValueMessage.html(errors.default).show();
|
|
else $fieldDefaultValueMessage.hide();
|
|
$('#fieldEditDefaultValue').toggleClass('has-error', !!errors.default);
|
|
}
|
|
|
|
if(canSetWidth)
|
|
{
|
|
var $fieldWidthMessage = $('#fieldWidthMessage');
|
|
if(errors.width) $fieldWidthMessage.html(errors.width).show();
|
|
else $fieldWidthMessage.hide();
|
|
$('#widthEditControl').toggleClass('has-error', !!errors.width);
|
|
}
|
|
|
|
if(!buildin || field.field === 'status')
|
|
{
|
|
var isCustomOptions = typeof field.options === 'object';
|
|
$('#fieldOptionType').val(isCustomOptions ? 'custom' : field.options).change();
|
|
}
|
|
|
|
if(canSetWidth) $('#fieldWidth').val(field.width);
|
|
if(canSetPosition) $('#fieldPosition').val(field.position);
|
|
|
|
$('#fieldIsValue').prop('checked', field.isValue === '1');
|
|
|
|
$filedEditForm.toggleClass('is-status', field.field === 'status').toggleClass('is-file', isFileOrActions).toggleClass('is-buildin', !!buildin).show();
|
|
};
|
|
|
|
/* Unactive field in preview */
|
|
var unactiveField = function()
|
|
{
|
|
if(activedField)
|
|
{
|
|
var oldActiveField = activedField;
|
|
activedField = null;
|
|
updateField(oldActiveField);
|
|
editField();
|
|
}
|
|
};
|
|
|
|
/* Active field in preview */
|
|
var activeField = function(id, force)
|
|
{
|
|
var oldActiveField = activedField;
|
|
var field = typeof id === 'object' ? id : getField(id);
|
|
if(field && field.show === '1' && (force || !oldActiveField || oldActiveField.id !== field.id))
|
|
{
|
|
activedField = field;
|
|
if(oldActiveField) updateField(oldActiveField);
|
|
updateField(field);
|
|
editField(field);
|
|
var $field = $('#field-' + field.id);
|
|
if($field.length && $field[0].scrollIntoViewIfNeeded)
|
|
{
|
|
$field[0].scrollIntoViewIfNeeded();
|
|
}
|
|
}
|
|
};
|
|
|
|
/* Delete field in preview */
|
|
var deleteField = function(id)
|
|
{
|
|
var field = getField(id);
|
|
if(field)
|
|
{
|
|
field.show = '0';
|
|
field.changed = true;
|
|
|
|
updateField(field);
|
|
updateShowedFields();
|
|
if(activedField && activedField.id === field.id) unactiveField();
|
|
updateListPreview();
|
|
}
|
|
};
|
|
|
|
/* Clone field in preview */
|
|
var cloneField = function(id)
|
|
{
|
|
var fieldIndex = getFieldIndex(id);
|
|
if(fieldIndex > -1)
|
|
{
|
|
var field = fields[fieldIndex];
|
|
var copyField = $.extend({}, field, {id: '_' + $.zui.uuid(), order: field.order + 1, field: '', name: '', changed: true, unsaved: true, buildin: '0', errors: {$length: 0}});
|
|
fields.splice(fieldIndex + 1, 0, copyField);
|
|
updateField(copyField, field);
|
|
validateField(copyField);
|
|
activeField(copyField.id);
|
|
}
|
|
};
|
|
|
|
/* Update fields preview controls and showed list on left */
|
|
updateFields(true);
|
|
updateShowedFields();
|
|
|
|
/* Bind events on preview element */
|
|
$preview.on('click', '.field-buttons>.btn,.field-delete-btn', function(e)
|
|
{
|
|
var $this = $(this);
|
|
var btnType = $this.data('type');
|
|
var id = $this.closest('.field-item,.field-col-item,.field-view-item').data('id');
|
|
if(btnType === 'delete') deleteField(id);
|
|
else if(btnType === 'copy') cloneField(id);
|
|
e.stopPropagation();
|
|
}).on('click', '.field-item,.field-col-item,.field-view-item', function()
|
|
{
|
|
activeField($(this).data('id'));
|
|
});
|
|
|
|
/* Make preview control sortable */
|
|
$preview.sortable(
|
|
{
|
|
selector: '.field-item,.field-col-item,.field-view-item',
|
|
trigger: '.field-sort-handler',
|
|
containerSelector: isView ? '#uiInfoViewPreview, #uiBasicViewPreview' : false,
|
|
start: function(){$('#editorPreview').addClass('editor-sorting');},
|
|
stopPropagation: false,
|
|
before: function(e)
|
|
{
|
|
/* Fix backyard task #11550 https://back.zcorp.cc/pms/task-view-11550.html */
|
|
e.event.preventDefault = function(){};
|
|
},
|
|
finish: function(e)
|
|
{
|
|
$('#editorPreview').removeClass('editor-sorting');
|
|
if(e.changed)
|
|
{
|
|
for(var i = 0; i < e.list.length; ++i)
|
|
{
|
|
var $item = e.list[i].item;
|
|
var field = getField($item.data('id'));
|
|
field.order = i * 100 + 1;
|
|
field.changed = true;
|
|
}
|
|
sortFields();
|
|
if(isView)
|
|
{
|
|
var field = getField(e.element.data('id'));
|
|
var newPosition = e.element.closest('.view-dropbox').data('position');
|
|
if(newPosition !== field.position)
|
|
{
|
|
field.position = newPosition;
|
|
field.changed = true;
|
|
updateField(field);
|
|
}
|
|
}
|
|
updateListPreview();
|
|
}
|
|
}
|
|
});
|
|
|
|
/* Update field type tip */
|
|
var updateFieldTypeTip = function(field)
|
|
{
|
|
var tip = '';
|
|
switch(field.type)
|
|
{
|
|
case 'tinyint':
|
|
case 'smallint':
|
|
case 'mediumint':
|
|
case 'int':
|
|
var max = fieldMax[field.type];
|
|
var min = fieldMin[field.type];
|
|
tip = window.tips.number.replace(/MAX/, max).replace(/MIN/, min);
|
|
$('#fieldDefaultValue').attr({min: min, max: max});
|
|
break;
|
|
case 'decimal':
|
|
var integerDigits = field.integerDigits;
|
|
var decimalDigits = field.decimalDigits;
|
|
|
|
integerDigits = integerDigits ? parseInt(integerDigits) : 0;
|
|
integerDigits = integerDigits > fieldMax.integerDigits ? fieldMax.integerDigits : integerDigits;
|
|
integerDigits = integerDigits < fieldMin.integerDigits ? fieldDefault.integerDigits : integerDigits;
|
|
integerDigits = parseInt(integerDigits);
|
|
|
|
decimalDigits = decimalDigits ? parseInt(decimalDigits) : 0;
|
|
decimalDigits = decimalDigits > fieldMax.decimalDigits ? fieldMax.decimalDigits : decimalDigits;
|
|
decimalDigits = decimalDigits < fieldMin.integerDigits ? fieldDefault.decimalDigits : decimalDigits;
|
|
decimalDigits = parseInt(decimalDigits);
|
|
|
|
var max = '.'.padStart(integerDigits + 1, 9).padEnd(integerDigits + decimalDigits + 1, 9);
|
|
var min = '-' + max;
|
|
|
|
tip = window.tips.number.replace(/MAX/, max).replace(/MIN/, min);
|
|
$('#fieldDefaultValue').attr({min: min, max: max});
|
|
break;
|
|
case 'char':
|
|
var length = field.length;
|
|
length = length ? parseInt(length) : 0;
|
|
length = length > fieldMax.charLength ? fieldMax.charLength : length;
|
|
length = length < fieldMin.charLength ? fieldDefault.charLength : length;
|
|
|
|
tip = window.tips.string.replace(/LENGTH/, length);
|
|
break;
|
|
case 'varchar':
|
|
var length = field.length;
|
|
length = length ? parseInt(length) : 0;
|
|
length = length > fieldMax.varcharLength ? fieldMax.varcharLength : length;
|
|
length = length < fieldMin.varcharLength ? fieldDefault.varcharLength : length;
|
|
|
|
tip = window.tips.string.replace(/LENGTH/, length);
|
|
break;
|
|
}
|
|
$('#fieldTypeTip').toggleClass('hidden', !tip).html(tip);
|
|
};
|
|
|
|
/* Bind events on properties editor */
|
|
var $fieldEditOptionsList = $('#fieldEditOptionsList');
|
|
$filedEditForm.on('change', function(e)
|
|
{
|
|
var field = activedField;
|
|
if(!field) return;
|
|
var $control = $(e.target);
|
|
if($control.closest('.field-option-item').length)
|
|
{
|
|
$control = $('#fieldControl');
|
|
}
|
|
var controlName = $control.attr('name');
|
|
if(!controlName) return;
|
|
|
|
var oldValue = field[controlName];
|
|
var controlValue = $control.val();
|
|
if($control.is(':checkbox')) controlValue = $control.prop('checked') ? controlValue : '';
|
|
var optionsKey = field.module + '_' + field.field + '_' + field.options;
|
|
|
|
if(controlName === 'length' || controlName === 'integerDigits' || controlName === 'decimalDigits')
|
|
{
|
|
var numValue = Number.parseInt(controlValue, 10);
|
|
var valueName = controlName === 'length' ? (field.type + 'Length') : controlName;
|
|
if(Number.isNaN(numValue)) controlValue = Number.parseInt(fieldDefault[valueName], 10);
|
|
else
|
|
{
|
|
var min = Number.parseInt(fieldMin[valueName], 10);
|
|
var max = Number.parseInt(fieldMax[valueName], 10);
|
|
var fixValue = Math.floor(Math.max(min, Math.min(max, numValue))) + '';
|
|
if(fixValue !== controlValue)
|
|
{
|
|
$control.val(fixValue);
|
|
controlValue = fixValue + '';
|
|
}
|
|
}
|
|
}
|
|
else if(controlName === 'defaultValue')
|
|
{
|
|
if(field.control === 'integer' || field.control === 'decimal')
|
|
{
|
|
var $fieldDefaultValue = $('#fieldDefaultValue');
|
|
controlValue = Math.min(Math.max(Number.parseInt(controlValue, 10), Number.parseInt($fieldDefaultValue.attr('min'), 10)), Number.parseInt($fieldDefaultValue.attr('max'), 10)) + '';
|
|
$fieldDefaultValue.val(controlValue);
|
|
}
|
|
if(controlValue)
|
|
{
|
|
var fieldOptions = remoteOptions[optionsKey];
|
|
if(fieldOptions)
|
|
{
|
|
var optionIndex = fieldOptions.findIndex(function(option)
|
|
{
|
|
return option.value == controlValue;
|
|
});
|
|
if(optionIndex < 0)
|
|
{
|
|
var controlText = $control.text();
|
|
fieldOptions.push({value: controlValue, text: controlText});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(controlName === 'optionType')
|
|
{
|
|
oldValue = typeof field.options === 'object' ? 'custom' : String(field.options);
|
|
if(controlValue === 'custom')
|
|
{
|
|
field.options = field._options || [];
|
|
}
|
|
else
|
|
{
|
|
if(typeof field.options === 'object') field._options = field.options;
|
|
field.options = controlValue;
|
|
}
|
|
}
|
|
else field[controlName] = controlValue;
|
|
|
|
if(controlName === 'control' || controlName === 'optionType')
|
|
{
|
|
var optionSelectType = getFieldSelectType(field);
|
|
$filedEditForm.find('#fieldEditOptionType').toggleClass('hide', !optionSelectType);
|
|
var isCustomOptions = optionSelectType && typeof field.options === 'object';
|
|
$('#fieldEditOptions').toggleClass('hide', !isCustomOptions);
|
|
$('#fieldEditSql').toggleClass('hide', isCustomOptions || !optionSelectType || field.options !== 'sql');
|
|
var $optionsList = $fieldEditOptionsList.empty();
|
|
var $fieldDefaultValue = $('#fieldDefaultValue');
|
|
var isMultiSelect = optionSelectType === 'multi';
|
|
if(oldValue !== controlValue) field.defaultValue = '';
|
|
|
|
if($('#dateCustom').length && field.control !== 'datetime' && field.control !== 'date')
|
|
{
|
|
$('#dateCustom').after($fieldDefaultValue);
|
|
$('#dateCustom').remove();
|
|
}
|
|
|
|
if(optionSelectType && optionSelectType !== 'text')
|
|
{
|
|
var picker = $fieldDefaultValue.data('zui.picker');
|
|
if(picker) picker.destroy();
|
|
if(!$fieldDefaultValue.is('select'))
|
|
{
|
|
$fieldDefaultValue.replaceWith($('<select id="fieldDefaultValue" name="defaultValue" class="form-control"><option value=""></option></select>').attr('multiple', isMultiSelect ? 'multiple' : null));
|
|
$fieldDefaultValue = $('#fieldDefaultValue');
|
|
}
|
|
else if(isMultiSelect !== !!$fieldDefaultValue.attr('multiple'))
|
|
{
|
|
var chosen = $fieldDefaultValue.data('chosen');
|
|
if(chosen) chosen.destroy();
|
|
$fieldDefaultValue.attr('multiple', isMultiSelect ? 'multiple' : null);
|
|
}
|
|
$fieldDefaultValue.empty();
|
|
if(isCustomOptions)
|
|
{
|
|
var hasEmptyOption;
|
|
if(!field.options.length) field.options.push({value: '', text: ''});
|
|
|
|
$.each(field.options, function(_, option)
|
|
{
|
|
var $inputGroup = $('<div class="input-group field-option-item"></div>');
|
|
$inputGroup.append('<span class="input-group-addon">' + window.keyValueList.key + '</span>');
|
|
$inputGroup.append($('<input name="optionValue" class="form-control w-50px" placeholder="' + window.langOptionCode + '" />').val(option.value));
|
|
$inputGroup.append('<span class="input-group-addon">' + window.keyValueList.value + '</span>');
|
|
$inputGroup.append($('<input name="optionText" class="form-control" />').val(option.text));
|
|
$inputGroup.append('<span class="input-group-addon" style="padding: 4px 2px"><i class="icon icon-plus" data-type="add"></i><i class="icon icon-minus" data-type="delete"></i></span>');
|
|
$optionsList.append($inputGroup);
|
|
$fieldDefaultValue.append($('<option>').attr('value', option.value).text(option.text));
|
|
if(!option.value.length) hasEmptyOption = true;
|
|
});
|
|
if(!hasEmptyOption) $fieldDefaultValue.prepend('<option value=""></option>');
|
|
}
|
|
else
|
|
{
|
|
var options = remoteOptions[optionsKey];
|
|
var hasEmptyOption;
|
|
if(options)
|
|
{
|
|
$.each(options, function(_, option)
|
|
{
|
|
if(!option.value.length) hasEmptyOption = true;
|
|
$fieldDefaultValue.append($('<option>').attr('value', option.value).text(option.text));
|
|
});
|
|
}
|
|
if(!hasEmptyOption) $fieldDefaultValue.prepend('<option value=""></option>');
|
|
}
|
|
var fieldValue = (typeof field.defaultValue === 'string' && field.defaultValue.length) ? field.defaultValue.split(',') : field.defaultValue;
|
|
$fieldDefaultValue.val(fieldValue);
|
|
|
|
initDefault(field, isCustomOptions);
|
|
}
|
|
else
|
|
{
|
|
if(!$fieldDefaultValue.is('input'))
|
|
{
|
|
var chosen = $fieldDefaultValue.data('chosen');
|
|
var picker = $fieldDefaultValue.data('zui.picker');
|
|
if(chosen) chosen.destroy();
|
|
if(picker) picker.destroy();
|
|
|
|
$fieldDefaultValue.replaceWith('<input type="text" id="fieldDefaultValue" name="defaultValue" class="form-control" />');
|
|
$fieldDefaultValue = $('#fieldDefaultValue').val(field.defaultValue);
|
|
}
|
|
var dateType = (field.control === 'datetime' || field.control === 'date') ? field.control : false;
|
|
if($fieldDefaultValue.data('dateType') !== dateType)
|
|
{
|
|
$fieldDefaultValue.data('dateType', dateType);
|
|
$fieldDefaultValue.attr('autocomplete', 'off');
|
|
var datepicker = $fieldDefaultValue.data('datetimepicker');
|
|
if(datepicker) datepicker.remove();
|
|
$fieldDefaultValue.val(field.defaultValue);
|
|
if(dateType) $fieldDefaultValue[dateType + 'picker']();
|
|
}
|
|
if(dateType == 'date' || dateType == 'datetime')
|
|
{
|
|
window.dateType = dateType;
|
|
fieldDefaultValue = field.defaultValue;
|
|
if($('#dateCustom').length === 0)
|
|
{
|
|
checked = field.defaultValue != 'currentTime' ? 'checked' : '';
|
|
$fieldDefaultValue.before("<div class='input-group' id='dateCustom'><span class='input-group-addon'><div class='checkbox-primary'><input type='checkbox' id='custom' value='1' " + checked + "/><label>" + window.custom + "</label></div></span></div>");
|
|
$('#dateCustom').prepend($fieldDefaultValue);
|
|
$('#custom').change();
|
|
}
|
|
else
|
|
{
|
|
$('#custom').prop('checked', field.defaultValue != 'currentTime').change();
|
|
}
|
|
$fieldDefaultValue = $('#fieldDefaultValue');
|
|
$fieldDefaultValue.val(field.defaultValue);
|
|
}
|
|
if(!dateType)
|
|
{
|
|
var isNumber = field.control === 'integer' || field.control === 'decimal';
|
|
$fieldDefaultValue.attr({type: isNumber ? 'number' : 'text', min: null, max: null});
|
|
}
|
|
}
|
|
|
|
$fieldDefaultValue.attr('placeholder', isMultiSelect ? window.multiDefaultPlaceholder : '');
|
|
|
|
if(controlName === 'optionType' && optionSelectType && !isCustomOptions)
|
|
{
|
|
getDefaultsOfOption(field, function(options)
|
|
{
|
|
optionsKey = field.module + '_' + field.field + '_' + field.options;
|
|
activedField.optionsData = options;
|
|
remoteOptions[optionsKey] = formatOptions(options);
|
|
validateField(field);
|
|
$('#fieldControl').change();
|
|
});
|
|
}
|
|
else if(controlName === 'control')
|
|
{
|
|
$filedEditForm.attr('data-control', controlValue);
|
|
|
|
if(controlValue !== 'file')
|
|
{
|
|
var defaultProps = getDefaultFieldByControl(controlValue);
|
|
var $hiddenOptions = $('#fieldType option').show().not('.' + defaultProps.optionClass).hide();
|
|
if($hiddenOptions.filter('[value="' + field.type + '"]').length)
|
|
{
|
|
$('#fieldType').val(defaultProps.type).change();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(controlName === 'type')
|
|
{
|
|
var typeHasLength = field.type === 'char' || field.type === 'varchar';
|
|
$('#fieldEditType').toggleClass('hide-type-length', !typeHasLength);
|
|
if(typeHasLength)
|
|
{
|
|
var valueName = field.type + 'Length';
|
|
var min = Number.parseInt(fieldMin[valueName], 10);
|
|
var max = Number.parseInt(fieldMax[valueName], 10);
|
|
var numValue = Number.parseInt(field.length, 10);
|
|
var fixValue = Math.floor(Math.max(min, Math.min(max, numValue))) + '';
|
|
$('#fieldLength').attr({min: min, max: max}).val(fixValue);
|
|
}
|
|
$('#decimalDigitsGroup').toggleClass('hidden', field.type !== 'decimal');
|
|
}
|
|
if(oldValue !== controlValue)
|
|
{
|
|
field.changed = true;
|
|
if(field.errors)
|
|
{
|
|
if(field.errors && field.errors[controlName]) delete field.errors[controlName];
|
|
if(controlName === 'control' || controlName === 'optionType') delete field.errors.defaultValue;
|
|
if(controlName === 'type') delete field.errors.length;
|
|
if(controlName === 'integerDigits') delete field.errors.integerDigits;
|
|
if(controlName === 'decimalDigits')
|
|
{
|
|
delete field.errors.decimalDigits;
|
|
delete field.errors.integerDigits;
|
|
}
|
|
}
|
|
}
|
|
validateField(field);
|
|
updateField(field);
|
|
updateFieldTypeTip(field);
|
|
});
|
|
|
|
/* Synchronize field options after use change it */
|
|
var syncFieldOptions = function()
|
|
{
|
|
if(!activedField || typeof activedField.options !== 'object') return;
|
|
var options = [];
|
|
$fieldEditOptionsList.find('.field-option-item').each(function()
|
|
{
|
|
var $item = $(this);
|
|
var value = $item.find('[name="optionValue"]').val();
|
|
var text = $item.find('[name="optionText"]').val();
|
|
options.push(
|
|
{
|
|
value: value,
|
|
text: text,
|
|
});
|
|
});
|
|
activedField.options = options;
|
|
activedField._options = options;
|
|
activedField.changed = true;
|
|
delete activedField.errors.defaultValue;
|
|
validateField(activedField);
|
|
$('#fieldControl').change();
|
|
};
|
|
$fieldEditOptionsList.on('focus', 'input', function()
|
|
{
|
|
$(this).closest('.field-option-item').addClass('active');
|
|
}).on('blur', 'input', function()
|
|
{
|
|
$(this).closest('.field-option-item').removeClass('active');
|
|
}).on('click', '.icon', function()
|
|
{
|
|
var $this = $(this);
|
|
var type = $this.data('type');
|
|
var $item = $this.closest('.field-option-item');
|
|
if(type === 'add')
|
|
{
|
|
var $newItem = $item.clone();
|
|
$newItem.find('[name="optionValue"],[name="optionText"]').val('');
|
|
$item.after($newItem);
|
|
}
|
|
else
|
|
{
|
|
if($fieldEditOptionsList.find('.field-option-item').length === 1)
|
|
{
|
|
$item.find('[name="optionValue"],[name="optionText"]').val('');
|
|
}
|
|
else $item.remove();
|
|
}
|
|
syncFieldOptions();
|
|
}).on('change', syncFieldOptions);
|
|
|
|
/* Support to add fields to preview section with drag-and-drop */
|
|
$('#editorControls').droppable(
|
|
{
|
|
container: 'body',
|
|
selector: '.btn-field-control',
|
|
target: '.field-item,#uiPreview,.field-col-item' + (hasActionsInList ? '' : ',#uiListHeader,#uiInfoViewPreview,#uiBasicViewPreview,.field-view-item'),
|
|
stopPropagation: true,
|
|
nested: true,
|
|
start: function()
|
|
{
|
|
$('input').blur();
|
|
$('#editor').addClass('editor-dragging');
|
|
},
|
|
always: function()
|
|
{
|
|
$('#editor').removeClass('editor-dragging');
|
|
},
|
|
drag: function(e)
|
|
{
|
|
$previewBox.find('.drop-hover').removeClass('drop-hover');
|
|
if(e.isIn) $(e.target).closest('#uiListTable,#uiPreviewWrapper,.view-panel').addClass('drop-hover');
|
|
},
|
|
drop: function(e)
|
|
{
|
|
var $element = e.element;
|
|
var field = $element.data('field');
|
|
var nextField;
|
|
var getOrder = function()
|
|
{
|
|
var $target = e.target;
|
|
nextField = $target.is('#uiPreview,#uiListHeader,#uiInfoViewPreview,#uiBasicViewPreview') ? null : getField($target.data('id'));
|
|
if(nextField) return nextField.order - 1;
|
|
var lastField = getField($preview.find('.field-item,.field-col-item,.field-view-item').last().data('id'));
|
|
return lastField ? lastField.order + 1 : fields.length * 100 + 2;
|
|
}
|
|
if(field)
|
|
{
|
|
field = getField(field);
|
|
if(field)
|
|
{
|
|
field.show = '1';
|
|
field.order = getOrder();
|
|
field.changed = true;
|
|
if(isView)
|
|
{
|
|
field.position = e.target.closest('.view-dropbox').data('position');
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var controlType = $element.data('type');
|
|
if(controlType)
|
|
{
|
|
field =
|
|
$.extend({
|
|
id : '_' + $.zui.uuid(),
|
|
control : controlType,
|
|
name : window.controlTypeList[controlType],
|
|
order : getOrder(),
|
|
buildin : '0',
|
|
layoutRules : '',
|
|
defaultValue : '',
|
|
mobileShow : '0',
|
|
module : window.flowModule,
|
|
options : [{value: '', text: ''}],
|
|
position : isView ? e.target.closest('.view-dropbox').data('position') : 'left',
|
|
readonly : '0',
|
|
rules : '',
|
|
show : '1',
|
|
summary : '',
|
|
length : '255',
|
|
width : 'auto',
|
|
changed : true,
|
|
isValue : '0',
|
|
unsaved : true
|
|
}, getDefaultFieldByControl(controlType));
|
|
fields.push(field);
|
|
}
|
|
}
|
|
if(field && typeof field === 'object')
|
|
{
|
|
if(nextField) nextField.changed = true;
|
|
field.changed = true;
|
|
sortFields();
|
|
updateField(field, nextField, true);
|
|
updateShowedFields();
|
|
activeField(field);
|
|
}
|
|
}
|
|
});
|
|
|
|
/* Limit input content to prevent mistakes */
|
|
$('#fieldLength').on('change', function()
|
|
{
|
|
var $input = $(this);
|
|
$input.val($input.val().replace(/[^\d,]/g, ''));
|
|
});
|
|
$('#fieldField').on('change', function()
|
|
{
|
|
var $input = $(this);
|
|
$input.val($input.val().replace(/[^a-zA-Z]/g, ''));
|
|
});
|
|
$('#fieldEditOptionsList').on('change', '[name="optionValue"]', function()
|
|
{
|
|
var $input = $(this);
|
|
$input.val($input.val().replace(/[^a-zA-Z\d]/g, ''));
|
|
});
|
|
|
|
$('#fieldEditOptionsList').on('change', '[name="optionText"]', function(){removeOptionError(activedField);});
|
|
$('#fieldEditOptionsList').on('click', '.icon-minus', function(){removeOptionError(activedField);});
|
|
|
|
/* Bind event to make list preview auto scroll with header */
|
|
$preview.on('scroll', function()
|
|
{
|
|
$('#uiListBody').scrollLeft($preview.scrollLeft());
|
|
});
|
|
|
|
/* Bind event to unactive field on use click top menu */
|
|
$('#editorNav').on('click', unactiveField);
|
|
|
|
$('#saveBtn').on('click', saveFields.bind(null, false, function(result)
|
|
{
|
|
if(result) setTimeout(function(){location.href = location.href;}, 1200);
|
|
},
|
|
true
|
|
));
|
|
|
|
$('a.btn.release').on('click', function()
|
|
{
|
|
if(saveFields(function(result)
|
|
{
|
|
if(result) $('a.btn.release').click();
|
|
})) return false;
|
|
});
|
|
|
|
$(document).on('change', '#custom', function()
|
|
{
|
|
var $fieldDefaultValue = $('#fieldDefaultValue');
|
|
if(!$(this).prop('checked'))
|
|
{
|
|
$fieldDefaultValue.replaceWith($('<select id="fieldDefaultValue" name="defaultValue" class="form-control"><option value=""></option><option value="currentTime">' + window.currentTimeLang + '</option></select>'));
|
|
$fieldDefaultValue = $('#fieldDefaultValue');
|
|
}
|
|
else
|
|
{
|
|
$fieldDefaultValue.replaceWith($('<input type="text" id="fieldDefaultValue" name="defaultValue" class="form-control"/>'));
|
|
$fieldDefaultValue = $('#fieldDefaultValue');
|
|
if(window.dateType) $fieldDefaultValue[window.dateType + 'picker']();
|
|
}
|
|
$fieldDefaultValue.data('dateType', window.dateType).attr('autocomplete', 'off');
|
|
});
|
|
|
|
$(document).on('hidden.zui.modal', '#triggerModal', function()
|
|
{
|
|
location.reload();
|
|
});
|
|
|
|
$('#editorPages > .btn, .prevStep, #editorSteps > ul > li > a[href*=flowchart]').on('click', function()
|
|
{
|
|
var url = $(this).attr('href');
|
|
if(saveFields(function(result)
|
|
{
|
|
if(result) setTimeout(function(){location.href = url;}, 1200);
|
|
})) return false;
|
|
});
|
|
|
|
$('.release.btn').on('click', function(){$('#confirmReleaseModal .close').click();});
|
|
|
|
/* Show alert before user leave page without save changes */
|
|
$(window).on('beforeunload', function(e)
|
|
{
|
|
var hasChanges;
|
|
for(var i = 0; i < fields.length; ++i)
|
|
{
|
|
var field = fields[i];
|
|
if(field.changed)
|
|
{
|
|
hasChanges = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(hasChanges)
|
|
{
|
|
e = e || window.event;
|
|
|
|
// IE8, Firefox 4
|
|
if(e) e.returnValue = window.leavePageTip;
|
|
|
|
// Chrome, Safari, Firefox 4+, Opera 12+ , IE 9+
|
|
return window.leavePageTip;
|
|
}
|
|
});
|
|
|
|
setAutoHeight();
|
|
$(window).on('resize', setAutoHeight);
|
|
|
|
/* Clear default error tips when input default. */
|
|
$('#fieldEditDefaultValue').on('input', function(e){
|
|
var field = activedField;
|
|
if(field.errors.default)
|
|
{
|
|
$('#fieldDefaultValueMessage').hide();
|
|
$('#fieldEditDefaultValue').toggleClass('has-error',false);
|
|
delete field.errors.default;
|
|
field.errors.$length --;
|
|
$('#field-' + field.id).toggleClass('has-error', !!(field.errors && field.errors.$length))
|
|
}
|
|
});
|
|
});
|