Files
YwxAppThink/app/backend/view/addon/design.html
T

375 lines
23 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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">填写合法 JSONevents 含 bind/listen/subscribemiddleware 含 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>