单页vue&简约cms系统
详细介绍
将Vue 3集成到基于Webman Admin的后台系统中,通过模板(layout)布局方式重构了视图,简化页面交互实现,同时基于腾讯md编辑器完成了简约cms并且可作为免费的微信推文编辑器。
因为个人以后端工作为主,这些年头经历的前端技术变革也不少。前后端分离 和 前后端不分离相关的项目都有经历。回到个人项目中,我更喜欢从简的方式。于是我将vue3以单文件方式引入到webman admin 后台中,同时扩展了webman view 的Raw
类(未使用第三方模板引擎)来稍微实现了模板布局渲染VueLayoutRaw
类,因为我觉得vue3来做一些交互比jquery操作dom更快,比如一些复杂的表单操作,如下图。
如果有喜欢的朋友可以尝试下,长期维护cms和模板功能,admin 基础功能保持与webman admin 一致更新。
功能特性
- 集成了vue3 webman admin 后台系统,方便通过数据绑定等方式下,快速开发后台系统。
- 通过修改后台一键生成菜单功能,增加vue模板渲染,实现模板布局,同时保留webman admin原始的操作,不影响原始操作。可以根据习惯来选择使用。
- 实现了模板layout渲染并实现了模板缓存,关闭缓存:修改
plugin/admin/view.php
的view_cache
为false,默认关闭,推荐线上开启opcache和缓存。 - 支持视图文件自定义css、引入外部css、引入外部js后,自动对应html正确的标签位置
- 集成了简洁的cms模块,同时使用腾讯cherry-markdown作为md编辑器,也同时是微信推文免费编辑器。支持:画图、公式、插入图片、视频、word、ppt、音频、实时预览等
- 集成了一个较为丰富的js工具,
tool.js
安装使用
安装
composer install -vvv
教程
视图如何自定义css、引入外部css、引入外部js
- 自定义css: 只需要在视图文件编写
style
标签就可以 - 引入外部css: 只需要在视图文件编写
<link href="">
标签就可以,切记一定必须是带有href
- 引入外部js: 只需要在视图文件编写
<script src="">
标签就可以,切记一定必须是带有src
列表技巧
tool.tableRender
tool.tableRender
是列表核心的调用方法,用于渲染列表table。
函数参数
重要钩子(hock)回调
列表 提供了 add、edit、remove、batchRemove等重要操作的回调函数覆盖操作
表单技巧
view_form_use_vmodel 参数解释
- 可以在
plugin/admin/view.php
配置view_form_use_vmodel
参数,默认为false
,表示不使用v-model,使用默认的form name。 - 当
view_form_use_vmodel
为true
时,相关的表单项会加入v-model=form.{字段名}
,同时原来的dom操作获取值会失效,下面说明一些解决方案(更多处理可以参考组件的相关文档):
通过layui form相关事件来获取值
- radio
// <input type="radio" name="status" lay-filter="status" v-for="m in statusItems" :value="m.key" :title="m.value" v-model="form.status" :checked="form.status == m.key" /> form.on('radio(status)', data => { this.form.status = data.elem.value; })
- checkbox
// <input type="checkbox" name="type" lay-filter="type-checkbox-filter" v-model="form.type" /> // 监听复选框 lay-filter="type-checkbox-filter" form.on('checkbox(type-checkbox-filter)', (data) => { var elem = data.elem; var checked = elem.checked; var value = elem.value === 'on' ? '0' : elem.value; console.log('type:', value, this.form.type_list) if (!checked) { this.form.type_list = this.form.type_list.filter((item) => { return item != value; }); } else { if (!this.form.type_list.includes(value)) { this.form.type_list.push(value); } } });
- switch
form.on('switch(cid)', function (data) { that.form.cid = data.elem.checked ? 1 : 0 });
- select
// <select name="catalog_code" lay-filter="catalog_code" v-model="form.catalog_code">...</select> form.on('select(catalog_code)', (e) => { this.form.catalog_code = e.value; });
...
通过组件对象获取值
- 富文本tinymce
富文本目前生成vue视图模板代码已经做了处理。var edit = tinymce.render({ elem: "#content", images_upload_url: "/app/admin/upload/image", }); edit.on("blur", function(){ layui.$("#content").val(edit.getContent()); that.form.content = edit.getContent(); });
- date
重点:that.form.created_at = timeRange
layui.laydate.render({ elem: "#created_at", range: ["#created_at-date-start", "#created_at-date-end"], type: "datetime", rangeLinked: true, done: function(value, date, endDate){ const timeRange = value.split(" - ") that.form.created_at = timeRange that.refreshTable() } });
- xmSelect
// vue data 定义 pid_xm_selector this.pid_xm_selector = layui.xmSelect.render({ el: "#pid", name: "pid", initValue: initValue, tips: "无", toolbar: {show: true, list: ["CLEAR"]}, data: res.data, value: "0", model: {"icon":"hidden","label":{"type":"text"}}, clickClose: true, radio: true, tree: {show: true,"strict":false,"clickCheck":true,"clickExpand":false}, }); // 提交事件 form.on("submit(save)", data => { const formData = $.extend(data.field, this.form); if (this.pid_xm_selector) { const selectValue = this.pid_xm_selector.getValue().shift(); formData.pid = selectValue.value; } // ... })
- 附件上传
重点:that.form.cover = res.data.url
layui.use(["upload", "layer", "popup", "util"], function() {
let input = layui.$("#$id").prev();
input.prev().html(layui.util.escape(input.val()));
layui.$("#attachment-choose-$id").on("click", function() {
parent.layer.open({
type: 2,
title: "选择附件",
content: "/app/admin/upload/attachment",
area: ["95%", "90%"],
success: function (layero, index) {
parent.layui.$("#layui-layer" + index).data("callback", function (data) {
input.val(data.url).prev().html(layui.util.escape(data.url));
// 更新
that.form.$id = data.url
});
}
});
});
layui.upload.render({
elem: "#cover",
url: "/app/admin/upload/image",
acceptMime: "image/gif,image/jpeg,image/jpg,image/png",
field: "__file__",
done: function (res) {
if (res.code > 0) return layui.layer.msg(res.msg);
that.form.cover = res.data.url
this.item.prev().val(res.data.url).prev().attr("src", res.data.url);
}
});
});
核心工具js:tool.js
tool.groupSeparator(num)
描述:
将数字添加千分位分隔符(例如,1234567.89
转换为 1,234,567.89
)。
参数:
num
:需要格式化的数字。
返回值:
带有千分位分隔符的数字字符串。
示例:
tool.groupSeparator(1234567.89); // "1,234,567.89"
tool.copy(text)
描述:
将指定的文本复制到剪贴板。
参数:
text
:要复制的文本。
返回值:
无
示例:
tool.copy("Hello, World!"); // 将 "Hello, World!" 复制到剪贴板
tool.getTopWin()
描述:
返回多层 iframe 结构中的最顶层窗口对象。
返回值:
最顶层的窗口对象。
示例:
const topWindow = tool.getTopWin();
tool.navigateTo(url)
/ tool.redirectTo(url)
描述:
导航到指定的 URL,更新最顶层窗口的地址。
参数:
url
:要导航到的 URL(可以是字符串或包含url
属性的对象)。
返回值:
无
示例:
tool.navigateTo("https://www.example.com");
tool.openPage(options, call_ok)
描述:
在弹出窗口中(iframe)打开指定页面,支持配置宽度、高度、标题等。
参数:
options
:包含页面配置的对象(title
、width
、height
、url
、refresh
)。call_ok
:页面关闭后的回调函数。
返回值:
无
示例:
tool.openPage({
title: "新页面",
url: "https://www.example.com",
width: 800,
height: 600
}, function () {
console.log("页面已关闭!");
});
tool.closePage()
描述:
关闭当前页面或弹出框。
返回值:
false
示例:
tool.closePage();
tool.createTab(options)
描述:
创建一个新的标签页,或者如果没有标签页功能,则在 iframe 中打开页面。
参数:
options
:包含标签页配置的对象(id
、title
、url
)。
返回值:
无
示例:
tool.createTab({
title: "新标签",
url: "https://www.example.com"
});
tool.openDrawer(options, call_ok)
描述:
打开一个抽屉(侧边栏),从指定的 URL 加载内容。
参数:
options
:包含抽屉配置的对象(title
、area
、offset
、url
、refresh
)。call_ok
:抽屉关闭后的回调函数。
返回值:
无
示例:
tool.openDrawer({
title: "抽屉内容",
url: "https://www.example.com"
});
tool.render()
描述:
渲染表单元素,确保最新的 UI 状态,包括 select
和其他输入框。
返回值:
无
示例:
tool.render();
tool.tableRender(url, cols, actions, elemId = 'data-table', params = {})
描述:
初始化并渲染一个数据表格,支持分页、排序和其他操作(编辑、删除、批量操作)。
参数:
url
:数据源的 URL。cols
:表格的列定义。actions
:包含操作的对象,如add
、edit
、remove
、doRemove
等。elemId
:表格元素的 ID(默认为'data-table'
)。params
:表格渲染的额外配置。
返回值:
无
示例:
tool.tableRender("/api/data", [
{ field: 'name', title: '姓名' },
{ field: 'age', title: '年龄' }
], {
add: { url: "/add" },
edit: { url: "/edit" },
remove: { url: "/remove" }
});
tool.sysPermissionCheck()
描述:
获取并检查当前用户的权限,根据权限显示/隐藏相应的元素。
返回值:
无
示例:
tool.sysPermissionCheck();
tool.request(method, url, data = {}, dataType = 'json')
描述:
封装 AJAX 请求,使用 Promise 处理异步 API 调用。
参数:
method
:HTTP 方法(例如,'GET'
、'POST'
)。url
:API 接口的 URL。data
:请求的 payload(可选)。dataType
:期望的响应数据类型(默认为'json'
)。
返回值:
返回一个 Promise 对象,成功时解析响应数据。
示例:
tool.request('POST', '/api/submit', { name: 'John' })
.then(response => console.log(response))
.catch(error => console.error(error));
tool.getUrlParam(name)
描述:
从当前 URL 中提取指定参数的值。
参数:
name
:URL 参数的名称。
返回值:
返回参数的值,如果未找到,则返回 null
。
示例:
const id = tool.getUrlParam('id'); // 获取 'id' 参数的值
window.alert(msg)
描述:
使用 layui.layer.alert()
显示自定义的提示消息。
window.confirm(message, btnArray, callback, type)
描述:
使用 layui.layer.confirm()
显示自定义的确认对话框。
window.toast(msg)
描述:
使用 layui.layer.msg()
显示自定义的吐司消息。
本文档作为 tool
对象功能的参考,提供了日期格式化、表格渲染、权限管理、AJAX 请求等功能的详细说明,旨在帮助开发者在 Web 应用中实现这些常用功能。
##联系方式
wanzij177@163.com
完成cms系统