chore: 重写初始提交(清空历史,整理后全量提交)
This commit is contained in:
@@ -0,0 +1,271 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>广告管理</title>
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link href="/assets/layui/css/layui.css" rel="stylesheet">
|
||||
<link href="/assets/ywxapp/css/ywxapp.css" rel="stylesheet">
|
||||
<script type="text/javascript">
|
||||
var config = { root: "/" };
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="layui-card">
|
||||
<div class="layui-form layui-card-header layuiadmin-card-header-auto">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<select name="slot" id="slotFilter">
|
||||
<option value="">全部广告位</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<select name="status">
|
||||
<option value="">全部状态</option>
|
||||
<option value="1">启用</option>
|
||||
<option value="0">禁用</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn layuiadmin-btn-useradmin" lay-submit lay-filter="tableSearchButton">
|
||||
<i class="layui-icon layui-icon-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-hide" id="dataTable" lay-filter="dataTable"></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/html" id="dataBar">
|
||||
<div class="layui-btn-group">
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" title="编辑" lay-event="update"><i class="layui-icon layui-icon-edit"></i></a>
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" title="删除" lay-event="delete"><i class="layui-icon layui-icon-delete"></i></a>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="statusTpl">
|
||||
<input type="checkbox" name="status" value="{{d.id}}" lay-skin="switch" lay-text="启用|禁用" lay-filter="statusSwitch" {{ d.status == 1 ? 'checked' : '' }}>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="typeTpl">
|
||||
{{# if(d.type == 1){ }}图片{{# } else { }}代码{{# } }}
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="slotTpl">
|
||||
{{# var m={'home_top':'首页顶部','home_bottom':'首页底部','detail_inline':'详情页内联','sidebar':'侧边栏'}; }}
|
||||
{{ m[d.slot] || d.slot }}
|
||||
</script>
|
||||
|
||||
<!-- 添加/编辑表单 -->
|
||||
<script type="text/html" id="dataFormTpl">
|
||||
<form class="layui-form" style="padding:20px 20px 0;">
|
||||
<input type="hidden" name="id" value="{{d.id||''}}">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">广告位</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="slot" lay-verify="required">
|
||||
<option value="">请选择广告位</option>
|
||||
{{# d.slots.forEach(function(item){ }}
|
||||
<option value="{{item.id}}" {{ d.slot == item.id ? 'selected' : '' }}>{{item.title}}</option>
|
||||
{{# }); }}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">标题</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="title" required lay-verify="required" placeholder="广告标题" class="layui-input" value="{{d.title||''}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">类型</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="type" value="1" title="图片广告" {{ d.type==undefined || d.type==1 ? 'checked' : '' }}>
|
||||
<input type="radio" name="type" value="2" title="代码广告" {{ d.type==2 ? 'checked' : '' }}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">图片</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="image" placeholder="图片URL(图片广告必填)" class="layui-input" value="{{d.image||''}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">跳转链接</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="url" placeholder="点击图片后跳转的URL" class="layui-input" value="{{d.url||''}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">广告代码</label>
|
||||
<div class="layui-input-block">
|
||||
<textarea name="code" placeholder="自定义HTML/JS代码(联盟广告等),代码广告必填" class="layui-textarea">{{d.code||''}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">排序</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="number" name="sort" class="layui-input" value="{{d.sort||0}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="status" value="1" title="启用" {{ d.status==undefined || d.status==1 ? 'checked' : '' }}>
|
||||
<input type="radio" name="status" value="0" title="禁用" {{ d.status==0 ? 'checked' : '' }}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">生效起</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="start_at" id="startAt" placeholder="留空=立即生效" class="layui-input" value="{{d.start_at_text||''}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">生效止</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="end_at" id="endAt" placeholder="留空=长期有效" class="layui-input" value="{{d.end_at_text||''}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-hide">
|
||||
<button class="layui-btn" lay-submit="" lay-filter="wxapp-form-submit" id="wxapp-form-submit">提交</button>
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
||||
|
||||
<script src="/assets/layui/layui.js"></script>
|
||||
<script src="/assets/ywxapp/ywxapp.js" module="backend"></script>
|
||||
<script>
|
||||
layui.use(['http', 'table', 'form', 'laytpl', 'laydate', 'layer'], function () {
|
||||
var $ = layui.$, table = layui.table, form = layui.form, laytpl = layui.laytpl, laydate = layui.laydate, http = layui.http, layer = layui.layer;
|
||||
var slots = [];
|
||||
http.get('/haonav/backend/ad/create').then(function (res) {
|
||||
if (res.code === 0) {
|
||||
slots = res.data || [];
|
||||
var sel = document.getElementById('slotFilter');
|
||||
slots.forEach(function (s) { sel.appendChild(new Option(s.title, s.id)); });
|
||||
form.render('select');
|
||||
} else { layer.msg(res.msg || '加载失败'); }
|
||||
});
|
||||
|
||||
var dataTable = table.render({
|
||||
elem: '#dataTable',
|
||||
defaultToolbar: [{ title: '创建广告', layEvent: 'dataCreate', icon: 'layui-icon-add-1' }, 'filter', 'exports', 'print' ],
|
||||
url: '/haonav/backend/ad/index',
|
||||
page: true, limit: 20,
|
||||
parseData: function (res) {
|
||||
return { code: res.code, msg: res.message || '', count: res.count || 0, data: res.data || [] };
|
||||
},
|
||||
cols: [[
|
||||
{ type: 'checkbox', fixed: 'left' },
|
||||
{ field: 'id', title: 'ID', width: 70, sort: true },
|
||||
{ field: 'slot', title: '广告位', width: 110, templet: '#slotTpl' },
|
||||
{ field: 'title', title: '标题', minWidth: 120 },
|
||||
{ field: 'type', title: '类型', width: 90, templet: '#typeTpl' },
|
||||
{ field: 'image', title: '预览', width: 90, align: 'center', templet: function (d) { return d.type == 1 && d.image ? '<img src="' + d.image + '" style="max-height:32px;max-width:60px;">' : (d.type == 2 ? '<span style="color:#999">代码</span>' : '-'); } },
|
||||
{ field: 'url', title: '链接', minWidth: 120, templet: function (d) { return d.url ? '<a href="' + d.url + '" target="_blank" style="color:#01AAED;">' + d.url + '</a>' : '-'; } },
|
||||
{ field: 'click_count', title: '点击', width: 80, sort: true },
|
||||
{ field: 'sort', title: '排序', width: 80, sort: true, edit: 'text' },
|
||||
{ field: 'status', title: '状态', width: 90, align: 'center', templet: '#statusTpl', unresize: true },
|
||||
{ field: 'create_at', title: '创建时间', width: 160, align: 'center', templet: function (d) { return d.create_at ? d.create_at : '-'; } },
|
||||
{ title: '操作', width: 110, align: 'center', toolbar: '#dataBar', fixed: 'right' }
|
||||
]]
|
||||
});
|
||||
|
||||
table.on('toolbar(dataTable)', function (obj) {
|
||||
if (obj.event === 'dataCreate') { active.dataCreate(); }
|
||||
});
|
||||
|
||||
table.on('edit(dataTable)', function (obj) {
|
||||
if (obj.field === 'sort') {
|
||||
http.post('sort', { id: obj.data.id, sort: obj.value }).then(function (res) {
|
||||
if (res.code === 0) { layer.msg(res.message || '已保存'); }
|
||||
else { layer.msg(res.message || '保存失败'); table.reload('dataTable', {}, true); }
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
table.on('tool(dataTable)', function (elem) {
|
||||
if (elem.event === 'update') { active.dataEdit(elem.data); }
|
||||
else if (elem.event === 'delete') { active.dataDelete([elem.data.id]); }
|
||||
});
|
||||
|
||||
form.on('submit(tableSearchButton)', function (data) {
|
||||
table.reload('dataTable', { where: data.field });
|
||||
});
|
||||
|
||||
form.on('switch(statusSwitch)', function (obj) {
|
||||
var id = this.value, st = obj.elem.checked ? 1 : 0;
|
||||
http.post('status', { id: id, status: st }).then(function (res) {
|
||||
if (res.code === 0) { layer.msg(res.message || '已保存'); }
|
||||
else { layer.msg(res.message || '失败'); obj.elem.checked = !obj.elem.checked; form.render('checkbox'); }
|
||||
});
|
||||
});
|
||||
|
||||
var dataFromFun = function (data, callback, done) {
|
||||
var html = laytpl($('#dataFormTpl').html()).render(data || {});
|
||||
layer.open({
|
||||
title: data.id ? '编辑广告' : '添加广告',
|
||||
content: html, anim: 'slideLeft', offset: 'r',
|
||||
btnAlign: 'l', area: ['440px', '99%'], shade: 0.1, shadeClose: true,
|
||||
btn: ['确定', '取消'],
|
||||
success: function (layero, index) {
|
||||
callback(layero, index);
|
||||
form.render();
|
||||
laydate.render({ elem: '#startAt', type: 'datetime' });
|
||||
laydate.render({ elem: '#endAt', type: 'datetime' });
|
||||
},
|
||||
yes: function (index, layero) {
|
||||
layui.form.on('submit(wxapp-form-submit)', function (elem) {
|
||||
done(layero, index, elem); layui.off('submit(wxapp-form-submit)'); return false;
|
||||
});
|
||||
layero.contents().find('#wxapp-form-submit').trigger('click');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var active = {
|
||||
dataCreate: function () {
|
||||
dataFromFun({ slots: slots }, function () { }, function (layero, index, elem) {
|
||||
var field = elem.field;
|
||||
http.post('save', field).then(function (res) {
|
||||
if (res.code === 0) { layer.close(index); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.message || '失败'); }
|
||||
});
|
||||
});
|
||||
},
|
||||
dataEdit: function (data) {
|
||||
http.get('edit?id=' + data.id).then(function (res) {
|
||||
if (res.code !== 0) { return layer.msg(res.msg || '加载失败'); }
|
||||
var d = res.data.info || {};
|
||||
d.slots = res.data.slots || slots;
|
||||
dataFromFun(d, function () { }, function (layero, index, elem) {
|
||||
var field = elem.field;
|
||||
http.put('update', field).then(function (r) {
|
||||
if (r.code === 0) { layer.close(index); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(r.message || '失败'); }
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
dataDelete: function (ids) {
|
||||
if (!ids.length) { return layer.msg('请选择数据'); }
|
||||
layer.confirm('确定删除吗?', function (index) {
|
||||
http.delete('delete', { ids: ids.join(',') }).then(function (res) {
|
||||
if (res.code === 0) { layer.close(index); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.message || '失败'); }
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,185 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>申请审核</title>
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link href="/assets/layui/css/layui.css" rel="stylesheet">
|
||||
<link href="/assets/ywxapp/css/ywxapp.css" rel="stylesheet">
|
||||
<script type="text/javascript">
|
||||
var config = { root: "/" };
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="layui-card">
|
||||
<div class="layui-form layui-card-header layuiadmin-card-header-auto">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<select name="type">
|
||||
<option value="">全部类型</option>
|
||||
<option value="1">收录/友链</option>
|
||||
<option value="2">广告合作</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<select name="status">
|
||||
<option value="">全部状态</option>
|
||||
<option value="0">待审核</option>
|
||||
<option value="1">已通过</option>
|
||||
<option value="2">已拒绝</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn layuiadmin-btn-useradmin" lay-submit lay-filter="tableSearchButton">
|
||||
<i class="layui-icon layui-icon-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-hide" id="dataTable" lay-filter="dataTable"></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/html" id="dataBar">
|
||||
<div class="layui-btn-group">
|
||||
{{# if(d.status == 0){ }}
|
||||
<a class="layui-btn layui-btn-sm layui-btn-normal" title="通过" lay-event="approve"><i class="layui-icon layui-icon-ok"></i></a>
|
||||
<a class="layui-btn layui-btn-sm layui-btn-warm" title="拒绝" lay-event="reject"><i class="layui-icon layui-icon-close"></i></a>
|
||||
{{# } }}
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" title="删除" lay-event="delete"><i class="layui-icon layui-icon-delete"></i></a>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="typeTpl">
|
||||
{{# if(d.type == 1){ }}<span class="layui-badge layui-bg-blue">收录/友链</span>{{# } else { }}<span class="layui-badge layui-bg-orange">广告合作</span>{{# } }}
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="statusTpl">
|
||||
{{# if(d.status == 0){ }}<span class="layui-badge layui-bg-gray">待审核</span>
|
||||
{{# } else if(d.status == 1){ }}<span class="layui-badge layui-bg-green">已通过</span>
|
||||
{{# } else { }}<span class="layui-badge">已拒绝</span>{{# } }}
|
||||
</script>
|
||||
|
||||
<!-- 通过(友链需选分类) -->
|
||||
<script type="text/html" id="approveFormTpl">
|
||||
<form class="layui-form" style="padding:20px 20px 0;">
|
||||
<input type="hidden" name="id" value="{{d.id}}">
|
||||
{{# if(d.type == 1){ }}
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">收录分类</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="cid" lay-verify="required">
|
||||
<option value="">请选择分类</option>
|
||||
{{# d.cates.forEach(function(c){ }}
|
||||
<option value="{{c.id}}">{{c.title}}</option>
|
||||
{{# }); }}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">上架方式</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="online" value="1" title="直接上架" checked>
|
||||
<input type="radio" name="online" value="0" title="进待审核">
|
||||
</div>
|
||||
</div>
|
||||
{{# } }}
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">审核备注</label>
|
||||
<div class="layui-input-block">
|
||||
<textarea name="reply" placeholder="选填,将保留在申请记录中" class="layui-textarea"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-hide">
|
||||
<button class="layui-btn" lay-submit="" lay-filter="wxapp-form-submit" id="wxapp-form-submit">提交</button>
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
||||
|
||||
<script src="/assets/layui/layui.js"></script>
|
||||
<script src="/assets/ywxapp/ywxapp.js" module="backend"></script>
|
||||
<script>
|
||||
layui.use(['http', 'table', 'form', 'laytpl', 'layer'], function () {
|
||||
var $ = layui.$, table = layui.table, form = layui.form, laytpl = layui.laytpl, http = layui.http, layer = layui.layer;
|
||||
var cates = [];
|
||||
http.get('/haonav/backend/apply/create').then(function (res) {
|
||||
if (res.code === 0) { cates = res.data || []; }
|
||||
});
|
||||
|
||||
table.render({
|
||||
elem: '#dataTable',
|
||||
defaultToolbar: ['filter', 'exports', 'print'],
|
||||
url: '/haonav/backend/apply/index',
|
||||
page: true, limit: 20,
|
||||
parseData: function (res) {
|
||||
return { code: res.code, msg: res.message || '', count: res.count || 0, data: res.data || [] };
|
||||
},
|
||||
cols: [[
|
||||
{ type: 'checkbox', fixed: 'left' },
|
||||
{ field: 'id', title: 'ID', width: 70, sort: true },
|
||||
{ field: 'type', title: '类型', width: 110, templet: '#typeTpl' },
|
||||
{ field: 'title', title: '名称', minWidth: 120 },
|
||||
{ field: 'url', title: '网址', minWidth: 160, templet: function (d) { return d.url ? '<a href="' + d.url + '" target="_blank" rel="nofollow" style="color:#01AAED;">' + d.url + '</a>' : '-'; } },
|
||||
{ field: 'contact', title: '联系方式', width: 140 },
|
||||
{ field: 'slot', title: '意向广告位', width: 110, templet: function (d) { var m = { 'home_top': '首页顶部', 'home_bottom': '首页底部', 'detail_inline': '详情页内联', 'sidebar': '侧边栏' }; return m[d.slot] || (d.slot || '-'); } },
|
||||
{ field: 'description', title: '说明', minWidth: 140 },
|
||||
{ field: 'status', title: '状态', width: 90, align: 'center', templet: '#statusTpl' },
|
||||
{ field: 'reply', title: '审核备注', minWidth: 110, templet: function (d) { return d.reply || '-'; } },
|
||||
{ field: 'ip', title: 'IP', width: 130 },
|
||||
{ field: 'create_at', title: '提交时间', width: 160, align: 'center', templet: function (d) { return d.create_at || '-'; } },
|
||||
{ title: '操作', width: 150, align: 'center', toolbar: '#dataBar', fixed: 'right' }
|
||||
]]
|
||||
});
|
||||
|
||||
form.on('submit(tableSearchButton)', function (data) {
|
||||
table.reload('dataTable', { where: data.field });
|
||||
});
|
||||
|
||||
table.on('tool(dataTable)', function (elem) {
|
||||
var d = elem.data;
|
||||
if (elem.event === 'approve') {
|
||||
d.cates = cates;
|
||||
var html = laytpl($('#approveFormTpl').html()).render(d);
|
||||
layer.open({
|
||||
title: '通过申请 #' + d.id,
|
||||
content: html, anim: 'slideLeft', offset: 'r',
|
||||
btnAlign: 'l', area: ['420px', '99%'], shade: 0.1, shadeClose: true,
|
||||
btn: ['确定', '取消'],
|
||||
success: function () { form.render(); },
|
||||
yes: function (index, layero) {
|
||||
layui.form.on('submit(wxapp-form-submit)', function (el) {
|
||||
http.post('approve', el.field).then(function (res) {
|
||||
if (res.code === 0) { layer.msg(res.message || '已通过'); layer.close(index); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.message || '失败'); }
|
||||
});
|
||||
layui.off('submit(wxapp-form-submit)'); return false;
|
||||
});
|
||||
layero.contents().find('#wxapp-form-submit').trigger('click');
|
||||
}
|
||||
});
|
||||
} else if (elem.event === 'reject') {
|
||||
layer.prompt({ formType: 2, title: '拒绝原因(将保留在记录中,可留空)' }, function (val, index) {
|
||||
http.post('reject', { id: d.id, reply: val || '' }).then(function (res) {
|
||||
if (res.code === 0) { layer.msg(res.message || '已拒绝'); layer.close(index); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.message || '失败'); }
|
||||
});
|
||||
});
|
||||
} else if (elem.event === 'delete') {
|
||||
layer.confirm('确定删除该申请吗?', function (index) {
|
||||
http.delete('delete', { ids: String(d.id) }).then(function (res) {
|
||||
if (res.code === 0) { layer.close(index); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.message || '失败'); }
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,420 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{$title | default='YwxApp'}</title>
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link href="/assets/layui/css/layui.css" rel="stylesheet">
|
||||
<link href="/assets/ywxapp/css/ywxapp.css" rel="stylesheet">
|
||||
|
||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements. All other JS at the end of file. -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="__CDN__/assets/js/html5shiv.js"></script>
|
||||
<script src="__CDN__/assets/js/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
<script type="text/javascript">
|
||||
var config = {
|
||||
root: "/",
|
||||
};
|
||||
var require = {
|
||||
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="layui-card">
|
||||
<div class="layui-form layui-card-header layuiadmin-card-header-auto">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<input type="text" name="title" placeholder="请输入标题" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn layuiadmin-btn-useradmin" lay-submit lay-filter="tableSearchButton">
|
||||
<i class="layui-icon layui-icon-search layuiadmin-button-btn"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-hide" id="dataTable" lay-filter="dataTable"></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/html" id="dataBarTpl">
|
||||
<div class="layui-btn-group">
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" title="编辑节点" lay-event="update"><i class="layui-icon layui-icon-edit"></i> </a>
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" title="添加子节点" lay-event="create"><i class="layui-icon layui-icon-add-1"></i> </a>
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" title="删除节点" lay-event="delete"><i class="layui-icon layui-icon-delete"></i> </a>
|
||||
</div>
|
||||
</script>
|
||||
<!-- 添加/编辑菜单表单模板 -->
|
||||
<script type="text/html" id="dataFormTpl">
|
||||
<form class="layui-form layui-form-pane" lay-filter="wxapp-form" id="wxapp-form">
|
||||
<input type="hidden" name="id" value="{{= d.id || '' }}">
|
||||
<input type="hidden" name="pid" value="{{= d.pid || 0 }}">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">标题</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="title" required lay-verify="required" placeholder="请输入标题" value="{{ d.title || '' }}" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">图标</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="icon" placeholder="请选择图标" value="{{ d.icon ? d.icon.replace('layui-icon ', '') : '' }}" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item ">
|
||||
<label class="layui-form-label">摘要</label>
|
||||
<div class="layui-input-block">
|
||||
<input name="summary" placeholder="请输入摘要" class="layui-input" value="{{ d.summary || '' }}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">关键词</label>
|
||||
<div class="layui-input-block">
|
||||
<textarea name="keywords" placeholder="请输入关键词" class="layui-textarea">{{d.keywords||''}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">描述</label>
|
||||
<div class="layui-input-block">
|
||||
<textarea name="description" placeholder="请输入描述信息" class="layui-textarea">{{d.description||''}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">排序</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="number" name="sort" value="{{ d.sort || 0 }}" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="checkbox" name="status" lay-skin="switch" lay-text="启用|禁用" {{ d.status != 0 ? 'checked' : (!d.id ? 'checked' : '') }}>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item layui-hide">
|
||||
<button class="layui-btn" lay-submit="" lay-filter="wxapp-form-submit" id="wxapp-form-submit">提交</button>
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
||||
<!-- 状态开关 -->
|
||||
<script type="text/html" id="statusTpl">
|
||||
<input type="checkbox" name="status" value="{{d.id}}" lay-skin="switch" lay-text="启用|禁用" lay-filter="statusSwitch" {{ d.status == 1 ? 'checked' : '' }}>
|
||||
</script>
|
||||
<script type="text/html" id="dataRecybinTpl">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header"> </div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-hide" id="dataRecybinTable" lay-filter="dataRecybinTable"></table>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
</script>
|
||||
<script src="/assets/layui/layui.js"></script>
|
||||
<script src="/assets/ywxapp/ywxapp.js" module="backend"></script>
|
||||
<script>
|
||||
layui.use(['layer', 'http'], function () {
|
||||
var $ = layui.$;
|
||||
var treeTable = layui.treeTable;
|
||||
var form = layui.form;
|
||||
var layer = layui.layer;
|
||||
var laytpl = layui.laytpl;
|
||||
var http = layui.http;
|
||||
console.log('layui.treeTable:', layui.setter.layerArea()); // 调试输出
|
||||
treeTable.render({
|
||||
defaultToolbar: [{
|
||||
title: '创建资源', layEvent: 'dataCreate', icon: 'layui-icon-add-1'
|
||||
}, {
|
||||
title: '删除资源', layEvent: 'dataDelete', icon: 'layui-icon-delete'
|
||||
}, {
|
||||
title: '资源回收站', layEvent: 'dataRecybin', icon: 'layui-icon-home'
|
||||
}, 'filter', 'exports', 'print'
|
||||
],
|
||||
elem: '#dataTable',
|
||||
url: 'category/index',
|
||||
parseData: function (res) { // 预处理返回数据
|
||||
console.log('原始数据:', res); // 调试输入
|
||||
return {
|
||||
code: res.code === 0 ? 0 : 1, // 0 标识成功
|
||||
data: res.data || [], // 数据数组
|
||||
msg: res.message || '' // 错误信息
|
||||
};
|
||||
},
|
||||
tree: {
|
||||
customName: {
|
||||
children: "children",
|
||||
isParent: "is_parent",
|
||||
name: "title",
|
||||
id: "id",
|
||||
pid: "pid",
|
||||
icon: "icon"
|
||||
},
|
||||
data: { isSimpleData: true, rootPid: 0 },
|
||||
view: {},
|
||||
async: {},
|
||||
callback: {}
|
||||
},
|
||||
height: 'full-100',
|
||||
toolbar: '#tableBarTpl',
|
||||
cols: [[
|
||||
{ type: 'checkbox', fixed: 'left' },
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true, fixed: 'left' },
|
||||
{ field: 'title', title: '名称', width: 180, fixed: 'left' },
|
||||
{ field: 'icon', title: '图标', width: 80 },
|
||||
{ field: 'sort', title: '排序', width: 80, sort: true },
|
||||
{ field: 'status', title: '状态', width: 96, align: 'center', templet: '#statusTpl' },
|
||||
{ fixed: "right", title: "操作", align: "center", toolbar: "#dataBarTpl", fixed: 'right' }
|
||||
]],
|
||||
page: true,
|
||||
done: function (res, curr, count, origin) { }
|
||||
});
|
||||
treeTable.on('toolbar(dataTable)', function (obj) {
|
||||
var options = obj.config;
|
||||
switch (obj.event) {
|
||||
case 'dataCreate':
|
||||
active.dataCreate({ pid: 0, type: 1 });
|
||||
break;
|
||||
case 'dataDelete':
|
||||
var checkStatus = treeTable.checkStatus('dataTable'), checkData = checkStatus.data;
|
||||
let ids = checkData.map((item, index, array) => { return item.id; });
|
||||
active.dataDelete(ids);
|
||||
break;
|
||||
case 'dataRecybin':
|
||||
active.dataRecybin();
|
||||
break;
|
||||
};
|
||||
});
|
||||
treeTable.on('tool(dataTable)', function (elem) {
|
||||
var data = elem.data;
|
||||
switch (elem.event) {
|
||||
case 'update':
|
||||
active.dataEdit(data);
|
||||
break;
|
||||
case 'create':
|
||||
active.dataCreate({ pid: data.id });
|
||||
break;
|
||||
case 'delete':
|
||||
active.dataDelete([data.id]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
form.on('submit(tableSearchButton)', function (data) {
|
||||
var field = data.field;
|
||||
treeTable.reload('dataTable', {
|
||||
where: field
|
||||
});
|
||||
});
|
||||
// 状态开关
|
||||
form.on('switch(statusSwitch)', function (obj) {
|
||||
var id = this.value;
|
||||
var status = obj.elem.checked ? 1 : 0;
|
||||
layer.confirm('确定要' + (status ? '启用' : '禁用') + '该数据吗?', function (index) {
|
||||
layui.http.put('update', { id: id, status: status }).then(function (res) {
|
||||
if (res.code == 0) {
|
||||
layer.msg(res.msg, { icon: 1 });
|
||||
table.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg, { icon: 2 });
|
||||
obj.elem.checked = !obj.elem.checked;
|
||||
form.render('checkbox');
|
||||
}
|
||||
});
|
||||
layer.close(index);
|
||||
}, function () {
|
||||
obj.elem.checked = !obj.elem.checked;
|
||||
form.render('checkbox');
|
||||
});
|
||||
});
|
||||
|
||||
var dataFromFun = function (data, callback, done) {
|
||||
var formHtml = laytpl($('#dataFormTpl').html()).render(data || {});
|
||||
layer.open({
|
||||
title: data.id ? '编辑数据' : (data.pid ? '添加子数据' : '添加根数据'),
|
||||
content: formHtml,
|
||||
anim: "slideLeft",
|
||||
offset: "r",
|
||||
btnAlign: "l",
|
||||
area: layui.setter.layerArea(),
|
||||
shade: 0.1,
|
||||
shadeClose: true,
|
||||
btn: ['确定', '取消'],
|
||||
success: function (layero, index) {
|
||||
callback(layero, index);
|
||||
form.render();
|
||||
},
|
||||
yes: function (index, layero) {
|
||||
window.layui.form.on('submit(wxapp-form-submit)', function (elem) {
|
||||
done(layero, index, elem);
|
||||
layui.off('submit(wxapp-form)', 'from');
|
||||
return false;
|
||||
});
|
||||
layero.contents().find("#wxapp-form-submit").trigger('click');
|
||||
}
|
||||
});
|
||||
};
|
||||
//事件
|
||||
var active = {
|
||||
dataCreate: function (data = {}) {
|
||||
dataFromFun(data,
|
||||
function (layero, index) { },
|
||||
function (layero, index, elem) {
|
||||
var field = elem.field;
|
||||
field.icon = 'layui-icon ' + field.icon;
|
||||
field.status = field.status ? 1 : 0;
|
||||
http.post('save', field)
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
layer.close(index);
|
||||
treeTable.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg || '操作失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
dataEdit: function (data = {}) {
|
||||
dataFromFun(data,
|
||||
function (layero, index) { },
|
||||
function (layero, index, elem) {
|
||||
var field = elem.field;
|
||||
field.icon = 'layui-icon ' + field.icon;
|
||||
field.status = field.status ? 1 : 0;
|
||||
http.put('update', field)
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
layer.close(index);
|
||||
treeTable.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg || '操作失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
dataDelete: function (ids, force = 0) {
|
||||
if (ids.length === 0) {
|
||||
return layer.msg('请选择数据');
|
||||
}
|
||||
layer.prompt({
|
||||
formType: 1
|
||||
, title: '敏感操作,请验证口令'
|
||||
}, function (value, index) {
|
||||
layer.close(index);
|
||||
layer.confirm('确定删除吗?', function (index) {
|
||||
http.delete('delete', { ids: ids.join(','), force: force })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
layer.close(index);
|
||||
treeTable.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg || '操作失败');
|
||||
}
|
||||
});
|
||||
layer.msg('已删除');
|
||||
treeTable.reload('dataTable', {}, true);
|
||||
});
|
||||
});
|
||||
},
|
||||
dataRecybin: function (data = {}) {
|
||||
var formHtml = laytpl($('#dataRecybinTpl').html()).render(data || {});
|
||||
layer.open({
|
||||
title: '资源回收站',
|
||||
content: formHtml,
|
||||
anim: "slideLeft",
|
||||
offset: "r",
|
||||
area: ['60%', '99%'],
|
||||
shade: 0.1,
|
||||
shadeClose: true,
|
||||
success: function (layero, index) {
|
||||
layui.table.render({
|
||||
elem: '#dataRecybinTable',
|
||||
url: 'recyclebin',
|
||||
height: 'full-100',
|
||||
defaultToolbar: [{
|
||||
title: '批量删除数据',
|
||||
layEvent: 'dataDelete',
|
||||
icon: 'layui-icon-delete',
|
||||
onClick: function (obj) {
|
||||
var checkStatus = treeTable.checkStatus('dataRecybinTable'), checkData = checkStatus.data;
|
||||
let ids = checkData.map((item, index, array) => { return item.id; });
|
||||
console.log(ids);
|
||||
}
|
||||
}, 'filter', 'exports', 'print'],
|
||||
cols: [[
|
||||
{ type: 'checkbox', fixed: 'left' },
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true, fixed: 'left' },
|
||||
{ field: 'title', title: '标题', width: 180, fixed: 'left' },
|
||||
{
|
||||
fixed: "right", title: "操作", width: 120, align: "center", templet: function (d) {
|
||||
return `<div class="layui-btn-group">
|
||||
<a class="layui-btn layui-btn-sm" title="恢复数据" lay-event="restore" > <i class="layui-icon layui-icon-edit"></i> </a >
|
||||
<a class="layui-btn layui-btn-sm" title="删除数据" lay-event="delete"><i class="layui-icon layui-icon-delete"></i> </a>
|
||||
</div >`;
|
||||
}
|
||||
}
|
||||
]],
|
||||
page: true,
|
||||
done: function (res, curr, count, origin) {
|
||||
layui.table.on('tool(dataRecybinTable)', function (elem) {
|
||||
var data = elem.data;
|
||||
switch (elem.event) {
|
||||
case 'restore':
|
||||
active.dataRestore([data.id]);
|
||||
break;
|
||||
case 'delete':
|
||||
active.dataDelete([data.id], 1);
|
||||
break;
|
||||
}
|
||||
});
|
||||
layui.table.on('toolbar(dataRecybinTable)', function (elem) {
|
||||
switch (elem.event) {
|
||||
case 'dataDelete':
|
||||
var checkStatus = table.checkStatus('dataRecybinTable'), checkData = checkStatus.data;
|
||||
let idss = checkData.map((item, index, array) => { return item.id; });
|
||||
active.dataDelete(idss, 1);
|
||||
break;
|
||||
case 'dataRestore':
|
||||
var checkStatus = table.checkStatus('dataRecybinTable'), checkData = checkStatus.data;
|
||||
let ids = checkData.map((item, index, array) => { return item.id; });
|
||||
active.dataRestore(ids);
|
||||
break;
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
dataRestore: function (ids) {
|
||||
if (ids.length === 0) {
|
||||
return layer.msg('请选择数据');
|
||||
}
|
||||
http.put('restore', { ids: ids.join(',') })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
layer.msg('恢复成功');
|
||||
table.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg || '恢复失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,351 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{$title | default='YwxApp'}</title>
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link href="/assets/layui/css/layui.css" rel="stylesheet">
|
||||
<link href="/assets/ywxapp/css/ywxapp.css" rel="stylesheet">
|
||||
|
||||
<!-- HTML5 shim, for IE6-8 support of HTML5 elements. All other JS at the end of file. -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="__CDN__/assets/js/html5shiv.js"></script>
|
||||
<script src="__CDN__/assets/js/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
<script type="text/javascript">
|
||||
var config = {
|
||||
root: "/",
|
||||
};
|
||||
var require = {
|
||||
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
padding: 20px;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.layui-tab-content {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.form-item-tip {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.form-group-title {
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="form-container">
|
||||
<div id="formView"></div>
|
||||
</div>
|
||||
|
||||
<!-- 表单模板 -->
|
||||
<script type="text/html" id="formTpl">
|
||||
<div class="layui-tab layui-tab-brief" lay-filter="formTab">
|
||||
<ul class="layui-tab-title">
|
||||
{{# layui.each(d.groups, function(index, group){ }}
|
||||
<li class="{{# if(index === 0){ }}layui-this{{# } }}">{{ group }}</li>
|
||||
{{# }); }}
|
||||
</ul>
|
||||
|
||||
<div class="layui-tab-content">
|
||||
{{# layui.each(d.groups, function(groupIndex, group){ }}
|
||||
<div class="layui-tab-item {{# if(groupIndex === 0){ }}layui-show{{# } }}">
|
||||
<form class="layui-form" lay-filter="form-{{ group }}">
|
||||
<div class="form-group-title">{{ group }} 设置</div>
|
||||
|
||||
{{# layui.each(d.data[group], function(fieldIndex, field){ }}
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">{{ field.title }}</label>
|
||||
<div class="layui-input-block">
|
||||
{{# if(field.type === 'string' || field.type === 'text'){ }}
|
||||
<!-- 文本输入框 -->
|
||||
<input type="text"
|
||||
name="{{ field.name }}"
|
||||
value="{{ field.value || '' }}"
|
||||
placeholder="{{ field.tip || '请输入' + field.title }}"
|
||||
class="layui-input"
|
||||
{{# if(field.rule === 'required'){ }}lay-verify="required"{{# } }}>
|
||||
|
||||
{{# } else if(field.type === 'textarea'){ }}
|
||||
<!-- 文本域 -->
|
||||
<textarea name="{{ field.name }}"
|
||||
placeholder="{{ field.tip || '请输入' + field.title }}"
|
||||
class="layui-textarea"
|
||||
{{# if(field.rule === 'required'){ }}lay-verify="required"{{# } }}>{{ field.value || '' }}</textarea>
|
||||
|
||||
{{# } else if(field.type === 'select'){ }}
|
||||
<!-- 下拉选择框 -->
|
||||
<select name="{{ field.name }}"
|
||||
{{# if(field.rule === 'required'){ }}lay-verify="required"{{# } }}>
|
||||
<option value="">请选择{{ field.title }}</option>
|
||||
{{#
|
||||
var options = field.content ? field.content.split(',') : [];
|
||||
layui.each(options, function(optIndex, option){
|
||||
}}
|
||||
<option value="{{ option }}" {{# if(field.value == option){ }}selected{{# } }}>{{ option }}</option>
|
||||
{{# }); }}
|
||||
</select>
|
||||
|
||||
{{# } else if(field.type === 'checkbox'){ }}
|
||||
<!-- 复选框 -->
|
||||
{{#
|
||||
var checkboxes = field.content ? field.content.split(',') : [];
|
||||
layui.each(checkboxes, function(cbIndex, cbValue){
|
||||
}}
|
||||
<input type="checkbox"
|
||||
name="{{ field.name }}"
|
||||
title="{{ cbValue }}"
|
||||
value="{{ cbValue }}"
|
||||
{{# if(field.value && field.value.indexOf(cbValue) !== -1){ }}checked{{# } }}
|
||||
lay-skin="primary">
|
||||
{{# }); }}
|
||||
|
||||
{{# } else if(field.type === 'radio'){ }}
|
||||
<!-- 单选框 -->
|
||||
{{#
|
||||
var radios = field.content ? field.content.split(',') : [];
|
||||
layui.each(radios, function(rdIndex, rdValue){
|
||||
}}
|
||||
<input type="radio"
|
||||
name="{{ field.name }}"
|
||||
title="{{ rdValue }}"
|
||||
value="{{ rdValue }}"
|
||||
{{# if(field.value == rdValue){ }}checked{{# } }}>
|
||||
{{# }); }}
|
||||
|
||||
{{# } else if(field.type === 'number'){ }}
|
||||
<!-- 数字输入框 -->
|
||||
<input type="number"
|
||||
name="{{ field.name }}"
|
||||
value="{{ field.value || '' }}"
|
||||
placeholder="{{ field.tip || '请输入' + field.title }}"
|
||||
class="layui-input"
|
||||
{{# if(field.rule === 'required'){ }}lay-verify="required|number"{{# } }}>
|
||||
|
||||
{{# } else if(field.type === 'date'){ }}
|
||||
<!-- 日期选择器 -->
|
||||
<input type="text"
|
||||
name="{{ field.name }}"
|
||||
value="{{ field.value || '' }}"
|
||||
placeholder="{{ field.tip || '请选择日期' }}"
|
||||
class="layui-input"
|
||||
id="date-{{ field.id }}">
|
||||
{{# } }}
|
||||
|
||||
{{# if(field.tip){ }}
|
||||
<div class="form-item-tip">{{ field.tip }}</div>
|
||||
{{# } }}
|
||||
</div>
|
||||
</div>
|
||||
{{# }); }}
|
||||
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button type="button" class="layui-btn" lay-submit lay-filter="submit-{{ group }}">保存设置</button>
|
||||
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{{# }); }}
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<script src="/assets/layui/layui.js"></script>
|
||||
<script src="/assets/ywxapp/ywxapp.js" module="backend"></script>
|
||||
<script>// 原始数据
|
||||
var formData = [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "site_name",
|
||||
"group": "basic",
|
||||
"title": "站点名称",
|
||||
"tip": "请填写站点名称",
|
||||
"type": "string",
|
||||
"value": "YFCMF-TP6",
|
||||
"content": "",
|
||||
"rule": "required",
|
||||
"extend": "",
|
||||
"setting": ""
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "beian",
|
||||
"group": "basic",
|
||||
"title": "备案号",
|
||||
"tip": "",
|
||||
"type": "string",
|
||||
"value": "",
|
||||
"content": "",
|
||||
"rule": "",
|
||||
"extend": "",
|
||||
"setting": ""
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "site_description",
|
||||
"group": "basic",
|
||||
"title": "站点描述",
|
||||
"tip": "请填写站点描述",
|
||||
"type": "textarea",
|
||||
"value": "这是一个基于TP6的CMS系统",
|
||||
"content": "",
|
||||
"rule": "",
|
||||
"extend": "",
|
||||
"setting": ""
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "site_status",
|
||||
"group": "advanced",
|
||||
"title": "站点状态",
|
||||
"tip": "选择站点是否开启",
|
||||
"type": "select",
|
||||
"value": "1",
|
||||
"content": "开启,关闭,维护中",
|
||||
"rule": "required",
|
||||
"extend": "",
|
||||
"setting": ""
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "allow_comment",
|
||||
"group": "advanced",
|
||||
"title": "允许评论",
|
||||
"tip": "是否允许访客评论",
|
||||
"type": "radio",
|
||||
"value": "1",
|
||||
"content": "是,否",
|
||||
"rule": "",
|
||||
"extend": "",
|
||||
"setting": ""
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": "features",
|
||||
"group": "advanced",
|
||||
"title": "功能模块",
|
||||
"tip": "选择启用的功能模块",
|
||||
"type": "checkbox",
|
||||
"value": "文章,评论",
|
||||
"content": "文章,评论,用户,统计",
|
||||
"rule": "",
|
||||
"extend": "",
|
||||
"setting": ""
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "max_upload_size",
|
||||
"group": "upload",
|
||||
"title": "最大上传大小",
|
||||
"tip": "单位:MB",
|
||||
"type": "number",
|
||||
"value": "10",
|
||||
"content": "",
|
||||
"rule": "required",
|
||||
"extend": "",
|
||||
"setting": ""
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"name": "upload_date",
|
||||
"group": "upload",
|
||||
"title": "上传日期限制",
|
||||
"tip": "选择可上传文件的日期范围",
|
||||
"type": "date",
|
||||
"value": "2024-01-01",
|
||||
"content": "",
|
||||
"rule": "",
|
||||
"extend": "",
|
||||
"setting": ""
|
||||
}
|
||||
];
|
||||
|
||||
// 处理数据:按group分组
|
||||
function processData(data) {
|
||||
var groups = {};
|
||||
var groupNames = [];
|
||||
// 收集所有分组
|
||||
data.forEach(function (item) {
|
||||
if (!groups[item.group]) {
|
||||
groups[item.group] = [];
|
||||
groupNames.push(item.group);
|
||||
}
|
||||
groups[item.group].push(item);
|
||||
});
|
||||
return {
|
||||
groups: groupNames,
|
||||
data: groups
|
||||
};
|
||||
}
|
||||
|
||||
// 初始化Layui
|
||||
layui.use(['laytpl', 'form', 'element', 'laydate', 'http'], function (exports) {
|
||||
var laytpl = layui.laytpl;
|
||||
var form = layui.form;
|
||||
var element = layui.element;
|
||||
var laydate = layui.laydate;
|
||||
var http = layui.http;
|
||||
http.get('index').then(respon => {
|
||||
let respona = processData(respon.data && respon.data.length ? respon.data : formData);
|
||||
var getTpl = document.getElementById('formTpl').innerHTML;
|
||||
var view = document.getElementById('formView');
|
||||
laytpl(getTpl).render(respona, function (html) {
|
||||
view.innerHTML = html;
|
||||
// 重新渲染表单元素
|
||||
form.render();
|
||||
element.render('tab');
|
||||
// 初始化日期选择器
|
||||
formData.forEach(function (item) {
|
||||
if (item.type === 'date') {
|
||||
laydate.render({
|
||||
elem: '#date-' + item.id,
|
||||
format: 'yyyy-MM-dd'
|
||||
});
|
||||
}
|
||||
});
|
||||
// 监听表单提交
|
||||
form.on('submit', function (data) {
|
||||
console.log('表单提交数据:', data.field);
|
||||
http.post('update', data.field).then(respon => {
|
||||
console.log(respon)
|
||||
});
|
||||
layer.msg('表单提交成功!已输出到控制台');
|
||||
return false;
|
||||
});
|
||||
});
|
||||
})
|
||||
// 处理数据
|
||||
//var processedData = processData(formData);
|
||||
// 渲染模板
|
||||
// exports('configure', {});
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,155 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{$title | default='数据概览'}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link href="/assets/layui/css/layui.css" rel="stylesheet">
|
||||
<link href="/assets/ywxapp/css/ywxapp.css" rel="stylesheet">
|
||||
<style>
|
||||
body { padding: 16px; background: #f5f6fa; }
|
||||
.stat-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 16px; }
|
||||
.stat-card { background: #fff; border-radius: 8px; padding: 16px; box-shadow: 0 1px 6px rgba(0,0,0,.06); }
|
||||
.stat-card .num { font-size: 26px; font-weight: 700; color: #1E9FFF; }
|
||||
.stat-card .lbl { color: #666; font-size: 13px; margin-top: 4px; }
|
||||
.panel { background: #fff; border-radius: 8px; padding: 16px; margin-bottom: 16px; box-shadow: 0 1px 6px rgba(0,0,0,.06); }
|
||||
.panel h3 { font-size: 15px; margin-bottom: 12px; border-left: 3px solid #1E9FFF; padding-left: 8px; }
|
||||
.bar-row { display: flex; align-items: center; margin-bottom: 8px; }
|
||||
.bar-row .name { width: 120px; color: #333; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.bar-row .bar { flex: 1; background: #eef1f6; border-radius: 4px; height: 16px; margin: 0 8px; overflow: hidden; }
|
||||
.bar-row .bar > i { display: block; height: 100%; background: linear-gradient(90deg,#1E9FFF,#5FB878); }
|
||||
.bar-row .val { width: 60px; text-align: right; color: #999; font-size: 12px; }
|
||||
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
||||
@media (max-width: 900px) { .stat-cards { grid-template-columns: repeat(2,1fr); } .grid2 { grid-template-columns: 1fr; } }
|
||||
.heat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px,1fr)); gap: 8px; }
|
||||
.heat-cell { border-radius: 6px; padding: 10px 12px; color: #333; min-height: 54px; box-shadow: 0 1px 3px rgba(0,0,0,.06); line-height: 1.5; }
|
||||
.heat-cell b { font-size: 18px; }
|
||||
.heat-table { border-collapse: collapse; width: 100%; }
|
||||
.heat-table th { font-size: 11px; color: #999; font-weight: normal; padding: 2px 1px; }
|
||||
.heat-table td { height: 16px; border-radius: 2px; }
|
||||
.hh { width: 14px; }
|
||||
.dh { text-align: right; padding-right: 6px; white-space: nowrap; }
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="stat-cards" id="statCards"></div>
|
||||
<div class="grid2">
|
||||
<div class="panel">
|
||||
<h3>Top 分类(按链接数)</h3>
|
||||
<div id="topCategories"></div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h3>热门点击 Top10</h3>
|
||||
<table class="layui-table" lay-size="sm">
|
||||
<thead><tr><th>网站</th><th style="width:90px">点击</th></tr></thead>
|
||||
<tbody id="topClicks"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h3>点击热力图 · 星期×小时 <span id="timeMeta" style="font-weight:normal;color:#999;font-size:12px;"></span></h3>
|
||||
<div id="timeHeat"></div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h3>分类点击热度</h3>
|
||||
<div id="categoryHeat" class="heat-grid"></div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<h3>死链概览 <span id="deadMeta" style="font-weight:normal;color:#999;font-size:12px;"></span></h3>
|
||||
<table class="layui-table" lay-size="sm">
|
||||
<thead><tr><th>网站</th><th style="width:280px">网址</th><th style="width:90px">状态码</th><th style="width:140px">最近检测</th></tr></thead>
|
||||
<tbody id="deadList"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script src="/assets/layui/layui.js"></script>
|
||||
<script src="/assets/ywxapp/ywxapp.js" module="backend"></script>
|
||||
<script>
|
||||
function esc(s){ return String(s==null?'':s).replace(/[&<>"']/g,function(c){return {'&':'&','<':'<','>':'>','"':'"',"'":'''}[c];}); }
|
||||
// 点击密度 -> 颜色(浅米黄=低,深红=高)
|
||||
function heatColor(ratio){
|
||||
ratio = Math.max(0, Math.min(1, ratio));
|
||||
var r = 255, g = Math.round(245 - 200*ratio), b = Math.round(230 - 210*ratio);
|
||||
return 'rgb('+r+','+g+','+b+')';
|
||||
}
|
||||
layui.use(['http'], function () {
|
||||
var http = layui.http;
|
||||
http.get('index').then(function (res) {
|
||||
var d = (res && res.data) || {};
|
||||
renderCards(d.summary || {});
|
||||
renderCategories(d.topCategories || []);
|
||||
renderClicks(d.topClicks || []);
|
||||
renderDead(d.dead || {});
|
||||
if (d.heatmap) {
|
||||
renderTimeHeat(d.heatmap.time || [], d.heatmap.timeRange || '', d.heatmap.timeTotal || 0);
|
||||
renderCategoryHeat(d.heatmap.category || []);
|
||||
}
|
||||
});
|
||||
});
|
||||
function renderCards(s){
|
||||
var cards = [
|
||||
['链接总数', s.links], ['已上架', s.online], ['待审核', s.pending], ['已禁用', s.disabled],
|
||||
['分类数', s.categories], ['热门', s.hot], ['推荐', s.recommend], ['总点击', s.clicks]
|
||||
];
|
||||
document.getElementById('statCards').innerHTML = cards.map(function(c){
|
||||
return '<div class="stat-card"><div class="num">'+(c[1]||0)+'</div><div class="lbl">'+c[0]+'</div></div>';
|
||||
}).join('');
|
||||
}
|
||||
function renderCategories(list){
|
||||
var max = 1; list.forEach(function(c){ if(c.count>max) max=c.count; });
|
||||
document.getElementById('topCategories').innerHTML = list.map(function(c){
|
||||
var pct = Math.round(c.count/max*100);
|
||||
return '<div class="bar-row"><span class="name">'+(c.icon||'')+' '+esc(c.title)+'</span>'+
|
||||
'<span class="bar"><i style="width:'+pct+'%"></i></span><span class="val">'+c.count+'</span></div>';
|
||||
}).join('') || '<p style="color:#999">暂无数据</p>';
|
||||
}
|
||||
function renderClicks(list){
|
||||
document.getElementById('topClicks').innerHTML = list.map(function(it){
|
||||
return '<tr><td><a href="'+esc(it.url)+'" target="_blank">'+esc(it.title)+'</a></td><td>'+(it.click_count||0)+'</td></tr>';
|
||||
}).join('') || '<tr><td colspan="2" style="color:#999">暂无数据</td></tr>';
|
||||
}
|
||||
function renderTimeHeat(heat, meta, total){
|
||||
var days = ['周日','周一','周二','周三','周四','周五','周六'];
|
||||
var max = 1;
|
||||
for (var d = 0; d < 7; d++) { for (var h = 0; h < 24; h++) { var v = (heat[d] && heat[d][h]) || 0; if (v > max) max = v; } }
|
||||
document.getElementById('timeMeta').textContent = '(' + meta + ',共 ' + total + ' 次点击)';
|
||||
if (!heat.length) { document.getElementById('timeHeat').innerHTML = '<p style="color:#999">暂无点击数据(需先有访客点击跳转)</p>'; return; }
|
||||
var html = '<table class="heat-table"><thead><tr><th></th>';
|
||||
for (var h = 0; h < 24; h++) { html += '<th class="hh">' + (h % 24) + '</th>'; }
|
||||
html += '</tr></thead><tbody>';
|
||||
for (var d = 0; d < 7; d++) {
|
||||
html += '<tr><th class="dh">' + days[d] + '</th>';
|
||||
for (var h = 0; h < 24; h++) {
|
||||
var v = (heat[d] && heat[d][h]) || 0;
|
||||
var ratio = v / max;
|
||||
html += '<td style="background:' + heatColor(ratio) + '" title="' + days[d] + ' ' + h + ':00 点击 ' + v + ' 次"></td>';
|
||||
}
|
||||
html += '</tr>';
|
||||
}
|
||||
html += '</tbody></table>';
|
||||
document.getElementById('timeHeat').innerHTML = html;
|
||||
}
|
||||
function renderCategoryHeat(list){
|
||||
if (!list || !list.length) { document.getElementById('categoryHeat').innerHTML = '<p style="color:#999">暂无数据</p>'; return; }
|
||||
var max = 1; list.forEach(function(c){ if (c.clicks > max) max = c.clicks; });
|
||||
document.getElementById('categoryHeat').innerHTML = list.map(function(c){
|
||||
var ratio = c.clicks / max;
|
||||
var txt = ratio > 0.55 ? '#fff' : '#333';
|
||||
return '<div class="heat-cell" style="background:' + heatColor(ratio) + ';color:' + txt + '" title="' + esc(c.title) + ':' + (c.clicks || 0) + ' 次">' +
|
||||
(c.icon ? c.icon + ' ' : '') + esc(c.title) + '<br><b>' + (c.clicks || 0) + '</b></div>';
|
||||
}).join('');
|
||||
}
|
||||
function renderDead(d){
|
||||
document.getElementById('deadMeta').textContent = d.last_check_at ? ('(共 '+ (d.count||0) +' 条,最近检测 '+d.last_check_at+')') : '(尚未执行死链检测)';
|
||||
var list = d.list || [];
|
||||
document.getElementById('deadList').innerHTML = list.map(function(it){
|
||||
var t = it.last_check_at ? new Date(it.last_check_at*1000).toLocaleString() : '-';
|
||||
return '<tr><td>'+esc(it.title)+'</td><td style="word-break:break-all">'+esc(it.url)+'</td><td>'+(it.status_code===0?'无法访问':it.status_code)+'</td><td>'+t+'</td></tr>';
|
||||
}).join('') || '<tr><td colspan="4" style="color:#999">无死链或尚未检测</td></tr>';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,508 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{$title | default='网址管理'}</title>
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link href="/assets/layui/css/layui.css" rel="stylesheet">
|
||||
<link href="/assets/ywxapp/css/ywxapp.css" rel="stylesheet">
|
||||
<script type="text/javascript">
|
||||
var config = { root: "/" };
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="layui-card">
|
||||
<div class="layui-form layui-card-header layuiadmin-card-header-auto">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<input type="text" name="title" placeholder="请输入标题" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<input type="text" name="url" placeholder="请输入URL" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<input type="text" name="keywords" placeholder="请输入关键词" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<input type="text" name="description" placeholder="请输入描述" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<select name="status" lay-filter="statusFilter">
|
||||
<option value="">全部状态</option>
|
||||
<option value="1">启用</option>
|
||||
<option value="0">禁用</option>
|
||||
<option value="2">待审核</option>
|
||||
<option value="dead">死链(检测异常)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn layuiadmin-btn-useradmin" lay-submit lay-filter="tableSearchButton">
|
||||
<i class="layui-icon layui-icon-search layuiadmin-button-btn"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-hide" id="dataTable" lay-filter="dataTable"></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 顶部工具栏 -->
|
||||
<script type="text/html" id="tableBar">
|
||||
<button class="layui-btn layui-btn-sm" lay-event="checkLinks">检测死链</button>
|
||||
<button class="layui-btn layui-btn-sm" lay-event="refreshFavicon">刷新图标</button>
|
||||
<button class="layui-btn layui-btn-sm" lay-event="import">导入书签</button>
|
||||
<button class="layui-btn layui-btn-sm" lay-event="export">导出书签</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" lay-event="batchEnable">批量启用</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-warm" lay-event="batchDisable">批量禁用</button>
|
||||
<button class="layui-btn layui-btn-sm" lay-event="recoverdead">恢复死链</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-primary" lay-event="batchMove">批量移动分类</button>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="dataBar">
|
||||
<div class="layui-btn-group">
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" title="编辑" lay-event="update"><i class="layui-icon layui-icon-edit"></i></a>
|
||||
{{# if(!d.is_super) { }}
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" title="删除" lay-event="delete"><i class="layui-icon layui-icon-delete"></i></a>
|
||||
{{# } }}
|
||||
<a class="layui-btn layui-btn-sm layui-btn-normal" title="通过" lay-event="approve" {{# if(d.status != 2){ }}style="display:none"{{# } }}><i class="layui-icon layui-icon-ok"></i></a>
|
||||
<a class="layui-btn layui-btn-sm layui-btn-danger" title="拒绝" lay-event="reject" {{# if(d.status != 2){ }}style="display:none"{{# } }}><i class="layui-icon layui-icon-close"></i></a>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="dataRecybinTpl">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header"></div>
|
||||
<div class="layui-card-body">
|
||||
<table class="layui-hide" id="dataRecybinTable" lay-filter="dataRecybinTable"></table>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
<script type="text/html" id="dataRecybinBarTpl">
|
||||
<div class="layui-btn-group">
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" title="恢复数据" lay-event="update"><i class="layui-icon layui-icon-edit"></i></a>
|
||||
<a class="layui-btn layui-btn-sm layui-btn-primary" title="删除节点" lay-event="delete"><i class="layui-icon layui-icon-delete"></i></a>
|
||||
</div>
|
||||
</script>
|
||||
<script type="text/html" id="statusTpl">
|
||||
<input type="checkbox" name="status" value="{{d.id}}" lay-skin="switch" lay-text="启用|禁用" lay-filter="statusSwitch" {{ d.status == 1 ? 'checked' : '' }}>
|
||||
</script>
|
||||
<script type="text/html" id="hotspotTpl">
|
||||
<input type="checkbox" name="is_hot" value="{{d.id}}" lay-skin="switch" lay-text="启用|禁用" lay-filter="hotspotSwitch" {{ d.is_hot == 1 ? 'checked' : '' }}>
|
||||
</script>
|
||||
|
||||
<!-- 导入弹层 -->
|
||||
<script type="text/html" id="importTpl">
|
||||
<form class="layui-form" style="padding:20px;">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">目标分类</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="cid" id="importCid">
|
||||
<option value="">默认(首个分类)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">书签文件</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="file" name="file" id="importFile" accept=".html,.htm,.json" class="layui-input">
|
||||
<div style="color:#999;font-size:12px;margin-top:6px;">支持浏览器导出的 Netscape 书签 HTML 或 JSON 数组</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button type="button" class="layui-btn" id="doImport">开始导入</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
||||
|
||||
<!-- 添加/编辑表单 -->
|
||||
<script type="text/html" id="dataFormTpl">
|
||||
<form class="layui-form" style="padding: 20px 20px 0;">
|
||||
<input type="hidden" name="id" value="{{d.id||''}}">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">标题</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="title" required lay-verify="required|account" placeholder="请输入标题" autocomplete="off" class="layui-input" value="{{d.title||''}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">分类</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="cid" lay-verify="required">
|
||||
<option value="">请选择分类</option>
|
||||
{{# d.cates.forEach(function(item) { }}
|
||||
<option value="{{item.id}}" {{ d.cid == item.id ? 'selected' : '' }}>{{item.title}}</option>
|
||||
{{# }); }}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">链接</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="url" lay-verify="url" placeholder="请输入链接" autocomplete="off" class="layui-input" value="{{d.url||''}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">返利PID</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="pid" placeholder="选填,如淘宝/京东联盟推广位PID,点击跳转自动拼接返利参数" autocomplete="off" class="layui-input" value="{{d.pid||''}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">图标</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="icon" placeholder="请输入图标链接" autocomplete="off" class="layui-input" value="{{d.icon||''}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">关键词</label>
|
||||
<div class="layui-input-block">
|
||||
<textarea name="keywords" placeholder="请输入关键词" class="layui-textarea">{{d.keywords||''}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-form-text">
|
||||
<label class="layui-form-label">描述</label>
|
||||
<div class="layui-input-block">
|
||||
<textarea name="description" placeholder="请输入描述信息" class="layui-textarea">{{d.description||''}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="status" value="1" title="启用" {{ d.status==undefined || d.status==1 ? 'checked' : '' }}>
|
||||
<input type="radio" name="status" value="0" title="禁用" {{ d.status==0 ? 'checked' : '' }}>
|
||||
<input type="radio" name="status" value="2" title="待审核" {{ d.status==2 ? 'checked' : '' }}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-hide">
|
||||
<button class="layui-btn" lay-submit="" lay-filter="wxapp-form-submit" id="wxapp-form-submit">提交</button>
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
||||
<script src="/assets/layui/layui.js"></script>
|
||||
<script src="/assets/ywxapp/ywxapp.js" module="backend"></script>
|
||||
<script>
|
||||
layui.use(['http', 'util'], function () {
|
||||
var $ = layui.$, table = layui.table, form = layui.form, laytpl = layui.laytpl, http = layui.http, layer = layui.layer, util = layui.util;
|
||||
var cates = [];
|
||||
http.get('/haonav/backend/links/create').then((res) => {
|
||||
if (res.code === 0) { cates = res.data || []; } else { layer.msg(res.msg || '数据加载失败'); }
|
||||
});
|
||||
|
||||
var dataTable = table.render({
|
||||
elem: '#dataTable',
|
||||
defaultToolbar: [{
|
||||
title: '创建资源', layEvent: 'dataCreate', icon: 'layui-icon-add-1'
|
||||
}, {
|
||||
title: '删除资源', layEvent: 'dataDelete', icon: 'layui-icon-delete'
|
||||
}, {
|
||||
title: '资源回收站', layEvent: 'dataRecybin', icon: 'layui-icon-home'
|
||||
}, 'filter', 'exports', 'print'
|
||||
]
|
||||
, url: "/haonav/backend/links/index"
|
||||
, page: true,
|
||||
limit: 20,
|
||||
limits: [10, 15, 20, 30],
|
||||
parseData: function (res) {
|
||||
return { "code": res.code, "msg": res.message || '', "count": res.count || 0, "data": res.data || [] };
|
||||
},
|
||||
toolbar: '#tableBar'
|
||||
, cols: [[
|
||||
{ type: 'checkbox', fixed: 'left' }
|
||||
, { field: 'id', title: 'ID', width: 72, sort: true, fixed: 'left' }
|
||||
, { field: 'title', title: '名称', width: 130, fixed: 'left' }
|
||||
, { field: 'cate', title: '分类', width: 100, sort: true }
|
||||
, { field: 'url', title: '链接', minWidth: 160, templet: function (d) { return '<a href="' + (d.url || '') + '" target="_blank" style="color:#01AAED;">' + (d.url || '') + '</a>'; } }
|
||||
, { field: 'pid', title: '返利PID', width: 130, align: 'center', templet: function (d) { return d.pid ? '<span style="color:#e67e22;">' + d.pid + '</span>' : '-'; } }
|
||||
, { field: 'icon', title: '图标', width: 90, align: 'center', templet: function (d) { return d.icon ? '<img src="' + d.icon + '" style="width:24px;height:24px;">' : '-'; } }
|
||||
, { field: 'status_code', title: '检测码', width: 90, align: 'center', templet: function (d) { return d.status_code === null || d.status_code === '' ? '-' : (d.status_code >= 200 && d.status_code < 400 ? '<span style="color:#2ecc71">' + d.status_code + '</span>' : '<span style="color:#e74c3c">' + d.status_code + '</span>'); } }
|
||||
, { field: 'fail_count', title: '连败', width: 70, align: 'center', templet: function (d) { return (d.fail_count && d.fail_count > 0) ? '<span style="color:#e67e22;">' + d.fail_count + '</span>' : '0'; } }
|
||||
, { field: 'dead_at', title: '死链时间', width: 115, align: 'center', templet: function (d) { return (d.dead_at && d.dead_at > 0) ? '<span style="color:#e74c3c;">' + util.toDateString(d.dead_at * 1000, 'yyyy-MM-dd') + '</span>' : '-'; } }
|
||||
, { field: 'keywords', title: '关键词', width: 120, align: 'center' }
|
||||
, { field: 'description', title: '描述', minWidth: 160 }
|
||||
, { field: 'status', title: '状态', width: 90, align: 'center', templet: '#statusTpl', unresize: true }
|
||||
, { field: 'is_hot', title: '热点', width: 90, align: 'center', templet: '#hotspotTpl', unresize: true }
|
||||
, { field: 'click_count', title: '点击', width: 90, sort: true }
|
||||
, { field: 'sort', title: '排序', width: 80, sort: true, edit: 'text' }
|
||||
, { field: 'create_at', title: '创建时间', width: 160, align: 'center', sort: true }
|
||||
, { title: '操作', width: 150, align: 'left', toolbar: '#dataBar', fixed: 'right', unresize: true }
|
||||
]],
|
||||
done: function () { }
|
||||
});
|
||||
|
||||
table.on('toolbar(dataTable)', function (obj) {
|
||||
var options = obj.config;
|
||||
switch (obj.event) {
|
||||
case 'dataCreate': active.dataCreate({ pid: 0, type: 1 }); break;
|
||||
case 'dataDelete':
|
||||
var cs = table.checkStatus('dataTable'), cd = cs.data;
|
||||
active.dataDelete(cd.map((i) => i.id));
|
||||
break;
|
||||
case 'dataRecybin': active.dataRecybin(); break;
|
||||
case 'checkLinks':
|
||||
layer.confirm('将逐一检测全部链接可达性(可能耗时较长),确定开始?', function (index) {
|
||||
layer.close(index);
|
||||
var load = layer.load(2);
|
||||
http.post('checkLinks', {}).then(function (res) {
|
||||
layer.close(load);
|
||||
if (res.code == 0) {
|
||||
var msg = '检测完成:共 ' + res.data.total + ' 条,正常 ' + res.data.ok + ' 条,异常 ' + res.data.dead + ' 条';
|
||||
if (res.data.offlined) { msg += ',已自动下线 ' + res.data.offlined + ' 条'; }
|
||||
if (res.data.recovered) { msg += ',已自动恢复 ' + res.data.recovered + ' 条'; }
|
||||
layer.alert(msg);
|
||||
table.reload('dataTable', {}, true);
|
||||
} else { layer.msg(res.msg || '检测失败'); }
|
||||
}).catch(function () { layer.close(load); layer.msg('请求失败'); });
|
||||
});
|
||||
break;
|
||||
case 'refreshFavicon':
|
||||
var cs = table.checkStatus('dataTable'), cd = cs.data;
|
||||
if (cd.length === 0) { return layer.msg('请先勾选要刷新的链接'); }
|
||||
http.post('refreshFavicon', { ids: cd.map((i) => i.id).join(',') }).then(function (res) {
|
||||
if (res.code == 0) { layer.msg(res.msg || '已刷新'); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.msg || '失败'); }
|
||||
});
|
||||
break;
|
||||
case 'import': active.dataImport(); break;
|
||||
case 'export': window.open('export'); break;
|
||||
case 'batchEnable': active.batchOp('enable', '确定批量启用选中链接?'); break;
|
||||
case 'batchDisable': active.batchOp('disable', '确定批量禁用选中链接?'); break;
|
||||
case 'batchMove': active.batchMove(); break;
|
||||
case 'recoverdead': active.batchOp('recoverdead', '确定恢复选中死链(重新启用并清空失败计数)?'); break;
|
||||
};
|
||||
});
|
||||
|
||||
// 排序列行内编辑:失焦即保存
|
||||
table.on('edit(dataTable)', function (obj) {
|
||||
if (obj.field === 'sort') {
|
||||
http.post('batch', { op: 'sort', ids: String(obj.data.id), sort: obj.value }).then(function (res) {
|
||||
if (res.code === 0) { layer.msg(res.message || '排序已保存'); }
|
||||
else { layer.msg(res.message || res.msg || '保存失败'); table.reload('dataTable', {}, true); }
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
table.on('tool(dataTable)', function (elem) {
|
||||
var data = elem.data;
|
||||
switch (elem.event) {
|
||||
case 'update': active.dataEdit(data); break;
|
||||
case 'create': active.dataCreate({ pid: data.id }); break;
|
||||
case 'delete': active.dataDelete([data.id]); break;
|
||||
case 'approve':
|
||||
http.post('approve', { id: data.id }).then(function (res) {
|
||||
if (res.code == 0) { layer.msg('已通过'); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.msg || '操作失败'); }
|
||||
});
|
||||
break;
|
||||
case 'reject':
|
||||
layer.confirm('拒绝后该投稿将被禁用,确定?', function (index) {
|
||||
layer.close(index);
|
||||
http.post('reject', { id: data.id }).then(function (res) {
|
||||
if (res.code == 0) { layer.msg('已拒绝'); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.msg || '操作失败'); }
|
||||
});
|
||||
});
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
});
|
||||
|
||||
form.on('submit(tableSearchButton)', function (data) {
|
||||
table.reload('dataTable', { where: data.field });
|
||||
});
|
||||
|
||||
form.on('switch(statusSwitch)', function (obj) {
|
||||
var id = this.value, status = obj.elem.checked ? 1 : 0;
|
||||
layer.confirm('确定要' + (status ? '启用' : '禁用') + '该数据吗?', function (index) {
|
||||
layui.http.put('update', { id: id, status: status }).then(function (res) {
|
||||
if (res.code == 0) { layer.msg(res.msg, { icon: 1 }); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.msg, { icon: 2 }); obj.elem.checked = !obj.elem.checked; form.render('checkbox'); }
|
||||
});
|
||||
layer.close(index);
|
||||
}, function () {
|
||||
obj.elem.checked = !obj.elem.checked; form.render('checkbox');
|
||||
});
|
||||
});
|
||||
|
||||
form.on('switch(hotspotSwitch)', function (obj) {
|
||||
var id = this.value, is_hot = obj.elem.checked ? 1 : 0;
|
||||
layer.confirm('确定要' + (is_hot ? '推荐' : '不推荐') + '该数据吗?', function (index) {
|
||||
layui.http.put('update', { id: id, is_hot: is_hot }).then(function (res) {
|
||||
if (res.code == 0) { layer.msg(res.msg, { icon: 1 }); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.msg, { icon: 2 }); obj.elem.checked = !obj.elem.checked; form.render('checkbox'); }
|
||||
});
|
||||
layer.close(index);
|
||||
}, function () {
|
||||
obj.elem.checked = !obj.elem.checked; form.render('checkbox');
|
||||
});
|
||||
});
|
||||
|
||||
var dataFromFun = function (data, callback, done) {
|
||||
var formHtml = laytpl($('#dataFormTpl').html()).render(data || {});
|
||||
layer.open({
|
||||
title: data.id ? '编辑数据' : '添加数据',
|
||||
content: formHtml, anim: "slideLeft", offset: "r",
|
||||
btnAlign: "l", area: [window.innerWidth > 1200 ? '30%' : (window.innerWidth > 768 ? '50%' : '99%'), '99%'],
|
||||
shade: 0.1, shadeClose: true,
|
||||
btn: ['确定', '取消'],
|
||||
success: function (layero, index) { callback(layero, index); form.render(); },
|
||||
yes: function (index, layero) {
|
||||
window.layui.form.on('submit(wxapp-form-submit)', function (elem) {
|
||||
done(layero, index, elem); layui.off('submit(wxapp-form)', 'from'); return false;
|
||||
});
|
||||
layero.contents().find("#wxapp-form-submit").trigger('click');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var active = {
|
||||
dataCreate: function (data = {}) {
|
||||
data.cates = cates;
|
||||
dataFromFun(data, function () { }, function (layero, index, elem) {
|
||||
var field = elem.field; field.status = field.status ? 1 : 0;
|
||||
http.post('save', field).then((res) => {
|
||||
if (res.code === 0) { layer.close(index); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.msg || '操作失败'); }
|
||||
});
|
||||
});
|
||||
},
|
||||
dataEdit: function (data = {}) {
|
||||
data.cates = cates;
|
||||
dataFromFun(data, function () { }, function (layero, index, elem) {
|
||||
var field = elem.field; field.status = field.status ? 1 : 0;
|
||||
http.put('update', field).then((res) => {
|
||||
if (res.code === 0) { layer.close(index); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.msg || '操作失败'); }
|
||||
});
|
||||
});
|
||||
},
|
||||
dataImport: function () {
|
||||
var html = laytpl($('#importTpl').html()).render({});
|
||||
layer.open({
|
||||
title: '导入书签', content: html, area: ['480px', '320px'],
|
||||
success: function (layero) {
|
||||
var sel = layero.find('#importCid');
|
||||
cates.forEach(function (c) { sel.append('<option value="' + c.id + '">' + c.title + '</option>'); });
|
||||
form.render('select');
|
||||
layero.find('#doImport').on('click', function () {
|
||||
var fileInput = layero.find('#importFile')[0];
|
||||
if (!fileInput.files.length) { return layer.msg('请选择书签文件'); }
|
||||
var fd = new FormData();
|
||||
fd.append('file', fileInput.files[0]);
|
||||
fd.append('cid', layero.find('#importCid').val());
|
||||
layer.msg('导入中...', { icon: 16, time: 0 });
|
||||
fetch('import', { method: 'POST', body: fd }).then(r => r.json()).then(function (res) {
|
||||
layer.closeAll();
|
||||
if (res.code == 0) { layer.msg(res.msg || '导入成功'); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.msg || '导入失败'); }
|
||||
}).catch(function () { layer.closeAll(); layer.msg('导入失败'); });
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
dataDelete: function (ids, force = 0) {
|
||||
if (ids.length === 0) { return layer.msg('请选择数据'); }
|
||||
layer.prompt({ formType: 1, title: '敏感操作,请验证口令' }, function (value, index) {
|
||||
layer.close(index);
|
||||
layer.confirm('确定删除吗?', function (index) {
|
||||
http.delete('delete', { ids: ids.join(','), force: force }).then((res) => {
|
||||
if (res.code === 0) { layer.close(index); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.msg || '操作失败'); }
|
||||
});
|
||||
layer.msg('已删除'); table.reload('dataTable', {}, true);
|
||||
});
|
||||
});
|
||||
},
|
||||
dataRecybin: function (data = {}) {
|
||||
var formHtml = laytpl($('#dataRecybinTpl').html()).render(data || {});
|
||||
layer.open({
|
||||
title: '资源回收站', content: formHtml, anim: "slideLeft", offset: "r",
|
||||
area: ['60%', '99%'], shade: 0.1, shadeClose: true,
|
||||
success: function (layero, index) {
|
||||
layui.table.render({
|
||||
elem: '#dataRecybinTable', url: 'recyclebin', height: 'full-100',
|
||||
defaultToolbar: [{
|
||||
title: '批量删除数据', layEvent: 'dataDelete', icon: 'layui-icon-delete',
|
||||
onClick: function (obj) {
|
||||
var cs = table.checkStatus('dataRecybinTable'), cd = cs.data;
|
||||
active.dataDelete(cd.map((i) => i.id), 1);
|
||||
}
|
||||
}, 'filter', 'exports', 'print'],
|
||||
cols: [[
|
||||
{ type: 'checkbox', fixed: 'left' },
|
||||
{ field: 'id', title: 'ID', width: 80, sort: true, fixed: 'left' },
|
||||
{ field: 'title', title: 'title', width: 180, fixed: 'left' },
|
||||
{
|
||||
fixed: "right", title: "操作", width: 120, align: "center", templet: function (d) {
|
||||
return `<div class="layui-btn-group">
|
||||
<a class="layui-btn layui-btn-sm" title="恢复数据" lay-event="restore" > <i class="layui-icon layui-icon-edit"></i> </a >
|
||||
<a class="layui-btn layui-btn-sm" title="删除数据" lay-event="delete"><i class="layui-icon layui-icon-delete"></i> </a>
|
||||
</div >`;
|
||||
}
|
||||
}
|
||||
]],
|
||||
page: true,
|
||||
done: function () {
|
||||
layui.table.on('tool(dataRecybinTable)', function (elem) {
|
||||
var data = elem.data;
|
||||
if (elem.event === 'restore') { active.dataRestore([data.id]); }
|
||||
else if (elem.event === 'delete') { active.dataDelete([data.id], 1); }
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
batchOp: function (op, confirmText, extra) {
|
||||
var cd = table.checkStatus('dataTable').data;
|
||||
if (!cd.length) { return layer.msg('请先勾选要操作的链接'); }
|
||||
var ids = cd.map(function (i) { return i.id; }).join(',');
|
||||
layer.confirm(confirmText || '确定执行批量操作?', function (index) {
|
||||
layer.close(index);
|
||||
var params = Object.assign({ op: op, ids: ids }, extra || {});
|
||||
http.post('batch', params).then(function (res) {
|
||||
if (res.code === 0) { layer.msg(res.message || '操作成功'); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.message || res.msg || '操作失败'); }
|
||||
});
|
||||
});
|
||||
},
|
||||
batchMove: function () {
|
||||
var cd = table.checkStatus('dataTable').data;
|
||||
if (!cd.length) { return layer.msg('请先勾选要移动的链接'); }
|
||||
var opts = cates.map(function (c) { return '<option value="' + c.id + '">' + c.title + '</option>'; }).join('');
|
||||
layer.open({
|
||||
title: '批量移动分类(已选 ' + cd.length + ' 条)',
|
||||
area: ['420px', '220px'],
|
||||
content: '<div class="layui-form" style="padding:24px;"><div class="layui-form-item"><label class="layui-form-label">目标分类</label><div class="layui-input-block"><select id="batchMoveCid" lay-filter="batchMoveCid"><option value="">请选择分类</option>' + opts + '</select></div></div></div>',
|
||||
btn: ['确定移动', '取消'],
|
||||
success: function () { form.render('select'); },
|
||||
yes: function (index) {
|
||||
var cid = $('#batchMoveCid').val();
|
||||
if (!cid) { return layer.msg('请选择目标分类'); }
|
||||
var ids = cd.map(function (i) { return i.id; }).join(',');
|
||||
http.post('batch', { op: 'move', ids: ids, cid: cid }).then(function (res) {
|
||||
if (res.code === 0) { layer.close(index); layer.msg(res.message || '已移动'); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.message || res.msg || '移动失败'); }
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
dataRestore: function (ids) {
|
||||
if (ids.length === 0) { return layer.msg('请选择数据'); }
|
||||
http.put('restore', { ids: ids.join(',') }).then((res) => {
|
||||
if (res.code === 0) { layer.msg('恢复成功'); table.reload('dataTable', {}, true); }
|
||||
else { layer.msg(res.msg || '恢复失败'); }
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user