375 lines
23 KiB
HTML
375 lines
23 KiB
HTML
<div class="layui-card" style="margin:15px;">
|
||
<div class="layui-card-header">
|
||
插件设计器 <span id="addonName" class="layui-badge layui-bg-blue"></span>
|
||
<a href="{:url('addon/index')}" class="layui-btn layui-btn-sm layui-btn-primary" style="float:right;"><i class="layui-icon layui-icon-return"></i> 返回插件列表</a>
|
||
</div>
|
||
<div class="layui-card-body">
|
||
|
||
<!-- 新建模式:填写插件标识与基础信息 -->
|
||
<div id="createBox">
|
||
<fieldset class="layui-elem-field layui-field-title"><legend>新建插件</legend></fieldset>
|
||
<form class="layui-form" lay-filter="createForm" style="max-width:640px;">
|
||
<div class="layui-form-item">
|
||
<label class="layui-form-label">插件标识</label>
|
||
<div class="layui-input-inline" style="width:300px;">
|
||
<input type="text" name="name" id="createName" class="layui-input" placeholder="如 myaddon(字母开头)">
|
||
</div>
|
||
<div class="layui-form-mid layui-word-aux">目录名 / 命名空间标识,创建后不可改</div>
|
||
</div>
|
||
<div class="layui-form-item">
|
||
<label class="layui-form-label">名称</label>
|
||
<div class="layui-input-inline" style="width:300px;"><input type="text" name="title" class="layui-input" placeholder="插件显示名称"></div>
|
||
</div>
|
||
<div class="layui-form-item">
|
||
<label class="layui-form-label">简介</label>
|
||
<div class="layui-input-inline" style="width:300px;"><input type="text" name="intro" class="layui-input"></div>
|
||
</div>
|
||
<div class="layui-form-item">
|
||
<label class="layui-form-label">作者</label>
|
||
<div class="layui-input-inline" style="width:300px;"><input type="text" name="author" class="layui-input"></div>
|
||
</div>
|
||
<div class="layui-form-item">
|
||
<label class="layui-form-label">官网</label>
|
||
<div class="layui-input-inline" style="width:300px;"><input type="text" name="website" class="layui-input"></div>
|
||
</div>
|
||
<div class="layui-form-item">
|
||
<label class="layui-form-label">版本</label>
|
||
<div class="layui-input-inline" style="width:300px;"><input type="text" name="version" value="1.0.0" class="layui-input"></div>
|
||
</div>
|
||
<div class="layui-form-item">
|
||
<label class="layui-form-label">前台入口</label>
|
||
<div class="layui-input-inline" style="width:300px;"><input type="text" name="url" class="layui-input" placeholder="如 /myaddon"></div>
|
||
</div>
|
||
<div class="layui-form-item">
|
||
<div class="layui-input-block">
|
||
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="createBtn"><i class="layui-icon layui-icon-add-1"></i> 创建骨架</button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- 设计模式 -->
|
||
<div id="designBox" style="display:none;">
|
||
<div class="layui-tab" lay-filter="designTab">
|
||
<ul class="layui-tab-title">
|
||
<li class="layui-this">基础信息</li>
|
||
<li>配置项</li>
|
||
<li>菜单</li>
|
||
<li>事件/中间件</li>
|
||
<li>路由</li>
|
||
<li>页面模块</li>
|
||
</ul>
|
||
<div class="layui-tab-content">
|
||
|
||
<!-- 基础信息 -->
|
||
<div class="layui-tab-item layui-show">
|
||
<form class="layui-form" lay-filter="basicForm" style="max-width:640px;">
|
||
<div class="layui-form-item"><label class="layui-form-label">标识</label><div class="layui-input-inline" style="width:300px;"><input type="text" name="name" id="basicName" class="layui-input" readonly></div></div>
|
||
<div class="layui-form-item"><label class="layui-form-label">名称</label><div class="layui-input-inline" style="width:300px;"><input type="text" name="title" class="layui-input"></div></div>
|
||
<div class="layui-form-item"><label class="layui-form-label">简介</label><div class="layui-input-inline" style="width:300px;"><input type="text" name="intro" class="layui-input"></div></div>
|
||
<div class="layui-form-item"><label class="layui-form-label">作者</label><div class="layui-input-inline" style="width:300px;"><input type="text" name="author" class="layui-input"></div></div>
|
||
<div class="layui-form-item"><label class="layui-form-label">官网</label><div class="layui-input-inline" style="width:300px;"><input type="text" name="website" class="layui-input"></div></div>
|
||
<div class="layui-form-item"><label class="layui-form-label">版本</label><div class="layui-input-inline" style="width:300px;"><input type="text" name="version" class="layui-input"></div></div>
|
||
<div class="layui-form-item"><label class="layui-form-label">前台入口</label><div class="layui-input-inline" style="width:300px;"><input type="text" name="url" class="layui-input"></div></div>
|
||
<div class="layui-form-item"><label class="layui-form-label">授权</label><div class="layui-input-inline" style="width:300px;"><input type="text" name="license" class="layui-input"></div></div>
|
||
<div class="layui-form-item"><div class="layui-input-block"><button class="layui-btn" lay-submit lay-filter="basicBtn">保存基础信息</button></div></div>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- 配置项 -->
|
||
<div class="layui-tab-item">
|
||
<button class="layui-btn layui-btn-sm" id="cfgAdd"><i class="layui-icon layui-icon-add-1"></i> 新增配置项</button>
|
||
<table class="layui-table" style="margin-top:10px;">
|
||
<thead><tr><th>名称(name)</th><th>标题(title)</th><th>类型(type)</th><th>默认值(value)</th><th>提示(tip)</th><th>操作</th></tr></thead>
|
||
<tbody id="cfgRows"></tbody>
|
||
</table>
|
||
<button class="layui-btn" id="cfgSave">保存配置项</button>
|
||
</div>
|
||
|
||
<!-- 菜单 -->
|
||
<div class="layui-tab-item">
|
||
<div class="layui-form-item"><label class="layui-form-label">后台菜单</label><div class="layui-input-block"><textarea id="menuBackend" class="layui-textarea" style="min-height:120px;"></textarea></div></div>
|
||
<div class="layui-form-item"><label class="layui-form-label">会员菜单</label><div class="layui-input-block"><textarea id="menuMember" class="layui-textarea" style="min-height:80px;"></textarea></div></div>
|
||
<div class="layui-form-item"><label class="layui-form-label">前台菜单</label><div class="layui-input-block"><textarea id="menuFrontend" class="layui-textarea" style="min-height:80px;"></textarea></div></div>
|
||
<button class="layui-btn" id="menuSave">保存菜单(JSON)</button>
|
||
<div class="layui-form-mid layui-word-aux">格式参考现有插件 menu.json;保存时会校验 JSON。</div>
|
||
</div>
|
||
|
||
<!-- 事件/中间件 -->
|
||
<div class="layui-tab-item">
|
||
<div class="layui-form-item"><label class="layui-form-label">events</label><div class="layui-input-block"><textarea id="evEvents" class="layui-textarea" style="min-height:100px;"></textarea></div></div>
|
||
<div class="layui-form-item"><label class="layui-form-label">middleware</label><div class="layui-input-block"><textarea id="evMiddleware" class="layui-textarea" style="min-height:100px;"></textarea></div></div>
|
||
<div class="layui-form-item"><label class="layui-form-label">services</label><div class="layui-input-block"><textarea id="evServices" class="layui-textarea" style="min-height:80px;"></textarea></div></div>
|
||
<button class="layui-btn" id="hooksSave">保存事件/中间件/服务</button>
|
||
<div class="layui-form-mid layui-word-aux">填写合法 JSON;events 含 bind/listen/subscribe,middleware 含 alias/priority。</div>
|
||
</div>
|
||
|
||
<!-- 路由 -->
|
||
<div class="layui-tab-item">
|
||
<button class="layui-btn layui-btn-sm" id="routeAdd"><i class="layui-icon layui-icon-add-1"></i> 新增路由</button>
|
||
<table class="layui-table" style="margin-top:10px;">
|
||
<thead><tr><th>方法</th><th>路径(path)</th><th>控制器(controller)</th><th>方法(action)</th><th>操作</th></tr></thead>
|
||
<tbody id="routeRows"></tbody>
|
||
</table>
|
||
<button class="layui-btn" id="routeSave">保存路由</button>
|
||
</div>
|
||
|
||
<!-- 页面模块 -->
|
||
<div class="layui-tab-item">
|
||
<form class="layui-form" lay-filter="genForm" style="max-width:560px;">
|
||
<div class="layui-form-item">
|
||
<label class="layui-form-label">类型</label>
|
||
<div class="layui-input-inline" style="width:200px;">
|
||
<select name="gtype" id="genType">
|
||
<option value="controller">控制器 Controller</option>
|
||
<option value="model">模型 Model</option>
|
||
<option value="event">事件 Event</option>
|
||
<option value="listener">监听器 Listener</option>
|
||
<option value="middleware">中间件 Middleware</option>
|
||
<option value="service">服务 Service</option>
|
||
<option value="subscribe">订阅者 Subscribe</option>
|
||
<option value="validate">验证器 Validate</option>
|
||
<option value="command">命令行 Command</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="layui-form-item" id="genLayerBox">
|
||
<label class="layui-form-label">层级</label>
|
||
<div class="layui-input-inline" style="width:200px;">
|
||
<select name="layer" id="genLayer">
|
||
<option value="frontend">前台 frontend</option>
|
||
<option value="backend">后台 backend</option>
|
||
<option value="member">会员 member</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="layui-form-item" id="genKindBox">
|
||
<label class="layui-form-label">风格</label>
|
||
<div class="layui-input-inline" style="width:200px;">
|
||
<select name="kind" id="genKind">
|
||
<option value="default">资源(默认)</option>
|
||
<option value="api">API</option>
|
||
<option value="plain">空白</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="layui-form-item">
|
||
<label class="layui-form-label">类名</label>
|
||
<div class="layui-input-inline" style="width:200px;"><input type="text" name="name" id="genName" class="layui-input" placeholder="如 Category"></div>
|
||
<div class="layui-form-mid layui-word-aux">不含命名空间</div>
|
||
</div>
|
||
<div class="layui-form-item" id="genCmdBox" style="display:none;">
|
||
<label class="layui-form-label">命令名</label>
|
||
<div class="layui-input-inline" style="width:200px;"><input type="text" name="command" id="genCmd" class="layui-input" placeholder="如 myaddon:demo"></div>
|
||
</div>
|
||
<div class="layui-form-item"><div class="layui-input-block"><button class="layui-btn layui-btn-normal" id="genBtn">生成文件</button></div></div>
|
||
</form>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<hr>
|
||
<button class="layui-btn layui-btn-normal" id="btnInstall"><i class="layui-icon layui-icon-ok"></i> 开发安装(建表/注入菜单/启用)</button>
|
||
<button class="layui-btn layui-btn-danger" id="btnRemove"><i class="layui-icon layui-icon-delete"></i> 删除插件</button>
|
||
<span class="layui-word-aux">开发安装后改动实时生效,无需重新打包。</span>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
<script src="/assets/layui/layui.js"></script>
|
||
<script>
|
||
layui.use(['form', 'layer', 'element', 'jquery', 'http'], function () {
|
||
var form = layui.form, layer = layui.layer, element = layui.element, $ = layui.jquery, http = layui.http;
|
||
var addon = '{$addon|default=""}';
|
||
|
||
function showCreate(defName) {
|
||
$('#createBox').show();
|
||
$('#designBox').hide();
|
||
if (defName) $('#createName').val(defName);
|
||
}
|
||
function showDesign() {
|
||
$('#createBox').hide();
|
||
$('#designBox').show();
|
||
}
|
||
|
||
// 初始化:有 addon 则尝试读取,否则新建
|
||
if (addon) {
|
||
http.get('designRead?addon=' + encodeURIComponent(addon)).then(function (res) {
|
||
if (res.code === 0) {
|
||
fillForm(res.data);
|
||
showDesign();
|
||
} else {
|
||
showCreate(addon);
|
||
}
|
||
}).catch(function () { showCreate(addon); });
|
||
} else {
|
||
showCreate('');
|
||
}
|
||
|
||
function fillForm(d) {
|
||
$('#addonName').text(d.name || '');
|
||
addon = d.name || addon;
|
||
var info = d.info || {};
|
||
form.val('basicForm', {
|
||
name: info.name || '', title: info.title || '', intro: info.intro || '',
|
||
author: info.author || '', website: info.website || '', version: info.version || '',
|
||
url: info.url || '', license: info.license || ''
|
||
});
|
||
// 配置项
|
||
$('#cfgRows').empty();
|
||
(d.config || []).forEach(function (f) { addCfgRow(f); });
|
||
// 菜单
|
||
var m = d.menu || {};
|
||
$('#menuBackend').val(JSON.stringify(m.backend || [], null, 2));
|
||
$('#menuMember').val(JSON.stringify(m.member || [], null, 2));
|
||
$('#menuFrontend').val(JSON.stringify(m.frontend || [], null, 2));
|
||
// 事件/中间件
|
||
var ev = info.events || {};
|
||
$('#evEvents').val(JSON.stringify(ev, null, 2));
|
||
$('#evMiddleware').val(JSON.stringify(info.middleware || {}, null, 2));
|
||
$('#evServices').val(JSON.stringify(info.services || [], null, 2));
|
||
// 路由(从 route_raw 解析较复杂,这里留空由用户手动加;如需可解析 TODO)
|
||
}
|
||
|
||
// ===== 新建骨架 =====
|
||
form.on('submit(createBtn)', function (data) {
|
||
http.post('designCreate', data.field).then(function (res) {
|
||
if (res.code === 0) {
|
||
layer.msg('创建成功', { icon: 1 });
|
||
addon = data.field.name;
|
||
location.href = 'design?addon=' + encodeURIComponent(addon);
|
||
} else {
|
||
layer.msg(res.message || '创建失败', { icon: 2 });
|
||
}
|
||
}).catch(function () { layer.msg('请求失败', { icon: 2 }); });
|
||
return false;
|
||
});
|
||
|
||
// ===== 基础信息保存 =====
|
||
form.on('submit(basicBtn)', function (data) {
|
||
var fd = data.field; fd.addon = addon; fd.type = 'basic';
|
||
http.post('designSave', fd).then(function (res) {
|
||
layer.msg(res.code === 0 ? '已保存' : (res.message || '失败'), { icon: res.code === 0 ? 1 : 2 });
|
||
}).catch(function () { layer.msg('请求失败', { icon: 2 }); });
|
||
return false;
|
||
});
|
||
|
||
// ===== 配置项行 =====
|
||
function addCfgRow(f) {
|
||
f = f || {};
|
||
var tr = $('<tr>').append(
|
||
$('<td>').append($('<input class="layui-input cfg-name" value="' + (f.name||'') + '">')),
|
||
$('<td>').append($('<input class="layui-input cfg-title" value="' + (f.title||'') + '">')),
|
||
$('<td>').append($('<input class="layui-input cfg-type" value="' + (f.type||'string') + '">')),
|
||
$('<td>').append($('<input class="layui-input cfg-value" value="' + (f.value||'') + '">')),
|
||
$('<td>').append($('<input class="layui-input cfg-tip" value="' + (f.tip||'') + '">')),
|
||
$('<td>').append($('<button class="layui-btn layui-btn-xs layui-btn-danger cfg-del">删除</button>'))
|
||
);
|
||
$('#cfgRows').append(tr);
|
||
}
|
||
$('#cfgAdd').on('click', function () { addCfgRow(); });
|
||
$('#cfgRows').on('click', '.cfg-del', function () { $(this).closest('tr').remove(); });
|
||
$('#cfgSave').on('click', function () {
|
||
var fields = [];
|
||
$('#cfgRows tr').each(function () {
|
||
var t = $(this);
|
||
fields.push({ name: t.find('.cfg-name').val(), title: t.find('.cfg-title').val(),
|
||
type: t.find('.cfg-type').val(), value: t.find('.cfg-value').val(), tip: t.find('.cfg-tip').val() });
|
||
});
|
||
http.post('designSave', { addon: addon, type: 'config', fields: JSON.stringify(fields) })
|
||
.then(function (res) { layer.msg(res.code === 0 ? '配置已保存' : (res.message || '失败'), { icon: res.code === 0 ? 1 : 2 }); })
|
||
.catch(function () { layer.msg('请求失败', { icon: 2 }); });
|
||
});
|
||
|
||
// ===== 菜单保存 =====
|
||
$('#menuSave').on('click', function () {
|
||
try {
|
||
var menu = {
|
||
backend: JSON.parse($('#menuBackend').val() || '[]'),
|
||
member: JSON.parse($('#menuMember').val() || '[]'),
|
||
frontend: JSON.parse($('#menuFrontend').val() || '[]')
|
||
};
|
||
http.post('designSave', { addon: addon, type: 'menu', menu: JSON.stringify(menu) })
|
||
.then(function (res) { layer.msg(res.code === 0 ? '菜单已保存' : (res.message || '失败'), { icon: res.code === 0 ? 1 : 2 }); })
|
||
.catch(function () { layer.msg('请求失败', { icon: 2 }); });
|
||
} catch (e) { layer.msg('菜单 JSON 格式错误:' + e.message, { icon: 2 }); }
|
||
});
|
||
|
||
// ===== 事件/中间件保存 =====
|
||
$('#hooksSave').on('click', function () {
|
||
try {
|
||
var events = JSON.parse($('#evEvents').val() || '{}');
|
||
var middleware = JSON.parse($('#evMiddleware').val() || '{}');
|
||
var services = JSON.parse($('#evServices').val() || '[]');
|
||
http.post('designSave', { addon: addon, type: 'hooks',
|
||
events: JSON.stringify(events), middleware: JSON.stringify(middleware), services: JSON.stringify(services) })
|
||
.then(function (res) { layer.msg(res.code === 0 ? '已保存' : (res.message || '失败'), { icon: res.code === 0 ? 1 : 2 }); })
|
||
.catch(function () { layer.msg('请求失败', { icon: 2 }); });
|
||
} catch (e) { layer.msg('JSON 格式错误:' + e.message, { icon: 2 }); }
|
||
});
|
||
|
||
// ===== 路由行 =====
|
||
function addRouteRow(r) {
|
||
r = r || {};
|
||
var tr = $('<tr>').append(
|
||
$('<td>').append($('<select class="layui-input rt-method"><option>get</option><option>post</option><option>put</option><option>delete</option><option>any</option></select>').val(r.method||'get')),
|
||
$('<td>').append($('<input class="layui-input rt-path" value="' + (r.path||'') + '">')),
|
||
$('<td>').append($('<input class="layui-input rt-ctrl" value="' + (r.controller||'') + '">')),
|
||
$('<td>').append($('<input class="layui-input rt-action" value="' + (r.action||'index') + '">')),
|
||
$('<td>').append($('<button class="layui-btn layui-btn-xs layui-btn-danger rt-del">删除</button>'))
|
||
);
|
||
$('#routeRows').append(tr);
|
||
}
|
||
$('#routeAdd').on('click', function () { addRouteRow(); });
|
||
$('#routeRows').on('click', '.rt-del', function () { $(this).closest('tr').remove(); });
|
||
$('#routeSave').on('click', function () {
|
||
var routes = [];
|
||
$('#routeRows tr').each(function () {
|
||
var t = $(this);
|
||
routes.push({ method: t.find('.rt-method').val(), path: t.find('.rt-path').val(),
|
||
controller: t.find('.rt-ctrl').val(), action: t.find('.rt-action').val() });
|
||
});
|
||
http.post('designSave', { addon: addon, type: 'route', routes: JSON.stringify(routes) })
|
||
.then(function (res) { layer.msg(res.code === 0 ? '路由已保存' : (res.message || '失败'), { icon: res.code === 0 ? 1 : 2 }); })
|
||
.catch(function () { layer.msg('请求失败', { icon: 2 }); });
|
||
});
|
||
|
||
// ===== 页面模块生成 =====
|
||
$('#genType').on('change', function () {
|
||
var t = $(this).val();
|
||
var isCtrl = (t === 'controller');
|
||
$('#genLayerBox').toggle(isCtrl);
|
||
$('#genKindBox').toggle(isCtrl);
|
||
$('#genCmdBox').toggle(t === 'command');
|
||
});
|
||
$('#genBtn').on('click', function () {
|
||
var gtype = $('#genType').val();
|
||
var opts = { name: $('#genName').val() };
|
||
if (gtype === 'controller') { opts.layer = $('#genLayer').val(); opts.kind = $('#genKind').val(); }
|
||
if (gtype === 'command') { opts.command = $('#genCmd').val(); }
|
||
if (!opts.name) { layer.msg('请填写类名', { icon: 2 }); return; }
|
||
http.post('designGenerate', { addon: addon, gtype: gtype, opts: JSON.stringify(opts) })
|
||
.then(function (res) { layer.msg(res.code === 0 ? ('已生成:' + (res.data.file||'')) : (res.message || '失败'), { icon: res.code === 0 ? 1 : 2 }); })
|
||
.catch(function () { layer.msg('请求失败', { icon: 2 }); });
|
||
});
|
||
|
||
// ===== 开发安装 / 删除 =====
|
||
$('#btnInstall').on('click', function () {
|
||
http.post('designInstall', { addon: addon }).then(function (res) {
|
||
if (res.code === 0) { layer.msg('开发安装成功,菜单已注入', { icon: 1 }); }
|
||
else { layer.msg(res.message || '失败', { icon: 2 }); }
|
||
}).catch(function () { layer.msg('请求失败', { icon: 2 }); });
|
||
});
|
||
$('#btnRemove').on('click', function () {
|
||
layer.confirm('确定删除该插件目录?(开发调试用,不可恢复)', function (index) {
|
||
layer.close(index);
|
||
http.post('designRemove', { addon: addon }).then(function (res) {
|
||
if (res.code === 0) { layer.msg('已删除', { icon: 1 }); location.href = 'index'; }
|
||
else { layer.msg(res.message || '失败', { icon: 2 }); }
|
||
}).catch(function () { layer.msg('请求失败', { icon: 2 }); });
|
||
});
|
||
});
|
||
});
|
||
</script>
|