mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-10-29 08:58:22 +08:00
原实现三个组件在 JS 顶层用 $.ajax({async:false}) 同步拉取 .html 模板,
每个页面打开固定发 3 个同步请求(阻塞主线程,与组件是否被使用无关),
且路径硬编码 /static/ 与 __STATIC__(OSS 部署)脱节,模板 URL 无版本
参数存在与 js 缓存不同步的漂移问题,请求失败时静默渲染空白。
改造内容:
- 模板改为首次 render() 时异步获取,Deferred 缓存供同页多实例共享,
失败不缓存并在下次 render 自动重试,失败时 console.error + 提示
- 模板 URL 取 CONFIG.STATIC + CONFIG.STATIC_VERSION,layout 的
window.CONFIG 新增这两项注入;无注入时逐级回退 /static 与 VERSION,
兼容下游覆盖 layout 的旧页面
- 组件 CSS 由 _require.html 直接 link 引入(__STATIC__ + 版本键),
替代原 JS 动态注入
- render() 返回 Promise(resolve Vue 实例);仓库内调用均为
fire-and-forget,无兼容影响
293 lines
10 KiB
PHP
293 lines
10 KiB
PHP
(function () {
|
||
// 组件模板懒加载:首次 render 时才异步请求,同页多实例共享一次请求;
|
||
// 路径与版本跟随页面注入的 CONFIG.STATIC / CONFIG.STATIC_VERSION,
|
||
// 与 _require.html 中 js/css 的缓存键保持一致;失败不缓存,下次 render 重试。
|
||
// 样式 propertyInput.css 已改为在 _require.html 中直接引入,不再由 JS 注入。
|
||
var propertyInputTemplatePromise = null;
|
||
|
||
function loadPropertyInputTemplate() {
|
||
if (propertyInputTemplatePromise !== null) {
|
||
return propertyInputTemplatePromise;
|
||
}
|
||
|
||
var config = window.CONFIG || {};
|
||
var staticRoot = config.STATIC || '/static';
|
||
var version = config.STATIC_VERSION || config.VERSION || '';
|
||
var url = staticRoot + '/plugs/lay-module/propertyInput/propertyInput.html?t=' + version;
|
||
var deferred = $.Deferred();
|
||
|
||
$.ajax({
|
||
type: 'get',
|
||
url: url,
|
||
cache: true,
|
||
dataType: 'text'
|
||
}).done(function (template) {
|
||
if ($.trim(template)) {
|
||
deferred.resolve(template);
|
||
} else {
|
||
deferred.reject('模板内容为空');
|
||
}
|
||
}).fail(function (xhr) {
|
||
deferred.reject(xhr);
|
||
});
|
||
|
||
propertyInputTemplatePromise = deferred.promise();
|
||
propertyInputTemplatePromise.fail(function (reason) {
|
||
console.error('propertyInput 模板加载失败: ' + url, reason);
|
||
var ua = window.UA_ADMIN || window.ua;
|
||
if (ua) {
|
||
ua.msg.error('propertyInput 组件初始化失败');
|
||
}
|
||
// 失败不缓存,下次 render 自动重试
|
||
propertyInputTemplatePromise = null;
|
||
});
|
||
|
||
return propertyInputTemplatePromise;
|
||
}
|
||
|
||
var propertyInput = function () { };
|
||
|
||
propertyInput.prototype.render = function (elem, data) {
|
||
|
||
var defaultOption = {
|
||
placeholder: '请选择',
|
||
required: ''
|
||
};
|
||
|
||
var options = $.extend(defaultOption, data);
|
||
|
||
options.value = $.trim(options.value);
|
||
// 处理field参数
|
||
// 如果是字符串,则转为json
|
||
if (typeof options.field === 'string') {
|
||
try {
|
||
options.field = JSON.parse(options.field);
|
||
} catch (e) {
|
||
try {
|
||
options.field = eval('(' + options.field + ')');
|
||
} catch (e) {
|
||
options.field = [];
|
||
console.error('field参数格式错误');
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果没有定义,则初始化
|
||
if (!options.field) {
|
||
options.field = [
|
||
{
|
||
key: 'name',
|
||
title: '名称'
|
||
},
|
||
{
|
||
key: 'value',
|
||
title: '数据'
|
||
}
|
||
];
|
||
} else {
|
||
// 如果是对象,则转成数组
|
||
if (typeof options.field === 'object' && !Array.isArray(options.field)) {
|
||
var field = [];
|
||
for (var key in options.field) {
|
||
field.push({
|
||
key: key,
|
||
title: options.field[key]
|
||
});
|
||
}
|
||
options.field = field;
|
||
} else if (!Array.isArray(options.field) || options.field.length < 1) {
|
||
// 如果field不符合规则,则初始化
|
||
options.field = [
|
||
{
|
||
key: 'name',
|
||
title: '名称'
|
||
},
|
||
{
|
||
key: 'value',
|
||
title: '数据'
|
||
}
|
||
];
|
||
}
|
||
}
|
||
|
||
// 如果field没有key、title、type、default,则初始化
|
||
for (var i = 0; i < options.field.length; i++) {
|
||
if (!options.field[i].key) {
|
||
options.field[i].key = 'name';
|
||
}
|
||
if (!options.field[i].title) {
|
||
options.field[i].title = '名称';
|
||
}
|
||
if (!options.field[i].type) {
|
||
options.field[i].type = 'text';
|
||
}
|
||
if (!options.field[i].default) {
|
||
options.field[i].default = '';
|
||
}
|
||
|
||
|
||
|
||
// 如果type是textarea且没有定义height,则高度为40px
|
||
if (options.field[i].type == 'textarea' && !options.field[i].height) {
|
||
options.field[i].height = '40px';
|
||
}
|
||
|
||
// 如果type是radio
|
||
if (options.field[i].type == 'radio') {
|
||
|
||
// selectList支持以下三种形式的数据
|
||
// 1 ['string1','string2']
|
||
// 2 [{title:'string1',value:'string1'},{title:'string2',value:'string2'}]
|
||
// 3 {string1:'string1',string2:'string2'}
|
||
// 第2个是标准格式
|
||
|
||
|
||
// 如果没有定义selectList
|
||
if (!options.field[i].selectList) {
|
||
options.field[i].selectList = [
|
||
{
|
||
title: '是',
|
||
value: 1
|
||
},
|
||
{
|
||
title: '否',
|
||
value: 0
|
||
}
|
||
];
|
||
}
|
||
|
||
// 如果selectList是数组,但是数组的元素是字符串,则将该元素转为对象,title和value都为该元素
|
||
if (Array.isArray(options.field[i].selectList)) {
|
||
for (var j = 0; j < options.field[i].selectList.length; j++) {
|
||
if (typeof options.field[i].selectList[j] === 'string') {
|
||
options.field[i].selectList[j] = {
|
||
title: options.field[i].selectList[j],
|
||
value: options.field[i].selectList[j]
|
||
};
|
||
}
|
||
}
|
||
} else if (typeof options.field[i].selectList === 'object') {
|
||
// 如果selectList是对象,则将该对象转为数组
|
||
var selectList = [];
|
||
for (var key in options.field[i].selectList) {
|
||
selectList.push({
|
||
title: options.field[i].selectList[key],
|
||
value: key
|
||
});
|
||
}
|
||
options.field[i].selectList = selectList;
|
||
}
|
||
|
||
// selectList必须包含title和value,如果没有则报错
|
||
for (var j = 0; j < options.field[i].selectList.length; j++) {
|
||
if (!options.field[i].selectList[j].title || !options.field[i].selectList[j].value) {
|
||
console.error('selectList的元素必须包含title和value');
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
// 处理value参数
|
||
// 如果是字符串,则转为json
|
||
if (typeof options.value === 'string') {
|
||
try {
|
||
options.value = JSON.parse(options.value);
|
||
} catch (e) {
|
||
options.value = [];
|
||
}
|
||
}
|
||
|
||
// 如果value缺少uid,则生成
|
||
for (var i = 0; i < options.value.length; i++) {
|
||
if (!options.value[i].uid) {
|
||
options.value[i].uid = ua.randdomString();
|
||
}
|
||
}
|
||
|
||
return loadPropertyInputTemplate().then(function (template) {
|
||
return new Vue({
|
||
el: elem,
|
||
data() {
|
||
return {
|
||
setting: options,
|
||
value: '',
|
||
listItem: [
|
||
|
||
],
|
||
originalValue: '',
|
||
field: options.field,
|
||
};
|
||
},
|
||
|
||
created() {
|
||
if (this.setting.value) {
|
||
this.originalValue = $.extend(true, [], this.setting.value);
|
||
this.listItem = $.extend(true, [], this.setting.value);
|
||
this.updateValue(this.listItem);
|
||
|
||
if (this.listItem.length == 0) {
|
||
this.onAddItem();
|
||
}
|
||
}
|
||
|
||
},
|
||
mounted() {
|
||
if (options.required == 1) {
|
||
$(this.$refs['propertyInput']).closest('.layui-form-item').children('.layui-form-label').addClass('required');
|
||
}
|
||
},
|
||
template: template,
|
||
|
||
methods: {
|
||
onResetItem() {
|
||
this.listItem = $.extend(true, [], this.originalValue);
|
||
if (this.listItem.length == 0) {
|
||
this.onAddItem();
|
||
}
|
||
|
||
this.updateValue(this.listItem);
|
||
},
|
||
onAddItem() {
|
||
|
||
var emptyItem = {};
|
||
|
||
emptyItem.uid = ua.randdomString();
|
||
|
||
for (var i = 0; i < this.field.length; i++) {
|
||
emptyItem[this.field[i].key] = this.field[i].default;
|
||
}
|
||
|
||
this.listItem.push(emptyItem);
|
||
|
||
this.updateValue(this.listItem);
|
||
},
|
||
onItemChange(item, index, itemField, event) {
|
||
this.listItem[index][itemField.key] = event.target.value;
|
||
|
||
this.updateValue(this.listItem);
|
||
},
|
||
onItemDelete(item, index) {
|
||
if (this.listItem.length <= 1) {
|
||
this.listItem = [
|
||
{
|
||
name: '',
|
||
value: ''
|
||
}
|
||
];
|
||
} else {
|
||
this.listItem.splice(index, 1);
|
||
}
|
||
|
||
this.updateValue(this.listItem);
|
||
},
|
||
updateValue(newValue) {
|
||
this.value = JSON.stringify(newValue);
|
||
}
|
||
}
|
||
});
|
||
});
|
||
};
|
||
|
||
window.propertyInput = new propertyInput();
|
||
})(); |