chore: 重写初始提交(清空历史,整理后全量提交)
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* setter
|
||||
*/
|
||||
|
||||
// 初始化配置
|
||||
layui.define(['all'], function (exports) {
|
||||
console.log('wxapp config loaded');
|
||||
exports('setter', {
|
||||
paths: {
|
||||
core: '/assets/ywxapp/', // 核心库所在目录
|
||||
// core: layui.cache.base + '/adminui/dist/', // 核心库所在目录
|
||||
views: layui.cache.base + 'views/', // 动态模板所在目录
|
||||
modules: layui.cache.base + 'modules/', // 业务模块所在目录
|
||||
base: layui.cache.base // 记录静态资源所在基础目录
|
||||
},
|
||||
container: 'LAY_app', // 容器 ID
|
||||
entry: 'index', // 默认视图文件名
|
||||
engine: '.html', // 视图文件后缀名
|
||||
pageTabs: true, // 是否开启页面选项卡功能。 iframe 版推荐开启
|
||||
refreshCurrPage: true, // 当跳转页面 url 与当前页 url 相同时,是否自动执行刷新 --- 2.0+
|
||||
|
||||
name: 'AdminUI', // 系统名称
|
||||
tableName: 'adminui', // 本地存储表名
|
||||
MOD_NAME: 'wxapp', // 模块事件名
|
||||
// 记录
|
||||
record: {
|
||||
url: false // 是否开启 iframe 跳转时 url 记忆功能
|
||||
},
|
||||
debug: true, // 是否开启调试模式。如开启,接口异常时会抛出异常 URL 等信息
|
||||
// 自定义请求字段
|
||||
request: {
|
||||
tokenName: false // 自动携带 token 的字段名(如:access_token)。可设置 false 不携带。
|
||||
},
|
||||
// 自定义响应字段
|
||||
response: {
|
||||
statusName: 'code', // 数据状态的字段名称
|
||||
statusCode: {
|
||||
ok: 0, // 数据状态一切正常的状态码
|
||||
logout: 1001 // 登录状态失效的状态码
|
||||
},
|
||||
msgName: 'msg', // 状态信息的字段名称
|
||||
dataName: 'data' // 数据详情的字段名称
|
||||
},
|
||||
|
||||
// 配置业务模块目录中的特殊模块
|
||||
extend: {
|
||||
layim: 'layim/layim' // layim
|
||||
},
|
||||
|
||||
// 主题配置
|
||||
theme: {
|
||||
// 内置主题配色方案
|
||||
color: [{
|
||||
main: '#20222A', // 主题色
|
||||
selected: '#16baaa', // 选中色
|
||||
alias: 'default' // 默认别名
|
||||
}, {
|
||||
main: '#03152A',
|
||||
selected: '#3B91FF',
|
||||
alias: 'dark-blue' // 藏蓝
|
||||
}, {
|
||||
main: '#2E241B',
|
||||
selected: '#A48566',
|
||||
alias: 'coffee' // 咖啡
|
||||
}, {
|
||||
main: '#50314F',
|
||||
selected: '#7A4D7B',
|
||||
alias: 'purple-red' // 紫红
|
||||
}, {
|
||||
main: '#344058',
|
||||
logo: '#1E9FFF',
|
||||
selected: '#1E9FFF',
|
||||
alias: 'ocean' // 海洋
|
||||
}, {
|
||||
main: '#3A3D49',
|
||||
logo: '#2F9688',
|
||||
selected: '#16b777',
|
||||
alias: 'green' // 墨绿
|
||||
}, {
|
||||
main: '#20222A',
|
||||
logo: '#F78400',
|
||||
selected: '#F78400',
|
||||
alias: 'red' // 橙色
|
||||
}, {
|
||||
main: '#28333E',
|
||||
logo: '#AA3130',
|
||||
selected: '#AA3130',
|
||||
alias: 'fashion-red' // 时尚红
|
||||
}, {
|
||||
main: '#24262F',
|
||||
logo: '#3A3D49',
|
||||
selected: '#16baaa',
|
||||
alias: 'classic-black' // 经典黑
|
||||
}, {
|
||||
logo: '#226A62',
|
||||
header: '#2F9688',
|
||||
alias: 'green-header' // 墨绿头
|
||||
}, {
|
||||
main: '#344058',
|
||||
logo: '#0085E8',
|
||||
selected: '#1E9FFF',
|
||||
header: '#1E9FFF',
|
||||
alias: 'ocean-header' // 海洋头
|
||||
}, {
|
||||
header: '#393D49',
|
||||
alias: 'classic-black-header' // 经典黑头
|
||||
}, {
|
||||
main: '#50314F',
|
||||
logo: '#50314F',
|
||||
selected: '#7A4D7B',
|
||||
header: '#50314F',
|
||||
alias: 'purple-red-header' // 紫红头
|
||||
}, {
|
||||
main: '#28333E',
|
||||
logo: '#28333E',
|
||||
selected: '#AA3130',
|
||||
header: '#AA3130',
|
||||
alias: 'fashion-red-header' // 时尚红头
|
||||
}, {
|
||||
main: '#28333E',
|
||||
logo: '#16baaa',
|
||||
selected: '#16baaa',
|
||||
header: '#16baaa',
|
||||
alias: 'green-header' // 墨绿头
|
||||
}, {
|
||||
main: '#393D49',
|
||||
logo: '#393D49',
|
||||
selected: '#16baaa',
|
||||
header: '#23262E',
|
||||
alias: 'Classic-style1' // 经典风格1
|
||||
}, {
|
||||
main: '#001529',
|
||||
logo: '#001529',
|
||||
selected: '#1890FF',
|
||||
header: '#1890FF',
|
||||
alias: 'Classic-style2' // 经典风格2
|
||||
}, {
|
||||
main: '#25282A',
|
||||
logo: '#25282A',
|
||||
selected: '#35BDB2',
|
||||
header: '#35BDB2',
|
||||
alias: 'Classic-style3' // 经典风格3
|
||||
}],
|
||||
|
||||
// 初始的颜色索引,对应上面的配色方案数组索引
|
||||
// 如果本地已经有主题色记录,则以本地记录为优先,除非请求本地数据(localStorage)
|
||||
initColorIndex: 0
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,781 @@
|
||||
/**
|
||||
* admin.templte.css
|
||||
*/
|
||||
|
||||
/********************************
|
||||
|
||||
模板开始
|
||||
|
||||
*******************************/
|
||||
|
||||
#LAY_app,
|
||||
body,
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
.layui-layout-body {
|
||||
overflow: auto;
|
||||
}
|
||||
#LAY-user-login,
|
||||
.layadmin-user-display-show {
|
||||
display: block !important;
|
||||
}
|
||||
.layadmin-user-login {
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
padding: 110px 0;
|
||||
min-height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.layadmin-user-login-main {
|
||||
width: 375px;
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.layadmin-user-login-box {
|
||||
padding: 20px;
|
||||
}
|
||||
.layadmin-user-login-header {
|
||||
text-align: center;
|
||||
}
|
||||
.layadmin-user-login-header h2 {
|
||||
margin-bottom: 10px;
|
||||
font-weight: 300;
|
||||
font-size: 30px;
|
||||
color: #000;
|
||||
}
|
||||
.layadmin-user-login-header p {
|
||||
font-weight: 300;
|
||||
color: #999;
|
||||
}
|
||||
.layadmin-user-login-body .layui-form-item {
|
||||
position: relative;
|
||||
}
|
||||
.layadmin-user-login-icon {
|
||||
position: absolute;
|
||||
left: 1px;
|
||||
top: 1px;
|
||||
width: 38px;
|
||||
line-height: 36px;
|
||||
text-align: center;
|
||||
color: #d2d2d2;
|
||||
}
|
||||
.layadmin-user-login-body .layui-form-item .layui-input {
|
||||
padding-left: 38px;
|
||||
}
|
||||
.layadmin-user-login-codeimg {
|
||||
max-height: 38px;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.layadmin-user-login-other {
|
||||
position: relative;
|
||||
font-size: 0;
|
||||
line-height: 38px;
|
||||
padding-top: 20px;
|
||||
}
|
||||
.layadmin-user-login-other > * {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.layadmin-user-login-other .layui-icon {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
font-size: 26px;
|
||||
}
|
||||
.layadmin-user-login-other a:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.layadmin-user-jump-change {
|
||||
float: right;
|
||||
}
|
||||
.layadmin-user-login-footer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
line-height: 30px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.layadmin-user-login-footer span {
|
||||
padding: 0 5px;
|
||||
}
|
||||
.layadmin-user-login-footer a {
|
||||
padding: 0 5px;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.layadmin-user-login-footer a:hover {
|
||||
color: #000;
|
||||
}
|
||||
.layadmin-user-login-main[bgimg] {
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.ladmin-user-login-theme {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
.ladmin-user-login-theme ul {
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
background-color: #fff;
|
||||
}
|
||||
.ladmin-user-login-theme ul li {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 64px;
|
||||
height: 43px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
-webkit-transition: all 0.3s;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
.ladmin-user-login-theme ul li:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
@media screen and (max-width: 768px) {
|
||||
.layadmin-user-login {
|
||||
padding-top: 60px;
|
||||
}
|
||||
.layadmin-user-login-main {
|
||||
width: 300px;
|
||||
}
|
||||
.layadmin-user-login-box {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
/* 产品版本选择面板 */
|
||||
.layadmin-panel-selection {
|
||||
width: 768px;
|
||||
margin: 30px auto;
|
||||
}
|
||||
.layadmin-panel-selection .layui-panel-window {
|
||||
padding: 30px 50px;
|
||||
border-top-color: #ddd;
|
||||
text-align: center;
|
||||
}
|
||||
.layadmin-panel-selection .layui-panel-window h2 {
|
||||
padding-bottom: 15px;
|
||||
font-size: 18px;
|
||||
}
|
||||
.layadmin-panel-selection .layui-panel-window ul {
|
||||
margin-top: 15px;
|
||||
text-align: left;
|
||||
}
|
||||
.layadmin-panel-selection .layui-btn-container {
|
||||
margin-top: 30px;
|
||||
}
|
||||
.layadmin-panel-selection .layui-btn-container .layui-btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 通讯录面板 */
|
||||
.layadmin-maillist-fluid .layadmin-contact-box {
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #e7eaec;
|
||||
overflow: hidden;
|
||||
}
|
||||
.layadmin-maillist-fluid .layadmin-text-center {
|
||||
text-align: center;
|
||||
}
|
||||
.layadmin-maillist-fluid .layadmin-text-center img {
|
||||
max-width: 80%;
|
||||
border-radius: 50%;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.layadmin-maillist-fluid .layadmin-font-blod {
|
||||
font-weight: 600;
|
||||
}
|
||||
.layadmin-maillist-fluid .layadmin-maillist-img {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.layadmin-maillist-fluid .layadmin-title {
|
||||
margin: 5px 0 10px 0;
|
||||
}
|
||||
.layadmin-maillist-fluid .layadmin-textimg {
|
||||
line-height: 25px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.layadmin-maillist-fluid .layadmin-address {
|
||||
line-height: 1.5;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.layadmin-maillist-fluid .layadmin-padding-left20 {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
/* 个人主页 */
|
||||
.layadmin-homepage-shadow {
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.17);
|
||||
border-radius: 0;
|
||||
border: 1px solid #e7ecf3;
|
||||
}
|
||||
.layadmin-homepage-panel {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.layadmin-homepage-panel .text-center {
|
||||
text-align: center;
|
||||
}
|
||||
.layadmin-homepage-information {
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #e9e9e9;
|
||||
}
|
||||
.layadmin-homepage-pad-ver {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
.layadmin-homepage-pad-img {
|
||||
box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 50%;
|
||||
}
|
||||
.layadmin-homepage-font {
|
||||
font-weight: 600;
|
||||
color: #2b425b;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.layadmin-homepage-min-font {
|
||||
margin: 5px 0 10px 0;
|
||||
font-size: 0.9em;
|
||||
color: #afb9c3;
|
||||
}
|
||||
.layadmin-homepage-pad-ver a {
|
||||
line-height: 1.1em;
|
||||
font-size: 1.3em;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
.layadmin-homepage-pad-ver a:hover {
|
||||
background: #16b777;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.layadmin-homepage-about {
|
||||
padding: 15px;
|
||||
font-weight: 600;
|
||||
color: #2b425b;
|
||||
}
|
||||
.layadmin-homepage-list-group {
|
||||
font-size: 0.9em;
|
||||
line-height: 1.25;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.layadmin-homepage-list-group li {
|
||||
padding: 5px 15px;
|
||||
border: 1px solid #ddd;
|
||||
margin-bottom: -1px;
|
||||
border-width: 1px 0;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
.layadmin-homepage-list-group li i {
|
||||
font-size: 1.333em;
|
||||
line-height: 1.095em;
|
||||
vertical-align: middle;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.layadmin-homepage-list-group li a.color {
|
||||
color: #337ab7;
|
||||
}
|
||||
.layadmin-homepage-pad-hor {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
border-bottom: 15px;
|
||||
color: #758697;
|
||||
text-indent: 20px;
|
||||
}
|
||||
.layadmin-homepage-list-inline {
|
||||
margin-left: 15px;
|
||||
margin-right: 15px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.layadmin-homepage-list-inline a {
|
||||
display: inline-block;
|
||||
}
|
||||
.layadmin-homepage-list-inline .layui-btn {
|
||||
font-size: 0.9em;
|
||||
line-height: 1.42857;
|
||||
vertical-align: middle;
|
||||
height: auto;
|
||||
padding: 4px 7px;
|
||||
margin-left: 0px;
|
||||
margin: 0 10px 10px 0;
|
||||
}
|
||||
|
||||
/* 左侧个人信息区域 */
|
||||
.layadmin-homepage-text-center {
|
||||
text-align: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.layadmin-homepage-padding15 {
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.layadmin-homepage-padding8 {
|
||||
padding: 0 8px;
|
||||
}
|
||||
.layadmin-homepage-paddingmb {
|
||||
margin-bottom: 15px;
|
||||
margin: 0 -7.5px;
|
||||
}
|
||||
.layadmin-homepage-content {
|
||||
margin-left: 15px;
|
||||
padding: 0;
|
||||
}
|
||||
.layadmin-homepage-content .new-section-xs {
|
||||
margin: 12px 0;
|
||||
color: inherit;
|
||||
border: 0 !important;
|
||||
height: 0px;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
.layadmin-homepage-content .h4 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #2b425b;
|
||||
}
|
||||
.layadmin-homepage-content small {
|
||||
color: #afb9c3;
|
||||
}
|
||||
.layadmin-homepage-text-center .layui-icon {
|
||||
color: #ffb800;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.layadmin-homepage-padding15 .layui-col-sm5 a {
|
||||
font-size: 11px;
|
||||
height: auto;
|
||||
line-height: 1.5;
|
||||
padding: 5px 10px;
|
||||
float: right;
|
||||
}
|
||||
.layadmin-homepage-padding15 .layui-btn-normal {
|
||||
margin: 0 15px;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .layui-col-sm10,
|
||||
.layadmin-homepage-list-imgtxt .layui-col-sm2 {
|
||||
background: none;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .panel-body {
|
||||
padding: 15px 20px 25px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .panel-body .media-left {
|
||||
display: block;
|
||||
float: left;
|
||||
vertical-align: top;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .panel-body .media-left img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body {
|
||||
width: auto;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .pad-btm {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .pad-btm p:first-child {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .min-font {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .min-font .layui-breadcrumb a {
|
||||
font-size: 11px;
|
||||
}
|
||||
.layui-breadcrumb span[lay-separator] {
|
||||
margin: 0 5px;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .pad-btm .fontColor a {
|
||||
font-weight: 600;
|
||||
color: #337ab7;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .pad-btm .fontColor span {
|
||||
color: #758697;
|
||||
font-weight: 600;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .h-img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .img-xs {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .media {
|
||||
margin-top: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .media .media-right {
|
||||
float: right;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .media .media-right .list-inline {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .media .list-inline li {
|
||||
padding: 0 5px;
|
||||
display: inline-block;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt
|
||||
.media-body
|
||||
.media
|
||||
.media-right
|
||||
.list-inline
|
||||
li
|
||||
span {
|
||||
font-weight: 600;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .media .media-left {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
width: auto;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .media .media-left .font-blod {
|
||||
font-weight: 700;
|
||||
color: #758697;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .media-list {
|
||||
padding-top: 15px;
|
||||
margin-top: 15px;
|
||||
border-top: 1px solid #e9e9e9;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .media-list .media-item {
|
||||
padding-bottom: 15px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .media-list .media-item-left {
|
||||
display: inline-block;
|
||||
padding-right: 10px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .media-list .media-item-left img {
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .media-list .media-text {
|
||||
overflow: hidden;
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .media-list .media-text a {
|
||||
margin-right: 10px;
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt
|
||||
.media-body
|
||||
.media-list
|
||||
.media-text
|
||||
div:nth-child(2) {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .media-list .layui-btn {
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
font-size: 12px;
|
||||
padding: 0 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.layadmin-homepage-list-imgtxt .media-body .media-list .layui-btn {
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
font-size: 12px;
|
||||
padding: 0 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* 个人内容区域 */
|
||||
|
||||
.homepage-top .layui-card-body img {
|
||||
width: 100%;
|
||||
}
|
||||
.homepage-top .layui-card-body .margin-top10 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.homepage-top .layui-card-body .layui-btn {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
.homepage-top {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.layadmin-privateletterlist-item {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
.homepage-bottom .layui-card-body {
|
||||
padding: 0;
|
||||
}
|
||||
.homepage-bottom .layui-card-body img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.homepage-bottom .layui-card-body .meida-left {
|
||||
display: table-cell;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.homepage-bottom .layui-card-body .meida-right {
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
}
|
||||
.homepage-bottom .layui-card-body a:hover {
|
||||
background: #f2f2f2;
|
||||
}
|
||||
.homepage-bottom .layui-card-body {
|
||||
background: #fff;
|
||||
}
|
||||
.layui-card-header .panel-title .layui-icon {
|
||||
position: relative;
|
||||
left: 0;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
/* 右侧信息栏 */
|
||||
.layadmin-cmdlist-fluid {
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
.cmdlist-container {
|
||||
background: #fff;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
.cmdlist-container:hover {
|
||||
border: 1px solid #e8e8e8;
|
||||
}
|
||||
.cmdlist-container img {
|
||||
width: 100%;
|
||||
}
|
||||
.cmdlist-text {
|
||||
padding: 20px;
|
||||
}
|
||||
.cmdlist-text .info {
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
color: #5f5f5f;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.cmdlist-text .price {
|
||||
font-size: 14px;
|
||||
}
|
||||
.cmdlist-text .price b {
|
||||
margin-right: 20px;
|
||||
}
|
||||
.cmdlist-text .price p {
|
||||
display: inline-block;
|
||||
}
|
||||
.cmdlist-text .flow {
|
||||
text-align: right;
|
||||
float: right;
|
||||
}
|
||||
#demo0 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 商品列表 */
|
||||
.layadmin-message-fluid .layui-col-md12 {
|
||||
background: #fff;
|
||||
height: auto;
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
.layadmin-message-fluid .layui-input-block {
|
||||
margin-left: 0;
|
||||
}
|
||||
.layadmin-message-fluid .layui-form {
|
||||
padding: 45px 40px 0 40px;
|
||||
}
|
||||
.layadmin-message-fluid .layui-form-label {
|
||||
text-align: left;
|
||||
font-size: 18px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.layadmin-message-fluid .layui-textarea {
|
||||
min-height: 100px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.layadmin-message-fluid .layui-input-right {
|
||||
float: right;
|
||||
}
|
||||
.layadmin-messag-icon {
|
||||
overflow: hidden;
|
||||
float: left;
|
||||
}
|
||||
.layadmin-messag-icon .layui-icon {
|
||||
font-size: 24px;
|
||||
line-height: 30px;
|
||||
margin-right: 15px;
|
||||
color: #c4cbcf;
|
||||
}
|
||||
.layadmin-messag-icon {
|
||||
margin-top: 4px;
|
||||
}
|
||||
.message-content {
|
||||
padding: 0 40px;
|
||||
}
|
||||
.message-content .media-body {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
.message-content .media-body .pad-btm {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.message-content .media-left {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.message-content .media-left img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.message-text {
|
||||
padding-top: 10px;
|
||||
}
|
||||
.message-content-btn {
|
||||
text-align: center;
|
||||
}
|
||||
.message-content .layui-btn {
|
||||
height: auto;
|
||||
line-height: 26px;
|
||||
padding: 5px 30px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* 搜索结果页面 */
|
||||
.layadmin-serach-main .layui-card-header {
|
||||
height: auto;
|
||||
line-height: 24px;
|
||||
padding: 15px;
|
||||
}
|
||||
.layadmin-serach-list {
|
||||
margin-bottom: 10px;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
}
|
||||
.layadmin-serach-list h3 {
|
||||
padding: 10px 0;
|
||||
}
|
||||
.layadmin-serach-list h3 .layui-badge {
|
||||
top: -2px;
|
||||
}
|
||||
.layadmin-serach-list p {
|
||||
color: #5f5f5f;
|
||||
}
|
||||
.layadmin-serach-list li {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 20px;
|
||||
clear: both;
|
||||
}
|
||||
.layui-serachlist-cover {
|
||||
float: left;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.layui-serachlist-cover img {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
/* 用户列表开始 */
|
||||
.layadmin-caller {
|
||||
background: #fff;
|
||||
padding: 30px;
|
||||
}
|
||||
.layadmin-caller em {
|
||||
font-style: normal;
|
||||
}
|
||||
.layadmin-caller .caller-fl {
|
||||
float: left;
|
||||
}
|
||||
.layadmin-caller .caller-fr {
|
||||
float: right;
|
||||
}
|
||||
.layadmin-caller .caller-seach {
|
||||
position: relative;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
.layadmin-caller .caller-seach .caller-icon {
|
||||
font-size: 18px;
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
}
|
||||
.layadmin-caller .caller-seach-icon {
|
||||
left: 6px;
|
||||
}
|
||||
.layadmin-caller .caller-dump-icon {
|
||||
right: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.layadmin-caller .caller-pl32 {
|
||||
padding: 0 32px;
|
||||
}
|
||||
.layadmin-caller .caller-tab {
|
||||
margin: 0;
|
||||
}
|
||||
.layadmin-caller .caller-contar {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.layadmin-caller .caller-contar .caller-item {
|
||||
padding: 25px 0;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
.layadmin-caller .caller-contar .caller-item .caller-main {
|
||||
margin-left: 20px;
|
||||
}
|
||||
.layadmin-caller .caller-contar .caller-item .caller-main p {
|
||||
line-height: 100%;
|
||||
padding: 8px 0;
|
||||
}
|
||||
.layadmin-caller .caller-contar .caller-item .caller-main p:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
.layadmin-caller .caller-contar .caller-item .caller-main em {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.layadmin-caller .caller-contar .caller-item .caller-main .caller-adds i {
|
||||
padding-right: 5px;
|
||||
margin: 0;
|
||||
}
|
||||
.layadmin-caller .caller-contar .caller-item .caller-main .caller-adds {
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
.layadmin-caller .caller-contar .caller-iconset i {
|
||||
margin: 0 5px;
|
||||
}
|
||||
.layadmin-caller .caller-contar .caller-iconset i:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
.layadmin-caller .caller-contar button {
|
||||
margin-top: 22px;
|
||||
}
|
||||
.layadmin-caller .caller-contar .caller-img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
/****** 模板结束 ******/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,90 @@
|
||||
layui.define(function (exports) {
|
||||
"use strict";
|
||||
|
||||
class WeChatJsonCrypto {
|
||||
constructor(token, encodingAesKey, appId) {
|
||||
if (!token || !appId || encodingAesKey.length !== 43 || !/^[A-Za-z0-9+\/]{43}$/.test(encodingAesKey)) {
|
||||
throw new Error("配置错误:Token/AppID不能为空,EncodingAESKey需为43位标准Base64字符");
|
||||
}
|
||||
this.token = token;
|
||||
this.appId = appId;
|
||||
this.blockSize = 32;
|
||||
this.encodingAesKey = CryptoJS.enc.Base64.parse(encodingAesKey + '=');
|
||||
if (!this.encodingAesKey.words?.length) throw new Error("密钥解析失败");
|
||||
}
|
||||
|
||||
// ✅ 核心修复1:加密时直接传入明文WordArray(非{ ciphertext: ... })
|
||||
async encrypt(data) {
|
||||
const jsonBytes = new TextEncoder().encode(JSON.stringify(data, null, 0));
|
||||
const appIdBytes = new TextEncoder().encode(this.appId);
|
||||
const random = crypto.getRandomValues(new Uint8Array(16));
|
||||
const lenBytes = new Uint8Array(new DataView(new ArrayBuffer(4)).buffer);
|
||||
new DataView(lenBytes.buffer).setUint32(0, jsonBytes.length, false);
|
||||
|
||||
// 拼接明文: random(16) + len(4) + json + appId
|
||||
const plain = new Uint8Array(20 + jsonBytes.length + appIdBytes.length);
|
||||
plain.set(random); plain.set(lenBytes, 16);
|
||||
plain.set(jsonBytes, 20); plain.set(appIdBytes, 20 + jsonBytes.length);
|
||||
|
||||
// PKCS#7填充
|
||||
const pad = this.blockSize - (plain.length % this.blockSize);
|
||||
const padded = new Uint8Array(plain.length + pad).fill(pad, plain.length);
|
||||
padded.set(plain);
|
||||
|
||||
const iv = CryptoJS.lib.WordArray.create(this.encodingAesKey.words.slice(0, 4));
|
||||
const encrypted = CryptoJS.AES.encrypt(
|
||||
CryptoJS.lib.WordArray.create(padded), // ← 修复点:直接传明文
|
||||
this.encodingAesKey,
|
||||
{ iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.NoPadding }
|
||||
);
|
||||
return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
|
||||
}
|
||||
|
||||
async decrypt(encryptedBase64) {
|
||||
try {
|
||||
const cipherWA = CryptoJS.enc.Base64.parse(encryptedBase64);
|
||||
if (cipherWA.sigBytes < 32) return null;
|
||||
|
||||
const iv = CryptoJS.lib.WordArray.create(this.encodingAesKey.words.slice(0, 4));
|
||||
const decryptedWA = CryptoJS.AES.decrypt(
|
||||
{ ciphertext: cipherWA },
|
||||
this.encodingAesKey,
|
||||
{ iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.NoPadding }
|
||||
);
|
||||
|
||||
// 转Uint8Array
|
||||
const decrypted = new Uint8Array(decryptedWA.sigBytes);
|
||||
for (let i = 0; i < decryptedWA.sigBytes; i++) {
|
||||
decrypted[i] = (decryptedWA.words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
|
||||
}
|
||||
|
||||
// ✅ 核心修复2:严格PKCS#7填充验证与移除
|
||||
const padLen = decrypted[decrypted.length - 1];
|
||||
if (padLen < 1 || padLen > this.blockSize || decrypted.length < padLen) return null;
|
||||
for (let i = decrypted.length - padLen; i < decrypted.length; i++) {
|
||||
if (decrypted[i] !== padLen) return null;
|
||||
}
|
||||
const plain = decrypted.slice(0, -padLen); // 移除填充
|
||||
|
||||
if (plain.length < 20) return null;
|
||||
const jsonLen = new DataView(plain.slice(16, 20).buffer).getUint32(0, false);
|
||||
if (jsonLen <= 0 || 20 + jsonLen > plain.length) return null;
|
||||
|
||||
const jsonStr = new TextDecoder().decode(plain.slice(20, 20 + jsonLen));
|
||||
// ✅ 核心修复3:AppID直接解码(无字符过滤)
|
||||
const fromAppId = new TextDecoder().decode(plain.slice(20 + jsonLen));
|
||||
if (fromAppId !== this.appId) return null;
|
||||
|
||||
return JSON.parse(jsonStr);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
generateSignature(timestamp, nonce, encryptMsg) {
|
||||
return CryptoJS.SHA1([this.token, timestamp, nonce, encryptMsg].sort().join('')).toString(CryptoJS.enc.Hex);
|
||||
}
|
||||
}
|
||||
// 导出模块
|
||||
exports('JsonCrypto', WeChatJsonCrypto);
|
||||
});
|
||||
@@ -0,0 +1,751 @@
|
||||
/**
|
||||
* =============================================
|
||||
* 权限管理模块 - Layui版本
|
||||
* Auth + RBAC 权限控制完整实现
|
||||
* =============================================
|
||||
*
|
||||
* 使用说明:
|
||||
* 1. 放置在layui的modules目录下
|
||||
* 2. 使用:layui.use('permission', function(mod){...})
|
||||
* 3. 或者直接:var permission = layui.permission;
|
||||
*
|
||||
* @version 2.0.0 - Layui Module
|
||||
* @author Permission Module
|
||||
* @date 2024-01-01
|
||||
*/
|
||||
|
||||
layui.define(function (exports) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* =============================================
|
||||
* 权限配置管理器
|
||||
* =============================================
|
||||
*/
|
||||
const PermissionConfig = (function () {
|
||||
// 私有变量
|
||||
const _defaultToolbar = {
|
||||
create: { title: '创建资源', layEvent: 'dataCreate', icon: 'layui-icon-add-1', permission: 'create', color: 'layui-btn-normal', size: '', show: true },
|
||||
recycle: { title: '回收站', layEvent: 'dataRecycle', icon: 'layui-icon-refresh', permission: 'recycle', color: 'layui-btn-warm', size: '', show: true },
|
||||
filter: { title: '筛选', layEvent: 'filter', icon: 'layui-icon-search', permission: 'filter', color: 'layui-btn-primary', size: '', show: true },
|
||||
export: { title: '导出', layEvent: 'export', icon: 'layui-icon-download-circle', permission: 'export', color: 'layui-btn-primary', size: '', show: true },
|
||||
print: { title: '打印', layEvent: 'print', icon: 'layui-icon-print', permission: 'print', color: 'layui-btn-primary', size: '', show: true },
|
||||
refresh: { title: '刷新', layEvent: 'refresh', icon: 'layui-icon-refresh', permission: 'refresh', color: 'layui-btn-primary', size: '', show: true },
|
||||
batchDelete: { title: '批量删除', layEvent: 'batchDelete', icon: 'layui-icon-delete', permission: 'batch_delete', color: 'layui-btn-danger', size: '', show: true },
|
||||
import: { title: '导入', layEvent: 'import', icon: 'layui-icon-upload-circle', permission: 'import', color: 'layui-btn-primary', size: '', show: true }
|
||||
};
|
||||
|
||||
const _rowToolbar = {
|
||||
view: { title: '查看', layEvent: 'dataView', icon: 'layui-icon-eye', permission: 'view', color: 'layui-btn-primary layui-btn-xs', size: 'layui-btn-xs', show: true },
|
||||
edit: { title: '编辑', layEvent: 'dataEdit', icon: 'layui-icon-edit', permission: 'edit', color: 'layui-btn-normal layui-btn-xs', size: 'layui-btn-xs', show: true },
|
||||
delete: { title: '删除', layEvent: 'dataDelete', icon: 'layui-icon-delete', permission: 'delete', color: 'layui-btn-danger layui-btn-xs', size: 'layui-btn-xs', show: true },
|
||||
copy: { title: '复制', layEvent: 'dataCopy', icon: 'layui-icon-file-b', permission: 'copy', color: 'layui-btn-warm layui-btn-xs', size: 'layui-btn-xs', show: true },
|
||||
enable: { title: '启用', layEvent: 'dataEnable', icon: 'layui-icon-play', permission: 'enable', color: 'layui-btn-normal layui-btn-xs', size: 'layui-btn-xs', show: true },
|
||||
disable: { title: '禁用', layEvent: 'dataDisable', icon: 'layui-icon-pause', permission: 'disable', color: 'layui-btn-warm layui-btn-xs', size: 'layui-btn-xs', show: true },
|
||||
audit: { title: '审核', layEvent: 'dataAudit', icon: 'layui-icon-ok-circle', permission: 'audit', color: 'layui-btn-normal layui-btn-xs', size: 'layui-btn-xs', show: true },
|
||||
unaudit: { title: '取消审核', layEvent: 'dataUnaudit', icon: 'layui-icon-close', permission: 'unaudit', color: 'layui-btn-warm layui-btn-xs', size: 'layui-btn-xs', show: true }
|
||||
};
|
||||
|
||||
const _customButtons = {
|
||||
toolbar: {},
|
||||
row: {}
|
||||
};
|
||||
|
||||
// 工具函数
|
||||
const _isArray = function (obj) {
|
||||
return Object.prototype.toString.call(obj) === '[object Array]';
|
||||
};
|
||||
|
||||
// 公共方法
|
||||
return {
|
||||
/**
|
||||
* 获取默认工具条配置
|
||||
*/
|
||||
getDefaultToolbar: function () {
|
||||
return Object.assign({}, _defaultToolbar);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取行工具条配置
|
||||
*/
|
||||
getRowToolbar: function () {
|
||||
return Object.assign({}, _rowToolbar);
|
||||
},
|
||||
|
||||
/**
|
||||
* 注册自定义按钮
|
||||
* @param {String} type - 按钮类型 (toolbar/row)
|
||||
* @param {String} key - 按钮唯一标识
|
||||
* @param {Object} config - 按钮配置
|
||||
*/
|
||||
registerButton: function (type, key, config) {
|
||||
if (!['toolbar', 'row'].includes(type)) {
|
||||
console.error('按钮类型必须是 toolbar 或 row');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_customButtons[type]) {
|
||||
_customButtons[type] = {};
|
||||
}
|
||||
|
||||
_customButtons[type][key] = Object.assign({
|
||||
title: key,
|
||||
layEvent: key,
|
||||
icon: 'layui-icon-more',
|
||||
permission: key,
|
||||
color: 'layui-btn-primary',
|
||||
size: '',
|
||||
show: true
|
||||
}, config);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取工具条配置
|
||||
* @param {String} type - toolbar/row
|
||||
* @param {Array} keys - 需要的按钮key列表
|
||||
* @param {Object} customConfig - 自定义配置(覆盖默认配置)
|
||||
* @returns {Array} 配置数组
|
||||
*/
|
||||
getToolbarConfig: function (type, keys = [], customConfig = {}) {
|
||||
const baseConfig = type === 'toolbar' ? _defaultToolbar : _rowToolbar;
|
||||
const customBtns = _customButtons[type] || {};
|
||||
|
||||
// 合并基础配置和自定义配置
|
||||
const allConfig = Object.assign({}, baseConfig, customBtns);
|
||||
|
||||
// 应用自定义配置覆盖
|
||||
Object.keys(customConfig).forEach(key => {
|
||||
if (allConfig[key]) {
|
||||
allConfig[key] = Object.assign(allConfig[key], customConfig[key]);
|
||||
}
|
||||
});
|
||||
|
||||
// 如果指定了keys,只返回指定的按钮
|
||||
if (keys.length > 0) {
|
||||
return keys.map(key => allConfig[key]).filter(Boolean);
|
||||
}
|
||||
|
||||
// 返回所有配置
|
||||
return Object.values(allConfig).filter(btn => btn.show !== false);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取单个按钮配置
|
||||
* @param {String} type - toolbar/row
|
||||
* @param {String} key - 按钮key
|
||||
* @returns {Object} 按钮配置
|
||||
*/
|
||||
getButtonConfig: function (type, key) {
|
||||
const config = this.getToolbarConfig(type);
|
||||
return config.find(btn => btn.layEvent === key || (btn.permission && btn.permission.includes(key)));
|
||||
},
|
||||
|
||||
/**
|
||||
* 批量注册按钮
|
||||
* @param {String} type - 按钮类型
|
||||
* @param {Object} buttons - 按钮配置对象
|
||||
*/
|
||||
registerButtons: function (type, buttons) {
|
||||
Object.keys(buttons).forEach(key => {
|
||||
this.registerButton(type, key, buttons[key]);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 清除自定义按钮
|
||||
* @param {String} type - 按钮类型
|
||||
* @param {String} key - 按钮key,不传则清除所有
|
||||
*/
|
||||
clearCustomButtons: function (type, key) {
|
||||
if (key) {
|
||||
delete _customButtons[type]?.[key];
|
||||
} else {
|
||||
_customButtons[type] = {};
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
/**
|
||||
* =============================================
|
||||
* 权限验证工具类
|
||||
* =============================================
|
||||
*/
|
||||
const Auth = (function () {
|
||||
// 私有变量
|
||||
let _permissions = [];
|
||||
let _permissionCache = {};
|
||||
let _user = null;
|
||||
let _controller = null; // 当前页面对应的控制器(用于裸动作 token 解析)
|
||||
|
||||
// 工具函数
|
||||
const _isArray = function (obj) {
|
||||
return Object.prototype.toString.call(obj) === '[object Array]';
|
||||
};
|
||||
|
||||
const _isString = function (obj) {
|
||||
return typeof obj === 'string';
|
||||
};
|
||||
|
||||
// 前端短词 → 后端动作 同义词(按钮配置常用 create,权限 key 用 add)
|
||||
const _synonyms = {
|
||||
create: 'add',
|
||||
recycle: 'recyclebin',
|
||||
batch_delete: 'delete',
|
||||
batchDelete: 'delete'
|
||||
};
|
||||
|
||||
const _splitPermissions = function (perm) {
|
||||
if (_isArray(perm)) return perm;
|
||||
if (_isString(perm)) return perm.split(',').map(p => p.trim());
|
||||
return [];
|
||||
};
|
||||
|
||||
// 公共方法
|
||||
return {
|
||||
/**
|
||||
* 初始化权限
|
||||
* @param {Array} permissions - 用户权限列表
|
||||
* @param {Object} user - 用户信息
|
||||
*/
|
||||
init: function (permissions = [], user = null) {
|
||||
_permissions = permissions || [];
|
||||
_user = user;
|
||||
_controller = null;
|
||||
this.clearCache();
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取当前用户
|
||||
* @returns {Object} 用户信息
|
||||
*/
|
||||
getUser: function () {
|
||||
return _user;
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置当前用户
|
||||
* @param {Object} user - 用户信息
|
||||
*/
|
||||
setUser: function (user) {
|
||||
_user = user;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取权限列表
|
||||
* @returns {Array} 权限列表
|
||||
*/
|
||||
getPermissions: function () {
|
||||
return _permissions;
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置当前页面对应的控制器(用于裸动作 token 解析,如 has('view') → admin:view)
|
||||
*/
|
||||
setController: function (controller) {
|
||||
_controller = controller || null;
|
||||
this.clearCache();
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查单个权限
|
||||
* 支持:
|
||||
* - 精确 key:admin:edit
|
||||
* - 控制器通配:admin:*(覆盖该控制器下所有动作)
|
||||
* - 超级管理员:* / *:*
|
||||
* - 裸动作 token(结合 setController):view → admin:view,create → admin:add(同义词)
|
||||
* @param {String} permission - 权限标识
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
has: function (permission) {
|
||||
if (!permission) return true;
|
||||
|
||||
// 使用缓存
|
||||
if (_permissionCache[permission] !== undefined) {
|
||||
return _permissionCache[permission];
|
||||
}
|
||||
|
||||
const perms = _splitPermissions(permission);
|
||||
const result = perms.some(p => this._match(p));
|
||||
_permissionCache[permission] = result;
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* 单个权限标识匹配(内部)
|
||||
*/
|
||||
_match: function (p) {
|
||||
if (!p) return true;
|
||||
|
||||
// 精确匹配
|
||||
if (_permissions.indexOf(p) > -1) return true;
|
||||
|
||||
// 超级管理员放行
|
||||
if (_permissions.indexOf('*') > -1 || _permissions.indexOf('*:*') > -1) return true;
|
||||
|
||||
// 完整 key:controller:action / controller:*
|
||||
if (p.indexOf(':') > -1) {
|
||||
const parts = p.split(':');
|
||||
const ctrl = parts[0];
|
||||
const act = parts[1];
|
||||
if (act === '*') {
|
||||
return _permissions.indexOf(ctrl + ':*') > -1;
|
||||
}
|
||||
return _permissions.indexOf(ctrl + ':' + act) > -1
|
||||
|| _permissions.indexOf(ctrl + ':*') > -1;
|
||||
}
|
||||
|
||||
// 裸动作 token:结合当前控制器解析(含同义词)
|
||||
const action = _synonyms[p] || p;
|
||||
if (_controller) {
|
||||
if (_permissions.indexOf(_controller + ':*') > -1) return true;
|
||||
if (_permissions.indexOf(_controller + ':' + action) > -1) return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查多个权限(满足一个即可)
|
||||
* @param {Array} permissions - 权限列表
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasAny: function (permissions) {
|
||||
if (!_isArray(permissions) || permissions.length === 0) return false;
|
||||
return permissions.some(p => this.has(p));
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查所有权限
|
||||
* @param {Array} permissions - 权限列表
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasAll: function (permissions) {
|
||||
if (!_isArray(permissions) || permissions.length === 0) return false;
|
||||
return permissions.every(p => this.has(p));
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查权限并返回权限标识
|
||||
* @param {String|Array} permissions - 权限标识
|
||||
* @returns {String|Boolean} 有权限返回权限标识,无权限返回false
|
||||
*/
|
||||
check: function (permissions) {
|
||||
if (_isArray(permissions)) {
|
||||
for (let p of permissions) {
|
||||
if (this.has(p)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return this.has(permissions) ? permissions : false;
|
||||
},
|
||||
|
||||
/**
|
||||
* 过滤有权限的按钮
|
||||
* @param {Array} buttons - 按钮配置数组
|
||||
* @returns {Array} 有权限的按钮数组
|
||||
*/
|
||||
filterButtons: function (buttons) {
|
||||
if (!_isArray(buttons)) return [];
|
||||
return buttons.filter(btn => {
|
||||
// 如果没有设置permission,默认显示
|
||||
if (!btn.permission) return true;
|
||||
// 支持多个权限,用逗号分隔
|
||||
const perms = _splitPermissions(btn.permission);
|
||||
return this.hasAny(perms);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成工具条HTML
|
||||
* @param {Array} buttons - 按钮配置数组
|
||||
* @param {String} containerClass - 容器样式类
|
||||
* @param {Object} options - 额外选项
|
||||
* @returns {String} HTML字符串
|
||||
*/
|
||||
renderToolbar: function (buttons, containerClass = 'layui-btn-container', options = {}) {
|
||||
const filteredButtons = this.filterButtons(buttons);
|
||||
|
||||
if (filteredButtons.length === 0) return '';
|
||||
console.log('🔧 渲染工具条 - 需要在项目中实现',filteredButtons);
|
||||
|
||||
// const html = filteredButtons.map(btn => {
|
||||
// const color = btn.color || 'layui-btn-primary';
|
||||
// const size = btn.size || '';
|
||||
// const icon = btn.icon ? `<i class="${btn.icon}"></i>` : '';
|
||||
// const disabled = btn.disabled ? 'disabled' : '';
|
||||
// const style = btn.style ? `style="${btn.style}"` : '';
|
||||
|
||||
// return `<button class="layui-btn ${color} ${size}" lay-event="${btn.layEvent}" title="${btn.title}" ${disabled} ${style}>
|
||||
// ${icon}${btn.title}
|
||||
// </button>`;
|
||||
// }).join('');
|
||||
|
||||
// const containerStyle = options.containerStyle ? `style="${options.containerStyle}"` : '';
|
||||
// return `<div class="${containerClass}" ${containerStyle}>${html}</div>`;
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成行工具条HTML
|
||||
* @param {Array} buttons - 按钮配置数组
|
||||
* @param {Object} options - 额外选项
|
||||
* @returns {String} HTML字符串
|
||||
*/
|
||||
renderRowToolbar: function (buttons, options = {}) {
|
||||
const filteredButtons = this.filterButtons(buttons);
|
||||
|
||||
if (filteredButtons.length === 0) return '';
|
||||
|
||||
const html = filteredButtons.map(btn => {
|
||||
const color = btn.color || 'layui-btn-primary layui-btn-xs';
|
||||
const icon = btn.icon ? `<i class="${btn.icon}"></i>` : '';
|
||||
const disabled = btn.disabled ? 'disabled' : '';
|
||||
const style = btn.style ? `style="${btn.style}"` : '';
|
||||
|
||||
return `<a class="layui-btn ${color}" lay-event="${btn.layEvent}" title="${btn.title}" ${disabled} ${style}>
|
||||
${icon}
|
||||
</a>`;
|
||||
}).join('');
|
||||
|
||||
return `<div class="layui-btn-group">${html}</div>`;
|
||||
},
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
*/
|
||||
clearCache: function () {
|
||||
_permissionCache = {};
|
||||
},
|
||||
|
||||
/**
|
||||
* 批量检查权限
|
||||
* @param {Array} permissions - 权限列表
|
||||
* @returns {Object} 权限检查结果对象
|
||||
*/
|
||||
batchCheck: function (permissions) {
|
||||
if (!_isArray(permissions)) return {};
|
||||
|
||||
return permissions.reduce((result, perm) => {
|
||||
result[perm] = this.has(perm);
|
||||
return result;
|
||||
}, {});
|
||||
},
|
||||
|
||||
/**
|
||||
* 预加载常用权限
|
||||
* @param {Array} permissions - 常用权限列表
|
||||
*/
|
||||
preload: function (permissions = ['create', 'edit', 'delete', 'view']) {
|
||||
permissions.forEach(perm => this.has(perm));
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
/**
|
||||
* =============================================
|
||||
* 动态权限更新管理器
|
||||
* =============================================
|
||||
*/
|
||||
const PermissionManager = (function () {
|
||||
// 私有变量
|
||||
const _listeners = [];
|
||||
let _refreshTimeout = null;
|
||||
|
||||
// 公共方法
|
||||
return {
|
||||
/**
|
||||
* 添加权限变更监听器
|
||||
* @param {Function} callback - 回调函数
|
||||
* @param {String} id - 监听器ID(用于移除)
|
||||
*/
|
||||
onPermissionChange: function (callback, id = null) {
|
||||
_listeners.push({ callback, id });
|
||||
},
|
||||
|
||||
/**
|
||||
* 移除权限变更监听器
|
||||
* @param {String} id - 监听器ID
|
||||
*/
|
||||
offPermissionChange: function (id) {
|
||||
const index = _listeners.findIndex(l => l.id === id);
|
||||
if (index !== -1) {
|
||||
_listeners.splice(index, 1);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 触发权限变更
|
||||
* @param {Array} newPermissions - 新的权限列表
|
||||
* @param {Object} user - 用户信息
|
||||
*/
|
||||
triggerChange: function (newPermissions, user = null) {
|
||||
// 更新权限
|
||||
Auth.init(newPermissions, user);
|
||||
|
||||
// 保存到localStorage
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem('permissions', JSON.stringify(newPermissions));
|
||||
if (user) {
|
||||
localStorage.setItem('user', JSON.stringify(user));
|
||||
}
|
||||
}
|
||||
|
||||
// 通知所有监听器
|
||||
_listeners.forEach(({ callback }) => {
|
||||
try {
|
||||
callback(newPermissions, user);
|
||||
} catch (e) {
|
||||
console.error('权限变更监听器执行出错:', e);
|
||||
}
|
||||
});
|
||||
|
||||
// 防抖刷新表格
|
||||
clearTimeout(_refreshTimeout);
|
||||
_refreshTimeout = setTimeout(() => {
|
||||
this.refreshTables();
|
||||
}, 100);
|
||||
},
|
||||
|
||||
/**
|
||||
* 刷新所有表格
|
||||
*/
|
||||
refreshTables: function () {
|
||||
console.log('🔄 刷新表格 - 需要在项目中实现');
|
||||
|
||||
// 触发自定义事件
|
||||
const event = new CustomEvent('permissionChanged', {
|
||||
detail: { permissions: Auth.getPermissions() }
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.dispatchEvent(event);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 从服务器同步最新权限
|
||||
* @param {String} url - 权限接口地址
|
||||
* @returns {Promise} 同步结果
|
||||
*/
|
||||
syncPermissions: function (url = '/api/user/permissions') {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// if (typeof $ !== 'undefined' && $.ajax) {
|
||||
// $.get(url, function (res) {
|
||||
// if (res.code === 200 || res.success) {
|
||||
// this.triggerChange(res.data.permissions || res.data, res.data.user);
|
||||
// resolve(true);
|
||||
// } else {
|
||||
// reject(new Error(res.msg || '同步权限失败'));
|
||||
// }
|
||||
// }.bind(this)).fail(function (xhr) {
|
||||
// reject(new Error('网络请求失败: ' + xhr.statusText));
|
||||
// });
|
||||
// } else if (typeof fetch !== 'undefined') {
|
||||
// fetch(url)
|
||||
// .then(res => res.json())
|
||||
// .then(res => {
|
||||
// if (res.code === 200 || res.success) {
|
||||
// this.triggerChange(res.data.permissions || res.data, res.data.user);
|
||||
// resolve(true);
|
||||
// } else {
|
||||
// reject(new Error(res.msg || '同步权限失败'));
|
||||
// }
|
||||
// })
|
||||
// .catch(err => reject(err));
|
||||
// } else {
|
||||
// reject(new Error('不支持的HTTP请求方式'));
|
||||
// }
|
||||
// }.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* 清除所有监听器
|
||||
*/
|
||||
clearListeners: function () {
|
||||
_listeners.length = 0;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
/**
|
||||
* =============================================
|
||||
* 权限按钮组件
|
||||
* =============================================
|
||||
*/
|
||||
class AuthButton extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
this._clickHandler = this._handleClick.bind(this);
|
||||
}
|
||||
|
||||
static get observedAttributes() {
|
||||
return ['permission', 'text', 'icon', 'type', 'size', 'disabled'];
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this.shadowRoot.removeEventListener('click', this._clickHandler);
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
if (oldValue !== newValue) {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
_handleClick(e) {
|
||||
if (!this.hasAttribute('disabled')) {
|
||||
this.dispatchEvent(new CustomEvent('auth-click', {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { button: this }
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const permission = this.getAttribute('permission');
|
||||
const text = this.getAttribute('text') || '按钮';
|
||||
const icon = this.getAttribute('icon') || '';
|
||||
const type = this.getAttribute('type') || 'primary';
|
||||
const size = this.getAttribute('size') || '';
|
||||
const disabled = this.hasAttribute('disabled');
|
||||
const hasPermission = !permission || Auth.has(permission);
|
||||
|
||||
// 无权限时隐藏
|
||||
if (!hasPermission) {
|
||||
this.shadowRoot.innerHTML = '';
|
||||
this.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
this.style.display = '';
|
||||
const iconHtml = icon ? `<i class="${icon}"></i>` : '';
|
||||
const btnClass = `layui-btn layui-btn-${type} ${size}`;
|
||||
const disabledAttr = disabled ? 'disabled' : '';
|
||||
|
||||
this.shadowRoot.innerHTML = `
|
||||
<style>
|
||||
:host {
|
||||
display: inline-block;
|
||||
}
|
||||
.layui-btn {
|
||||
display: inline-block;
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
padding: 0 18px;
|
||||
background-color: #009688;
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
text-decoration: none;
|
||||
}
|
||||
.layui-btn:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.layui-btn[disabled] {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.layui-btn-xs {
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
padding: 0 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.layui-btn-sm {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.layui-btn-lg {
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
padding: 0 25px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.layui-btn-primary {
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
border: 1px solid #d2d2d2;
|
||||
}
|
||||
.layui-btn-normal {
|
||||
background-color: #1E9FFF;
|
||||
}
|
||||
.layui-btn-warm {
|
||||
background-color: #FFB800;
|
||||
}
|
||||
.layui-btn-danger {
|
||||
background-color: #FF5722;
|
||||
}
|
||||
i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
</style>
|
||||
<button class="${btnClass}" ${disabledAttr}>
|
||||
${iconHtml}${text}
|
||||
</button>
|
||||
`;
|
||||
|
||||
// 添加点击事件
|
||||
const btn = this.shadowRoot.querySelector('button');
|
||||
btn.removeEventListener('click', this._clickHandler);
|
||||
btn.addEventListener('click', this._clickHandler);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册自定义元素
|
||||
if (typeof customElements !== 'undefined' && !customElements.get('auth-button')) {
|
||||
customElements.define('auth-button', AuthButton);
|
||||
}
|
||||
|
||||
/**
|
||||
* =============================================
|
||||
* 导出模块
|
||||
* =============================================
|
||||
*/
|
||||
exports('auth', {
|
||||
// 核心对象auth
|
||||
Auth: Auth,
|
||||
PermissionConfig: PermissionConfig,
|
||||
PermissionManager: PermissionManager,
|
||||
|
||||
// 便捷方法(直接暴露常用方法)
|
||||
init: Auth.init.bind(Auth),
|
||||
setController: Auth.setController.bind(Auth),
|
||||
has: Auth.has.bind(Auth),
|
||||
hasAny: Auth.hasAny.bind(Auth),
|
||||
hasAll: Auth.hasAll.bind(Auth),
|
||||
getPermissions: Auth.getPermissions.bind(Auth),
|
||||
renderToolbar: Auth.renderToolbar.bind(Auth),
|
||||
renderRowToolbar: Auth.renderRowToolbar.bind(Auth),
|
||||
filterButtons: Auth.filterButtons.bind(Auth),
|
||||
|
||||
// 配置方法
|
||||
getToolbarConfig: PermissionConfig.getToolbarConfig.bind(PermissionConfig),
|
||||
registerButton: PermissionConfig.registerButton.bind(PermissionConfig),
|
||||
registerButtons: PermissionConfig.registerButtons.bind(PermissionConfig),
|
||||
|
||||
// 管理器方法
|
||||
onPermissionChange: PermissionManager.onPermissionChange.bind(PermissionManager),
|
||||
triggerChange: PermissionManager.triggerChange.bind(PermissionManager),
|
||||
syncPermissions: PermissionManager.syncPermissions.bind(PermissionManager),
|
||||
|
||||
// 按钮组件
|
||||
AuthButton: AuthButton
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,364 @@
|
||||
|
||||
// ==================== ChatClient 模块定义 (ES6 兼容) ====================
|
||||
layui.define(['jquery', 'layer', 'laytpl', 'util'], function (exports) {
|
||||
"use strict";
|
||||
|
||||
class ChatClient {
|
||||
constructor(config) {
|
||||
this.config = Object.assign({
|
||||
wsUrl: 'wss://echo.websocket.org',
|
||||
token: '',
|
||||
uid: '',
|
||||
reconnectDelay: 3000,
|
||||
maxReconnect: 5
|
||||
}, config);
|
||||
|
||||
this.ws = null;
|
||||
this.reconnectCount = 0;
|
||||
this.messageQueue = [];
|
||||
this.isConnected = false;
|
||||
this.currentSession = { id: '', type: '' };
|
||||
this.requestCallbacks = {};
|
||||
this.heartbeatTimer = null;
|
||||
this.eventHandlers = {};
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统事件
|
||||
* @param {*} event
|
||||
* @param {*} handler
|
||||
* @returns
|
||||
*/
|
||||
on(event, handler) {
|
||||
if (!this.eventHandlers[event]) this.eventHandlers[event] = [];
|
||||
this.eventHandlers[event].push(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发
|
||||
* @param {*} event
|
||||
* @param {...any} args
|
||||
* @returns
|
||||
*/
|
||||
emit(event, ...args) {
|
||||
if (this.eventHandlers[event]) {
|
||||
this.eventHandlers[event].forEach(handler => handler.apply(this, args));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
init() {
|
||||
this.connect();
|
||||
window.addEventListener('beforeunload', () => this.disconnect());
|
||||
}
|
||||
|
||||
/**
|
||||
* 链接
|
||||
*/
|
||||
connect() {
|
||||
try {
|
||||
this.ws = new WebSocket(this.config.wsUrl);
|
||||
this.ws.onopen = () => {
|
||||
console.log('✅ WebSocket 连接成功');
|
||||
this.isConnected = true;
|
||||
this.reconnectCount = 5;
|
||||
this.sendAuth();
|
||||
this.startHeartbeat();
|
||||
this.emit('connect');
|
||||
// 重发队列消息
|
||||
while (this.messageQueue.length > 0) {
|
||||
const msg = this.messageQueue.shift();
|
||||
this.ws.send(JSON.stringify(msg));
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onmessage = (event) => {
|
||||
try {
|
||||
const packet = JSON.parse(event.data);
|
||||
this.handleMessage(packet);
|
||||
} catch (e) {
|
||||
console.error('❌ 消息解析失败:', e);
|
||||
this.emit('error', e);
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onerror = (error) => {
|
||||
console.error('❌ WebSocket 错误:', error);
|
||||
this.emit('error', error);
|
||||
};
|
||||
|
||||
this.ws.onclose = () => {
|
||||
console.log('⚠️ WebSocket 连接关闭');
|
||||
this.isConnected = false;
|
||||
this.stopHeartbeat();
|
||||
this.emit('close');
|
||||
this.attemptReconnect();
|
||||
};
|
||||
} catch (e) {
|
||||
console.error('❌ WebSocket 初始化失败:', e);
|
||||
this.attemptReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送验证信息
|
||||
*/
|
||||
sendAuth() {
|
||||
this.send({ content: this.config.token }, 'sys', 'auth');
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始心跳
|
||||
*/
|
||||
startHeartbeat() {
|
||||
this.stopHeartbeat();
|
||||
this.heartbeatTimer = setInterval(() => {
|
||||
if (this.isConnected && this.ws.readyState === WebSocket.OPEN) {
|
||||
this.send({}, 'sys', 'heartbeat');
|
||||
}
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止心跳
|
||||
*/
|
||||
stopHeartbeat() {
|
||||
if (this.heartbeatTimer) {
|
||||
clearInterval(this.heartbeatTimer);
|
||||
this.heartbeatTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重连
|
||||
*/
|
||||
attemptReconnect() {
|
||||
if (this.reconnectCount < this.config.maxReconnect && !this.isConnected) {
|
||||
this.reconnectCount++;
|
||||
console.log(`🔄 尝试重连 (${this.reconnectCount}/${this.config.maxReconnect})...`);
|
||||
setTimeout(() => this.connect(), this.config.reconnectDelay * this.reconnectCount);
|
||||
} else {
|
||||
this.emit('reconnect-failed');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送
|
||||
* @param {*} payload
|
||||
* @param {*} msgType
|
||||
* @param {*} options
|
||||
* @returns
|
||||
*/
|
||||
send(payload, _frame, type, options = {}) {
|
||||
const packet = {
|
||||
header: {
|
||||
id: 'pkt_' + Date.now() + '_' + Math.random().toString(36).substr(2, 5),
|
||||
frame: _frame,
|
||||
type: type,
|
||||
from: this.user?.uid,
|
||||
timestamp: Date.now()
|
||||
},
|
||||
payload: payload || {}
|
||||
};
|
||||
// 存储回调
|
||||
if (typeof options.callback === 'function') {
|
||||
this.requestCallbacks[packet.header.id] = {
|
||||
callback: options.callback,
|
||||
timeout: setTimeout(() => {
|
||||
if (this.requestCallbacks[packet.header.id]) {
|
||||
this.requestCallbacks[packet.header.id].callback({
|
||||
header: { type: 'timeout' },
|
||||
payload: { message: '请求超时' }
|
||||
});
|
||||
delete this.requestCallbacks[packet.header.id];
|
||||
}
|
||||
}, 10000)
|
||||
};
|
||||
}
|
||||
if (this.isConnected && this.ws.readyState === WebSocket.OPEN) {
|
||||
this.ws.send(JSON.stringify(packet));
|
||||
} else {
|
||||
this.messageQueue.push(packet);
|
||||
if (!this.isConnected) this.connect();
|
||||
}
|
||||
return packet.header.id;
|
||||
}
|
||||
|
||||
handleMessage(packet) {
|
||||
const frame = packet.header?.frame;
|
||||
// if (packet.header?.id) {
|
||||
// const origId = packet.header.id;
|
||||
// if (this.requestCallbacks[origId]) {
|
||||
// clearTimeout(this.requestCallbacks[origId].timeout);
|
||||
// this.requestCallbacks[origId].callback(packet);
|
||||
// delete this.requestCallbacks[origId];
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// 业务消息分发
|
||||
switch (frame) {
|
||||
case 'sys':
|
||||
this.handleSystem(packet);
|
||||
break;
|
||||
case 'chat':
|
||||
this.emit('friend-apply', packet.payload);
|
||||
break;
|
||||
case 'rel':
|
||||
this.emit('message-read', packet.payload);
|
||||
break;
|
||||
case 'ctl':
|
||||
this.emit('friend-apply-result', packet.payload);
|
||||
break;
|
||||
case 'err':
|
||||
this.emit('group-invite', packet.payload);
|
||||
break;
|
||||
case 'auth':
|
||||
this.emit('auth-response', packet.payload);
|
||||
break;
|
||||
case 'heartbeat':
|
||||
this.emit('auth-response', packet.payload);
|
||||
break;
|
||||
default:
|
||||
this.emit('message', packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
handleSystem(packet) {
|
||||
const type = packet.header?.type;
|
||||
switch (type) {
|
||||
case 'heartbeat':
|
||||
break;
|
||||
case 'auth':
|
||||
this.user = packet.payload.content ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 业务方法(只处理数据,不涉及渲染)
|
||||
sendMessage(sessionId, sessionType, content, msgType = 'text') {
|
||||
const message = {
|
||||
header: {
|
||||
id: 'msg_' + Date.now(),
|
||||
type: msgType,
|
||||
subtype: sessionType,
|
||||
from: this.user.uid,
|
||||
to: sessionId,
|
||||
timestamp: new Date().toISOString()
|
||||
},
|
||||
payload: {
|
||||
content: content,
|
||||
}
|
||||
};
|
||||
this.ws.send(JSON.stringify(packet));
|
||||
// // 发送消息到服务器
|
||||
// this.send({
|
||||
// session_id: sessionId,
|
||||
// session_type: sessionType,
|
||||
// content: content,
|
||||
// type: msgType,
|
||||
// id: msgId
|
||||
// }, 'send_message', {
|
||||
// callback: (res) => {
|
||||
// if (res.payload?.success) {
|
||||
// // 更新本地消息状态为已发送
|
||||
// message.read_status = 'sent';
|
||||
// this.emit('message-sent', message);
|
||||
// } else {
|
||||
// message.read_status = 'failed';
|
||||
// this.emit('message-failed', message);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
// 立即触发本地消息事件(用于前端渲染)
|
||||
this.emit('local-message', message);
|
||||
return msgId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送图片
|
||||
* @param {*} sessionId
|
||||
* @param {*} sessionType
|
||||
* @param {*} imgUrl
|
||||
* @returns
|
||||
*/
|
||||
sendImage(sessionId, sessionType, imgUrl) {
|
||||
return this.sendMessage(sessionId, sessionType, `[图片]${imgUrl}`, 'image');
|
||||
}
|
||||
|
||||
/**发送读取状态 */
|
||||
sendReadReceipt(msgId, sessionId, sessionType) {
|
||||
this.send({
|
||||
id: msgId,
|
||||
session_id: sessionId,
|
||||
session_type: sessionType
|
||||
}, 'read_receipt');
|
||||
}
|
||||
|
||||
/**设置会话 */
|
||||
setCurrentSession(sessionId, sessionType) {
|
||||
this.currentSession = { id: sessionId, type: sessionType };
|
||||
this.emit('session-changed', this.currentSession);
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索用户
|
||||
* @param {*} keyword
|
||||
* @param {*} callback
|
||||
*/
|
||||
searchUsers(keyword, callback) {
|
||||
// 实际项目中替换为真实API调用
|
||||
setTimeout(() => {
|
||||
const mockUsers = [
|
||||
{ uid: 'u2001', nickname: '设计师小王', avatar: 'W', is_friend: false },
|
||||
{ uid: 'u2002', nickname: '产品经理李', avatar: 'L', is_friend: true },
|
||||
{ uid: 'u2003', nickname: '后端工程师赵', avatar: 'Z', is_friend: false }
|
||||
].filter(u => u.nickname.includes(keyword) || u.uid.includes(keyword));
|
||||
callback({ code: 0, data: mockUsers });
|
||||
}, 300);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加好友
|
||||
* @param {*} targetUid
|
||||
* @param {*} remark
|
||||
* @param {*} callback
|
||||
*/
|
||||
addFriend(targetUid, remark, callback) {
|
||||
this.send({
|
||||
target_uid: targetUid,
|
||||
remark: remark || '你好,加个好友吧~'
|
||||
}, 'add_friend_request', { callback });
|
||||
}
|
||||
|
||||
/**
|
||||
* 接受好友
|
||||
* @param {*} applyId
|
||||
* @param {*} agree
|
||||
* @param {*} callback
|
||||
*/
|
||||
handleFriendApply(applyId, agree, callback) {
|
||||
this.send({
|
||||
apply_id: applyId,
|
||||
action: agree ? 'agree' : 'reject'
|
||||
}, 'handle_friend_apply', { callback });
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.stopHeartbeat();
|
||||
if (this.ws) {
|
||||
this.ws.close();
|
||||
this.ws = null;
|
||||
}
|
||||
this.isConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 导出模块
|
||||
exports('chatClient', ChatClient);
|
||||
});
|
||||
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
* common
|
||||
* 通用元素组件
|
||||
*/
|
||||
layui.define('component', function (exports) {
|
||||
var table = layui.table;
|
||||
// 创建组件
|
||||
var component = layui.component({
|
||||
name: 'common', // 组件名称
|
||||
config: {
|
||||
urls: {
|
||||
create: '',
|
||||
edit: '',
|
||||
delete: '',
|
||||
restore: '',
|
||||
},
|
||||
cols: []
|
||||
},
|
||||
CONST: {},
|
||||
render: function () { // 组件渲染逻辑
|
||||
// …
|
||||
},
|
||||
beforeInit: function (options) {
|
||||
console.log(options); // 获得组件初始化前的配置项
|
||||
},
|
||||
beforeRender: function (options) {
|
||||
console.log(options); // 获得组件渲染前的配置项
|
||||
},
|
||||
extendsInstance: function (that) {
|
||||
return {
|
||||
// 关闭组件
|
||||
close: function () {
|
||||
that.remove(); // 调用组件原型中的 remove 方法
|
||||
}
|
||||
}
|
||||
},
|
||||
events: function () {
|
||||
// 亦可包含针对组件的 window, document 等全局事件
|
||||
// …
|
||||
}
|
||||
// 其他选项
|
||||
});
|
||||
|
||||
var Class = component.Class;
|
||||
|
||||
Class.prototype.tableRender = function () {
|
||||
var dataTable = table.render({
|
||||
elem: '#dataTable'
|
||||
, url: '/admin/Role/index' // 后端数据接口
|
||||
, page: true
|
||||
, limit: 15
|
||||
, toolbar: '#tableBarTpl'
|
||||
, cols: [[
|
||||
{ type: 'checkbox', fixed: 'left' }
|
||||
, { field: 'id', title: 'ID', width: 60, align: 'center', sort: true, fixed: 'left' }
|
||||
, { field: 'title', title: '名称', width: 150, align: 'center', fixed: 'left' }
|
||||
, { field: 'name', title: '代码', width: 150, align: 'center', fixed: 'left' }
|
||||
, { field: 'description', title: '描述', minWidth: 200, align: 'left' }
|
||||
, { field: 'status', title: '状态', width: 120, align: 'center', templet: '#statusTpl' }
|
||||
, { field: 'create_time', title: '创建时间', width: 160, align: 'center', sort: true }
|
||||
, { title: '操作', width: 220, align: 'left', toolbar: '#dataBarTpl', fixed: 'right', unresize: true }
|
||||
]]
|
||||
, done: function (res, curr, count) { }
|
||||
});
|
||||
|
||||
table.on('toolbar(dataTable)', function (elem) {
|
||||
switch (elem.event) {
|
||||
case 'dataCreate':
|
||||
active.dataCreate({ pid: 0, type: 1 });
|
||||
break;
|
||||
case 'dataDelete':
|
||||
var checkStatus = table.checkStatus('dataTable'), checkData = checkStatus.data;
|
||||
let ids = checkData.map((item, index, array) => { return item.id; });
|
||||
active.dataDelete(ids);
|
||||
break;
|
||||
case 'dataRecybin':
|
||||
active.dataRecybin();
|
||||
break;
|
||||
};
|
||||
});
|
||||
|
||||
table.on('tool(dataTable)', function (elem) {
|
||||
var data = elem.data;
|
||||
switch (elem.event) {
|
||||
case 'update':
|
||||
active.dataEdit(data);
|
||||
break;
|
||||
case 'permission':
|
||||
openPermissionForm(data);
|
||||
break;
|
||||
case 'delete':
|
||||
active.dataDelete([data.id]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
Class.prototype.fromRender = function (data, callback, done) {
|
||||
var title = data ? '编辑数据[' + data.account + ']' : '添加数据';
|
||||
var formHtml = laytpl($('#dataFormTpl').html()).render(data || {});
|
||||
layer.open({
|
||||
title: data.id ? '编辑菜单' : (data.pid ? '添加子菜单' : '添加根菜单'),
|
||||
content: formHtml,
|
||||
anim: "slideLeft",
|
||||
offset: "r",
|
||||
btnAlign: "l",
|
||||
area: ['50% ', '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');
|
||||
}
|
||||
});
|
||||
};
|
||||
Class.prototype.dataCreate = function (data = {}) {
|
||||
dataFromFun(data,
|
||||
function (layero, index) { },
|
||||
function (layero, index, elem) {
|
||||
var field = elem.field;
|
||||
http.post('/admin/Role/save', field)
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
layer.close(index);
|
||||
table.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg || '操作失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
Class.prototype.dataEdit = function (data = {}) {
|
||||
dataFromFun(data,
|
||||
function (layero, index) { },
|
||||
function (layero, index, elem) {
|
||||
var field = elem.field;
|
||||
http.put('/admin/Role/update', field)
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
layer.close(index);
|
||||
table.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg || '操作失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
Class.prototype.dataRecybin = function () {
|
||||
// …
|
||||
};
|
||||
Class.prototype.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('/admin/Role/delete', { ids: ids.join(','), force: force })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
layer.close(index);
|
||||
table.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg || '操作失败');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
Class.prototype.dataRestore = function (ids) {
|
||||
if (ids.length === 0) {
|
||||
return layer.msg('请选择数据');
|
||||
}
|
||||
http.put('/admin/Role/restore', { ids: ids.join(',') })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
layer.msg('恢复成功');
|
||||
table.reload('dataTable', {}, true);
|
||||
} else {
|
||||
layer.msg(res.msg || '恢复失败');
|
||||
}
|
||||
});
|
||||
};
|
||||
exports(component.CONST.MOD_NAME, component);
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
body {
|
||||
}
|
||||
/*!
|
||||
* Cropper v0.7.6-beta
|
||||
* https://github.com/fengyuanchen/cropper
|
||||
*
|
||||
* Copyright 2014 Fengyuan Chen
|
||||
* Released under the MIT license
|
||||
*/
|
||||
|
||||
.cropper-container{position:relative;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none}.cropper-container img{width:100%;height:100%;min-width:0!important;min-height:0!important;max-width:none!important;max-height:none!important}.cropper-modal,.cropper-canvas{position:absolute;top:0;right:0;bottom:0;left:0}.cropper-canvas{background-color:#fff;opacity:0;filter:alpha(opacity=0)}.cropper-modal{background-color:#000;opacity:.5;filter:alpha(opacity=50)}.cropper-dragger{position:absolute;top:10%;left:10%;width:80%;height:80%}.cropper-viewer{display:block;width:100%;height:100%;overflow:hidden;outline-width:1px;outline-style:solid;outline-color:#69f;outline-color:rgba(51,102,255,.75)}.cropper-dashed{position:absolute;display:block;border:0 dashed #fff;opacity:.5;filter:alpha(opacity=50)}.cropper-dashed.dashed-h{top:33.3%;left:0;width:100%;height:33.3%;border-top-width:1px;border-bottom-width:1px}.cropper-dashed.dashed-v{top:0;left:33.3%;width:33.3%;height:100%;border-right-width:1px;border-left-width:1px}.cropper-face,.cropper-line,.cropper-point{position:absolute;display:block;width:100%;height:100%;opacity:.1;filter:alpha(opacity=10)}.cropper-face{top:0;left:0;cursor:move;background-color:#fff}.cropper-line{background-color:#69f}.cropper-line.line-e{top:0;right:-3px;width:5px;cursor:e-resize}.cropper-line.line-n{top:-3px;left:0;height:5px;cursor:n-resize}.cropper-line.line-w{top:0;left:-3px;width:5px;cursor:w-resize}.cropper-line.line-s{bottom:-3px;left:0;height:5px;cursor:s-resize}.cropper-point{width:5px;height:5px;background-color:#69f;opacity:.75;filter:alpha(opacity=75)}.cropper-point.point-e{top:50%;right:-3px;margin-top:-3px;cursor:e-resize}.cropper-point.point-n{top:-3px;left:50%;margin-left:-3px;cursor:n-resize}.cropper-point.point-w{top:50%;left:-3px;margin-top:-3px;cursor:w-resize}.cropper-point.point-s{bottom:-3px;left:50%;margin-left:-3px;cursor:s-resize}.cropper-point.point-ne{top:-3px;right:-3px;cursor:ne-resize}.cropper-point.point-nw{top:-3px;left:-3px;cursor:nw-resize}.cropper-point.point-sw{bottom:-3px;left:-3px;cursor:sw-resize}.cropper-point.point-se{right:-3px;bottom:-3px;width:20px;height:20px;cursor:se-resize;opacity:1;filter:alpha(opacity=100)}.cropper-point.point-se:before{position:absolute;right:-50%;bottom:-50%;display:block;width:200%;height:200%;content:" ";background-color:#69f;opacity:0;filter:alpha(opacity=0)}@media (min-width:768px){.cropper-point.point-se{width:15px;height:15px}}@media (min-width:992px){.cropper-point.point-se{width:10px;height:10px}}@media (min-width:1200px){.cropper-point.point-se{width:5px;height:5px;opacity:.75;filter:alpha(opacity=75)}}.cropper-hidden{display:none!important}.cropper-invisible{position:fixed;top:0;left:0;z-index:-1;width:auto!important;max-width:none!important;height:auto!important;max-height:none!important;opacity:0;filter:alpha(opacity=0)}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-canvas,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,133 @@
|
||||
/*!
|
||||
* Cropper v3.0.0
|
||||
*/
|
||||
|
||||
layui.define(['jquery','layer','cropper'],function (exports) {
|
||||
var $ = layui.jquery
|
||||
,layer = layui.layer;
|
||||
var html = "<link rel=\"stylesheet\" href=\"/assets/ywxapp/modules/cropper/cropper.css\">\n" +
|
||||
"<div class=\"layui-fluid showImgEdit\" style=\"display: none\">\n" +
|
||||
" <div class=\"layui-form-item\">\n" +
|
||||
" <div class=\"layui-input-inline layui-btn-container\" style=\"width: auto;\">\n" +
|
||||
" <label for=\"cropper_avatarImgUpload\" class=\"layui-btn layui-btn-primary\">\n" +
|
||||
" <i class=\"layui-icon\"></i>选择图片\n" +
|
||||
" </label>\n" +
|
||||
" <input class=\"layui-upload-file\" id=\"cropper_avatarImgUpload\" type=\"file\" value=\"选择图片\" name=\"file\">\n" +
|
||||
" </div>\n" +
|
||||
" <div class=\"layui-form-mid layui-word-aux\">头像的尺寸限定150x150px,大小在50kb以内</div>\n" +
|
||||
" </div>\n" +
|
||||
" <div class=\"layui-row layui-col-space15\">\n" +
|
||||
" <div class=\"layui-col-xs9\">\n" +
|
||||
" <div class=\"readyimg\" style=\"height:450px;background-color: rgb(247, 247, 247);\">\n" +
|
||||
" <img src=\"\" >\n" +
|
||||
" </div>\n" +
|
||||
" </div>\n" +
|
||||
" <div class=\"layui-col-xs3\">\n" +
|
||||
" <div class=\"img-preview\" style=\"width:200px;height:200px;overflow:hidden\">\n" +
|
||||
" </div>\n" +
|
||||
" </div>\n" +
|
||||
" </div>\n" +
|
||||
" <div class=\"layui-row layui-col-space15\">\n" +
|
||||
" <div class=\"layui-col-xs9\">\n" +
|
||||
" <div class=\"layui-row\">\n" +
|
||||
" <div class=\"layui-col-xs6\">\n" +
|
||||
" <button type=\"button\" class=\"layui-btn layui-icon layui-icon-left\" cropper-event=\"rotate\" data-option=\"-15\" title=\"Rotate -90 degrees\"> 向左旋转</button>\n" +
|
||||
" <button type=\"button\" class=\"layui-btn layui-icon layui-icon-right\" cropper-event=\"rotate\" data-option=\"15\" title=\"Rotate 90 degrees\"> 向右旋转</button>\n" +
|
||||
" </div>\n" +
|
||||
" <div class=\"layui-col-xs5\" style=\"text-align: right;\">\n" +
|
||||
" <button type=\"button\" class=\"layui-btn\" title=\"移动\"></button>\n" +
|
||||
" <button type=\"button\" class=\"layui-btn\" title=\"放大图片\"></button>\n" +
|
||||
" <button type=\"button\" class=\"layui-btn\" title=\"缩小图片\"></button>\n" +
|
||||
" <button type=\"button\" class=\"layui-btn layui-icon layui-icon-refresh\" cropper-event=\"reset\" title=\"重置图片\"></button>\n" +
|
||||
" </div>\n" +
|
||||
" </div>\n" +
|
||||
" </div>\n" +
|
||||
" <div class=\"layui-col-xs3\">\n" +
|
||||
" <button class=\"layui-btn layui-btn-fluid\" cropper-event=\"confirmSave\" type=\"button\"> 保存修改</button>\n" +
|
||||
" </div>\n" +
|
||||
" </div>\n" +
|
||||
"\n" +
|
||||
"</div>";
|
||||
var obj = {
|
||||
render: function(e){
|
||||
$('body').append(html);
|
||||
var self = this,
|
||||
elem = e.elem,
|
||||
saveW = e.saveW,
|
||||
saveH = e.saveH,
|
||||
mark = e.mark,
|
||||
area = e.area,
|
||||
url = e.url,
|
||||
done = e.done;
|
||||
|
||||
var content = $('.showImgEdit')
|
||||
,image = $(".showImgEdit .readyimg img")
|
||||
,preview = '.showImgEdit .img-preview'
|
||||
,file = $(".showImgEdit input[name='file']")
|
||||
, options = {aspectRatio: mark,preview: preview,viewMode:1};
|
||||
|
||||
$(elem).on('click',function () {
|
||||
layer.open({
|
||||
type: 1
|
||||
, content: content
|
||||
, area: area
|
||||
, success: function () {
|
||||
image.cropper(options);
|
||||
}
|
||||
, cancel: function (index) {
|
||||
layer.close(index);
|
||||
image.cropper('destroy');
|
||||
}
|
||||
});
|
||||
});
|
||||
$(".layui-btn").on('click',function () {
|
||||
var event = $(this).attr("cropper-event");
|
||||
//监听确认保存图像
|
||||
if(event === 'confirmSave'){
|
||||
image.cropper("getCroppedCanvas",{
|
||||
width: saveW,
|
||||
height: saveH
|
||||
}).toBlob(function(blob){
|
||||
var formData=new FormData();
|
||||
formData.append('file',blob,'head.jpg');
|
||||
$.ajax({
|
||||
method:"post",
|
||||
url: url, //用于文件上传的服务器端请求地址
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success:function(result){
|
||||
if(result.code == 0){
|
||||
layer.msg(result.msg,{icon: 1});
|
||||
layer.closeAll('page');
|
||||
return done(result.data.src);
|
||||
}else if(result.code == -1){
|
||||
layer.alert(result.msg,{icon: 2});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
//监听旋转
|
||||
}else if(event === 'rotate'){
|
||||
var option = $(this).attr('data-option');
|
||||
image.cropper('rotate', option);
|
||||
//重设图片
|
||||
}else if(event === 'reset'){
|
||||
image.cropper('reset');
|
||||
}
|
||||
//文件选择
|
||||
file.change(function () {
|
||||
var r= new FileReader();
|
||||
var f=this.files[0];
|
||||
r.readAsDataURL(f);
|
||||
r.onload=function (e) {
|
||||
image.cropper('destroy').attr('src', this.result).cropper(options);
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
exports('croppers', obj);
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,262 @@
|
||||
/**
|
||||
* Set echarts theme
|
||||
*/
|
||||
|
||||
|
||||
layui.define(function(exports) {
|
||||
exports('echartsTheme', {
|
||||
// 默认色板
|
||||
color: [
|
||||
'#16baaa','#1E9FFF','#16b777','#FFB980','#D87A80',
|
||||
'#8d98b3','#e5cf0d','#97b552','#95706d','#dc69aa',
|
||||
'#07a2a4','#9a7fd1','#588dd5','#f5994e','#c05050',
|
||||
'#59678c','#c9ab00','#7eb00a','#6f5553','#c14089'
|
||||
],
|
||||
|
||||
// 图表标题
|
||||
title: {
|
||||
textStyle: {
|
||||
fontWeight: 'normal',
|
||||
color: '#666' // 主标题文字颜色
|
||||
}
|
||||
},
|
||||
|
||||
// 值域
|
||||
dataRange: {
|
||||
itemWidth: 15,
|
||||
color: ['#16baaa','#e0ffff']
|
||||
},
|
||||
|
||||
// 工具箱
|
||||
toolbox: {
|
||||
color : ['#1e90ff', '#1e90ff', '#1e90ff', '#1e90ff'],
|
||||
effectiveColor : '#ff4500'
|
||||
},
|
||||
|
||||
// 提示框
|
||||
tooltip: {
|
||||
backgroundColor: 'rgba(50,50,50,0.5)', // 提示背景颜色,默认为透明度为0.7的黑色
|
||||
axisPointer : { // 坐标轴指示器,坐标轴触发有效
|
||||
type : 'line', // 默认为直线,可选为:'line' | 'shadow'
|
||||
lineStyle : { // 直线指示器样式设置
|
||||
color: '#16baaa'
|
||||
},
|
||||
crossStyle: {
|
||||
color: '#008acd'
|
||||
},
|
||||
shadowStyle : { // 阴影指示器样式设置
|
||||
color: 'rgba(200,200,200,0.2)'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 区域缩放控制器
|
||||
dataZoom: {
|
||||
dataBackgroundColor: '#efefff', // 数据背景颜色
|
||||
fillerColor: 'rgba(182,162,222,0.2)', // 填充颜色
|
||||
handleColor: '#008acd' // 手柄颜色
|
||||
},
|
||||
|
||||
// 网格
|
||||
grid: {
|
||||
borderColor: '#eee'
|
||||
},
|
||||
|
||||
// 类目轴 - X轴
|
||||
categoryAxis: {
|
||||
axisLine: { // 坐标轴线
|
||||
lineStyle: { // 属性lineStyle控制线条样式
|
||||
color: '#16baaa'
|
||||
}
|
||||
},
|
||||
axisTick: { //小标记
|
||||
show: false
|
||||
},
|
||||
splitLine: { // 分隔线
|
||||
lineStyle: { // 属性lineStyle(详见lineStyle)控制线条样式
|
||||
color: ['#eee']
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 数值型坐标轴默认参数 - Y轴
|
||||
valueAxis: {
|
||||
axisLine: { // 坐标轴线
|
||||
lineStyle: { // 属性lineStyle控制线条样式
|
||||
color: '#16baaa'
|
||||
}
|
||||
},
|
||||
splitArea : {
|
||||
show : true,
|
||||
areaStyle : {
|
||||
color: ['rgba(250,250,250,0.1)','rgba(200,200,200,0.1)']
|
||||
}
|
||||
},
|
||||
splitLine: { // 分隔线
|
||||
lineStyle: { // 属性lineStyle(详见lineStyle)控制线条样式
|
||||
color: ['#eee']
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
polar : {
|
||||
axisLine: { // 坐标轴线
|
||||
lineStyle: { // 属性lineStyle控制线条样式
|
||||
color: '#ddd'
|
||||
}
|
||||
},
|
||||
splitArea : {
|
||||
show : true,
|
||||
areaStyle : {
|
||||
color: ['rgba(250,250,250,0.2)','rgba(200,200,200,0.2)']
|
||||
}
|
||||
},
|
||||
splitLine : {
|
||||
lineStyle : {
|
||||
color : '#ddd'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
timeline : {
|
||||
lineStyle : {
|
||||
color : '#16baaa'
|
||||
},
|
||||
controlStyle : {
|
||||
normal : { color : '#16baaa'},
|
||||
emphasis : { color : '#16baaa'}
|
||||
},
|
||||
symbol : 'emptyCircle',
|
||||
symbolSize : 3
|
||||
},
|
||||
|
||||
// 柱形图默认参数
|
||||
bar: {
|
||||
itemStyle: {
|
||||
normal: {
|
||||
barBorderRadius: 2
|
||||
},
|
||||
emphasis: {
|
||||
barBorderRadius: 2
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 折线图默认参数
|
||||
line: {
|
||||
smooth : true,
|
||||
symbol: 'emptyCircle', // 拐点图形类型
|
||||
symbolSize: 3 // 拐点图形大小
|
||||
},
|
||||
|
||||
// K线图默认参数
|
||||
k: {
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: '#d87a80', // 阳线填充颜色
|
||||
color0: '#2ec7c9', // 阴线填充颜色
|
||||
lineStyle: {
|
||||
color: '#d87a80', // 阳线边框颜色
|
||||
color0: '#2ec7c9' // 阴线边框颜色
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 散点图默认参数
|
||||
scatter: {
|
||||
symbol: 'circle', // 图形类型
|
||||
symbolSize: 4 // 图形大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2
|
||||
},
|
||||
|
||||
// 雷达图默认参数
|
||||
radar : {
|
||||
symbol: 'emptyCircle', // 图形类型
|
||||
symbolSize:3
|
||||
//symbol: null, // 拐点图形类型
|
||||
//symbolRotate : null, // 图形旋转控制
|
||||
},
|
||||
|
||||
map: {
|
||||
itemStyle: {
|
||||
normal: {
|
||||
areaStyle: {
|
||||
color: '#ddd'
|
||||
},
|
||||
label: {
|
||||
textStyle: {
|
||||
color: '#d87a80'
|
||||
}
|
||||
}
|
||||
},
|
||||
emphasis: { // 也是选中样式
|
||||
areaStyle: {
|
||||
color: '#fe994e'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
force : {
|
||||
itemStyle: {
|
||||
normal: {
|
||||
linkStyle : {
|
||||
color : '#1e90ff'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
chord : {
|
||||
itemStyle : {
|
||||
normal : {
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(128, 128, 128, 0.5)',
|
||||
chordStyle : {
|
||||
lineStyle : {
|
||||
color : 'rgba(128, 128, 128, 0.5)'
|
||||
}
|
||||
}
|
||||
},
|
||||
emphasis : {
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(128, 128, 128, 0.5)',
|
||||
chordStyle : {
|
||||
lineStyle : {
|
||||
color : 'rgba(128, 128, 128, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
gauge : {
|
||||
axisLine: { // 坐标轴线
|
||||
lineStyle: { // 属性lineStyle控制线条样式
|
||||
color: [[0.2, '#2ec7c9'],[0.8, '#5ab1ef'],[1, '#d87a80']],
|
||||
width: 10
|
||||
}
|
||||
},
|
||||
axisTick: { // 坐标轴小标记
|
||||
splitNumber: 10, // 每份split细分多少段
|
||||
length :15, // 属性length控制线长
|
||||
lineStyle: { // 属性lineStyle控制线条样式
|
||||
color: 'auto'
|
||||
}
|
||||
},
|
||||
splitLine: { // 分隔线
|
||||
length :22, // 属性length控制线长
|
||||
lineStyle: { // 属性lineStyle(详见lineStyle)控制线条样式
|
||||
color: 'auto'
|
||||
}
|
||||
},
|
||||
pointer : {
|
||||
width : 5
|
||||
}
|
||||
},
|
||||
|
||||
textStyle: {
|
||||
fontFamily: '微软雅黑, Arial, Verdana, sans-serif'
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,422 @@
|
||||
// Layui 轻量级请求模块(基于 Fetch API + layui layer)
|
||||
// 支持文件上传
|
||||
//
|
||||
// 使用示例:
|
||||
// layui.use(['http'], function(){ var req = layui.request; req.post('/api/upload', formData).then(...).catch(...) });
|
||||
|
||||
layui.define(["layer", "setter"], function (exports) {
|
||||
"use strict";
|
||||
|
||||
var setter = layui.setter,
|
||||
layer = layui.layer;
|
||||
|
||||
let isRefreshing = false;
|
||||
let refreshQueue = [];
|
||||
let loadIndex;
|
||||
|
||||
// 默认配置
|
||||
const defaultConfig = {
|
||||
baseURL: '',
|
||||
timeout: 30000,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
},
|
||||
withCredentials: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* 刷新 Tokens
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
async function refreshToken() {
|
||||
const { refresh_token } = setter.getTokens();
|
||||
if (!refresh_token) {
|
||||
throw new Error("No refresh token");
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), defaultConfig.timeout);
|
||||
|
||||
try {
|
||||
var Accept = defaultConfig.headers['Accept'] || 'application/json';
|
||||
const response = await fetch(defaultConfig.baseURL + "/api/refresh", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': Accept,
|
||||
...defaultConfig.headers,
|
||||
},
|
||||
credentials: defaultConfig.withCredentials ? 'include' : 'same-origin',
|
||||
signal: controller.signal,
|
||||
body: JSON.stringify({ refresh_token }),
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const res = await response.json();
|
||||
|
||||
if (res.code === 200) {
|
||||
setter.saveTokens({
|
||||
access_token: res.access || res.access_token,
|
||||
refresh_token: res.refresh || res.refresh_token || refresh_token,
|
||||
});
|
||||
return res.access || res.access_token;
|
||||
} else {
|
||||
throw new Error(res.msg || "Refresh failed");
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') {
|
||||
throw new Error('Request timeout');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重试请求
|
||||
* @param {Object} options - fetch 配置选项
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function retryRequest(options) {
|
||||
const { access_token } = setter.getTokens();
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), options.timeout || defaultConfig.timeout);
|
||||
|
||||
try {
|
||||
// 构建请求头
|
||||
const headers = { ...defaultConfig.headers, ...options.headers };
|
||||
const { access_token: newToken } = setter.getTokens();
|
||||
if (newToken) {
|
||||
headers['Authorization'] = `Bearer ${newToken}`;
|
||||
}
|
||||
|
||||
// 如果是 FormData,删除 Content-Type 让浏览器自动设置
|
||||
if (options.data instanceof FormData) {
|
||||
delete headers['Content-Type'];
|
||||
}
|
||||
|
||||
const fetchOptions = {
|
||||
method: options.method || 'GET',
|
||||
headers,
|
||||
credentials: defaultConfig.withCredentials ? 'include' : 'same-origin',
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
// 添加 body(如果不是 GET/HEAD 请求)
|
||||
if (options.method !== 'GET' && options.method !== 'HEAD') {
|
||||
fetchOptions.body = options.data instanceof FormData
|
||||
? options.data
|
||||
: JSON.stringify(options.data);
|
||||
}
|
||||
|
||||
const response = await fetch(options.url, fetchOptions);
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
// 处理响应头中的 token
|
||||
const authHeader = response.headers.get('Authorization');
|
||||
if (authHeader && authHeader.startsWith('Bearer ')) {
|
||||
const newToken = authHeader.substring(7);
|
||||
setter.saveTokens({ access_token: newToken });
|
||||
}
|
||||
|
||||
let data;
|
||||
const contentType = response.headers.get('content-type');
|
||||
|
||||
if (contentType && contentType.includes('application/json')) {
|
||||
data = await response.json();
|
||||
} else if (contentType && contentType.includes('text/')) {
|
||||
data = await response.text();
|
||||
} else {
|
||||
data = await response.blob();
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const error = new Error(response.statusText || 'Request failed');
|
||||
error.status = response.status;
|
||||
error.data = data;
|
||||
throw error;
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') {
|
||||
error.message = 'Request timeout';
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch 请求核心方法
|
||||
* @param {Object} options - fetch 配置选项
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async function _Request(options) {
|
||||
const { access_token } = setter.getTokens();
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), options.timeout || defaultConfig.timeout);
|
||||
|
||||
try {
|
||||
// 构建请求头
|
||||
const headers = { ...defaultConfig.headers, ...options.headers };
|
||||
if (access_token) {
|
||||
headers['Authorization'] = `Bearer ${access_token}`;
|
||||
}
|
||||
|
||||
// 如果是 FormData,删除 Content-Type 让浏览器自动设置
|
||||
if (options.data instanceof FormData) {
|
||||
delete headers['Content-Type'];
|
||||
}
|
||||
|
||||
const fetchOptions = {
|
||||
method: options.method || 'GET',
|
||||
headers,
|
||||
credentials: defaultConfig.withCredentials ? 'include' : 'same-origin',
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
// 添加 body(如果不是 GET/HEAD 请求)
|
||||
if (options.method !== 'GET' && options.method !== 'HEAD') {
|
||||
fetchOptions.body = options.data instanceof FormData
|
||||
? options.data
|
||||
: JSON.stringify(options.data);
|
||||
}
|
||||
|
||||
const response = await fetch(options.url, fetchOptions);
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
// 处理响应头中的 token
|
||||
const authHeader = response.headers.get('Authorization');
|
||||
if (authHeader && authHeader.startsWith('Bearer ')) {
|
||||
const newToken = authHeader.substring(7);
|
||||
setter.saveTokens({ access_token: newToken });
|
||||
}
|
||||
|
||||
let data;
|
||||
const contentType = response.headers.get('content-type');
|
||||
|
||||
if (contentType && contentType.includes('application/json')) {
|
||||
data = await response.json();
|
||||
} else if (contentType && contentType.includes('text/')) {
|
||||
data = await response.text();
|
||||
} else {
|
||||
data = await response.blob();
|
||||
}
|
||||
|
||||
// 检查响应数据中的 token
|
||||
if (data && (data.access_token || data.access)) {
|
||||
setter.saveTokens({
|
||||
access_token: data.access_token || data.access,
|
||||
refresh_token: data.refresh_token || data.refresh,
|
||||
});
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401 && !options._retry) {
|
||||
// 401 错误,尝试刷新 token
|
||||
return new Promise((resolve, reject) => {
|
||||
refreshQueue.push({ resolve, reject, options });
|
||||
|
||||
if (!isRefreshing) {
|
||||
isRefreshing = true;
|
||||
|
||||
refreshToken()
|
||||
.then(() => {
|
||||
const queue = [...refreshQueue];
|
||||
refreshQueue = [];
|
||||
|
||||
queue.forEach(({ resolve, reject, options }) => {
|
||||
options._retry = true;
|
||||
retryRequest(options)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
|
||||
isRefreshing = false;
|
||||
})
|
||||
.catch((refreshError) => {
|
||||
refreshQueue.forEach(({ reject }) => reject(refreshError));
|
||||
refreshQueue = [];
|
||||
isRefreshing = false;
|
||||
|
||||
// 清除本地存储并跳转登录
|
||||
localStorage.clear();
|
||||
layer.msg("登录已过期,请重新登录", { icon: 2, time: 2000 });
|
||||
setTimeout(() => {
|
||||
window.top.location.href = "./login.html";
|
||||
// if (window === window.parent) {
|
||||
// window.location.href = "./login.html";
|
||||
// } else {
|
||||
// window.parent.location.href = "./login.html";
|
||||
// }
|
||||
}, 2000);
|
||||
|
||||
reject(refreshError);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const error = new Error(response.statusText || 'Request failed');
|
||||
error.status = response.status;
|
||||
error.data = data;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') {
|
||||
error.message = 'Request timeout';
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
async function setAccept(accept = "json") {
|
||||
switch (accept) {
|
||||
case 'xml':
|
||||
defaultConfig.headers['Accept'] = `application/xml`;
|
||||
break;
|
||||
case 'html':
|
||||
defaultConfig.headers['Accept'] = `text/html`;
|
||||
break;
|
||||
default:
|
||||
defaultConfig.headers['Accept'] = `application/json`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 暴露接口
|
||||
var http = {
|
||||
/**
|
||||
* 配置请求默认参数
|
||||
* @param {Object} cfg - 配置对象
|
||||
* @returns {Object} 当前配置
|
||||
*/
|
||||
config: function (cfg) {
|
||||
Object.assign(defaultConfig, cfg || {});
|
||||
return defaultConfig;
|
||||
},
|
||||
|
||||
/**
|
||||
* 通用请求方法
|
||||
* @param {Object} options - fetch 配置选项
|
||||
* @returns {Promise}
|
||||
*/
|
||||
request: _Request,
|
||||
|
||||
/**
|
||||
* GET 请求
|
||||
* @param {string} url - 请求地址
|
||||
* @param {Object} params - URL 参数
|
||||
* @param {Object} options - fetch 配置选项
|
||||
* @returns {Promise}
|
||||
*/
|
||||
get: function (url, params, options = {}) {
|
||||
// 处理 URL 参数
|
||||
if (params && Object.keys(params).length > 0) {
|
||||
const queryString = new URLSearchParams(params).toString();
|
||||
url += (url.includes('?') ? '&' : '?') + queryString;
|
||||
}
|
||||
|
||||
options.method = "GET";
|
||||
options.url = defaultConfig.baseURL + url;
|
||||
return _Request(options);
|
||||
},
|
||||
|
||||
/**
|
||||
* POST 请求
|
||||
* @param {string} url - 请求地址
|
||||
* @param {Object|FormData} data - 请求数据或 FormData 对象
|
||||
* @param {Object} options - fetch 配置选项
|
||||
* @returns {Promise}
|
||||
*/
|
||||
post: function (url, data, options = {}) {
|
||||
options.method = "POST";
|
||||
options.url = defaultConfig.baseURL + url;
|
||||
options.data = data;
|
||||
return _Request(options);
|
||||
},
|
||||
|
||||
/**
|
||||
* PUT 请求
|
||||
* @param {string} url - 请求地址
|
||||
* @param {Object|FormData} data - 请求数据或 FormData 对象
|
||||
* @param {Object} options - fetch 配置选项
|
||||
* @returns {Promise}
|
||||
*/
|
||||
put: function (url, data, options = {}) {
|
||||
options.method = "PUT";
|
||||
options.url = defaultConfig.baseURL + url;
|
||||
options.data = data;
|
||||
return _Request(options);
|
||||
},
|
||||
|
||||
/**
|
||||
* DELETE 请求
|
||||
* @param {string} url - 请求地址
|
||||
* @param {Object} params - URL 参数
|
||||
* @param {Object} options - fetch 配置选项
|
||||
* @returns {Promise}
|
||||
*/
|
||||
delete: function (url, params, options = {}) {
|
||||
// 处理 URL 参数
|
||||
if (params && Object.keys(params).length > 0) {
|
||||
const queryString = new URLSearchParams(params).toString();
|
||||
url += (url.includes('?') ? '&' : '?') + queryString;
|
||||
}
|
||||
|
||||
options.method = "DELETE";
|
||||
options.url = defaultConfig.baseURL + url;
|
||||
return _Request(options);
|
||||
},
|
||||
|
||||
/**
|
||||
* 上传文件(简化版)
|
||||
* @param {string} url - 上传地址
|
||||
* @param {File|File[]} files - 文件对象或文件数组
|
||||
* @param {Object} params - 其他参数
|
||||
* @param {Object} options - fetch 配置选项
|
||||
* @returns {Promise}
|
||||
*/
|
||||
upload: function (url, files, params = {}, options = {}) {
|
||||
const formData = new FormData();
|
||||
|
||||
// 添加文件
|
||||
if (Array.isArray(files)) {
|
||||
files.forEach((file, index) => {
|
||||
formData.append(`file${index}`, file);
|
||||
});
|
||||
} else if (files) {
|
||||
formData.append('file', files);
|
||||
}
|
||||
|
||||
// 添加其他参数
|
||||
Object.keys(params).forEach(key => {
|
||||
formData.append(key, params[key]);
|
||||
});
|
||||
|
||||
options.method = "POST";
|
||||
options.url = defaultConfig.baseURL + url;
|
||||
options.data = formData;
|
||||
return _Request(options);
|
||||
},
|
||||
/**
|
||||
* 刷新 token
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
refreshToken: refreshToken,
|
||||
|
||||
accept: setAccept,
|
||||
|
||||
};
|
||||
|
||||
exports("http", http);
|
||||
});
|
||||
@@ -0,0 +1,400 @@
|
||||
/**
|
||||
* Layui图标选择器
|
||||
* @author wujiawei0926@yeah.net
|
||||
* @version 1.1
|
||||
*/
|
||||
|
||||
layui.define(['laypage', 'form'], function (exports) {
|
||||
"use strict";
|
||||
|
||||
var IconPicker =function () {
|
||||
this.v = '1.1';
|
||||
}, _MOD = 'iconPicker',
|
||||
_this = this,
|
||||
$ = layui.jquery,
|
||||
laypage = layui.laypage,
|
||||
form = layui.form,
|
||||
BODY = 'body',
|
||||
TIPS = '请选择图标';
|
||||
|
||||
/**
|
||||
* 渲染组件
|
||||
*/
|
||||
IconPicker.prototype.render = function(options){
|
||||
var opts = options,
|
||||
// DOM选择器
|
||||
elem = opts.elem,
|
||||
// 数据类型:fontClass/unicode
|
||||
type = opts.type == null ? 'fontClass' : opts.type,
|
||||
// 是否分页:true/false
|
||||
page = opts.page == null ? true : opts.page,
|
||||
// 每页显示数量
|
||||
limit = opts.limit == null ? 12 : opts.limit,
|
||||
// 是否开启搜索:true/false
|
||||
search = opts.search == null ? true : opts.search,
|
||||
// 每个图标格子的宽度:'43px'或'20%'
|
||||
cellWidth = opts.cellWidth,
|
||||
// 点击回调
|
||||
click = opts.click,
|
||||
// 渲染成功后的回调
|
||||
success = opts.success,
|
||||
// json数据
|
||||
data = {},
|
||||
// 唯一标识
|
||||
tmp = new Date().getTime(),
|
||||
// 是否使用的class数据
|
||||
isFontClass = opts.type === 'fontClass',
|
||||
// 初始化时input的值
|
||||
ORIGINAL_ELEM_VALUE = $(elem).val(),
|
||||
TITLE = 'layui-select-title',
|
||||
TITLE_ID = 'layui-select-title-' + tmp,
|
||||
ICON_BODY = 'layui-iconpicker-' + tmp,
|
||||
PICKER_BODY = 'layui-iconpicker-body-' + tmp,
|
||||
PAGE_ID = 'layui-iconpicker-page-' + tmp,
|
||||
LIST_BOX = 'layui-iconpicker-list-box',
|
||||
selected = 'layui-form-selected',
|
||||
unselect = 'layui-unselect';
|
||||
|
||||
var a = {
|
||||
init: function () {
|
||||
data = common.getData[type]();
|
||||
|
||||
a.hideElem().createSelect().createBody().toggleSelect();
|
||||
a.preventEvent().inputListen();
|
||||
common.loadCss();
|
||||
|
||||
if (success) {
|
||||
success(this.successHandle());
|
||||
}
|
||||
|
||||
return a;
|
||||
},
|
||||
successHandle: function(){
|
||||
var d = {
|
||||
options: opts,
|
||||
data: data,
|
||||
id: tmp,
|
||||
elem: $('#' + ICON_BODY)
|
||||
};
|
||||
return d;
|
||||
},
|
||||
/**
|
||||
* 隐藏elem
|
||||
*/
|
||||
hideElem: function () {
|
||||
$(elem).hide();
|
||||
return a;
|
||||
},
|
||||
/**
|
||||
* 绘制select下拉选择框
|
||||
*/
|
||||
createSelect: function () {
|
||||
var oriIcon = '<i class="layui-icon">';
|
||||
|
||||
// 默认图标
|
||||
if(ORIGINAL_ELEM_VALUE === '') {
|
||||
if(isFontClass) {
|
||||
ORIGINAL_ELEM_VALUE = 'layui-icon-circle-dot';
|
||||
} else {
|
||||
ORIGINAL_ELEM_VALUE = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (isFontClass) {
|
||||
oriIcon = '<i class="layui-icon '+ ORIGINAL_ELEM_VALUE +'">';
|
||||
} else {
|
||||
oriIcon += ORIGINAL_ELEM_VALUE;
|
||||
}
|
||||
oriIcon += '</i>';
|
||||
|
||||
var selectHtml = '<div class="layui-iconpicker layui-unselect layui-form-select" id="'+ ICON_BODY +'">' +
|
||||
'<div class="'+ TITLE +'" id="'+ TITLE_ID +'">' +
|
||||
'<div class="layui-iconpicker-item">'+
|
||||
'<span class="layui-iconpicker-icon layui-unselect">' +
|
||||
oriIcon +
|
||||
'</span>'+
|
||||
'<i class="layui-edge"></i>' +
|
||||
'</div>'+
|
||||
'</div>' +
|
||||
'<div class="layui-anim layui-anim-upbit" style="">' +
|
||||
'123' +
|
||||
'</div>';
|
||||
$(elem).after(selectHtml);
|
||||
return a;
|
||||
},
|
||||
/**
|
||||
* 展开/折叠下拉框
|
||||
*/
|
||||
toggleSelect: function () {
|
||||
var item = '#' + TITLE_ID + ' .layui-iconpicker-item,#' + TITLE_ID + ' .layui-iconpicker-item .layui-edge';
|
||||
a.event('click', item, function (e) {
|
||||
var $icon = $('#' + ICON_BODY);
|
||||
if ($icon.hasClass(selected)) {
|
||||
$icon.removeClass(selected).addClass(unselect);
|
||||
} else {
|
||||
// 隐藏其他picker
|
||||
$('.layui-form-select').removeClass(selected);
|
||||
// 显示当前picker
|
||||
$icon.addClass(selected).removeClass(unselect);
|
||||
}
|
||||
e.stopPropagation();
|
||||
});
|
||||
return a;
|
||||
},
|
||||
/**
|
||||
* 绘制主体部分
|
||||
*/
|
||||
createBody: function () {
|
||||
// 获取数据
|
||||
var searchHtml = '';
|
||||
|
||||
if (search) {
|
||||
searchHtml = '<div class="layui-iconpicker-search">' +
|
||||
'<input class="layui-input">' +
|
||||
'<i class="layui-icon"></i>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
// 组合dom
|
||||
var bodyHtml = '<div class="layui-iconpicker-body" id="'+ PICKER_BODY +'">' +
|
||||
searchHtml +
|
||||
'<div class="'+ LIST_BOX +'"></div> '+
|
||||
'</div>';
|
||||
$('#' + ICON_BODY).find('.layui-anim').eq(0).html(bodyHtml);
|
||||
a.search().createList().check().page();
|
||||
|
||||
return a;
|
||||
},
|
||||
/**
|
||||
* 绘制图标列表
|
||||
* @param text 模糊查询关键字
|
||||
* @returns {string}
|
||||
*/
|
||||
createList: function (text) {
|
||||
var d = data,
|
||||
l = d.length,
|
||||
pageHtml = '',
|
||||
listHtml = $('<div class="layui-iconpicker-list">')//'<div class="layui-iconpicker-list">';
|
||||
|
||||
// 计算分页数据
|
||||
var _limit = limit, // 每页显示数量
|
||||
_pages = l % _limit === 0 ? l / _limit : parseInt(l / _limit + 1), // 总计多少页
|
||||
_id = PAGE_ID;
|
||||
|
||||
// 图标列表
|
||||
var icons = [];
|
||||
|
||||
for (var i = 0; i < l; i++) {
|
||||
var obj = d[i];
|
||||
|
||||
// 判断是否模糊查询
|
||||
if (text && obj.indexOf(text) === -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 是否自定义格子宽度
|
||||
var style = '';
|
||||
if (cellWidth !== null) {
|
||||
style += ' style="width:' + cellWidth + '"';
|
||||
}
|
||||
|
||||
// 每个图标dom
|
||||
var icon = '<div class="layui-iconpicker-icon-item" title="'+ obj +'" '+ style +'>';
|
||||
if (isFontClass){
|
||||
icon += '<i class="layui-icon '+ obj +'"></i>';
|
||||
} else {
|
||||
icon += '<i class="layui-icon">'+ obj.replace('amp;', '') +'</i>';
|
||||
}
|
||||
icon += '</div>';
|
||||
|
||||
icons.push(icon);
|
||||
}
|
||||
|
||||
// 查询出图标后再分页
|
||||
l = icons.length;
|
||||
_pages = l % _limit === 0 ? l / _limit : parseInt(l / _limit + 1);
|
||||
for (var i = 0; i < _pages; i++) {
|
||||
// 按limit分块
|
||||
var lm = $('<div class="layui-iconpicker-icon-limit" id="layui-iconpicker-icon-limit-' + tmp + (i+1) +'">');
|
||||
|
||||
for (var j = i * _limit; j < (i+1) * _limit && j < l; j++) {
|
||||
lm.append(icons[j]);
|
||||
}
|
||||
|
||||
listHtml.append(lm);
|
||||
}
|
||||
|
||||
// 无数据
|
||||
if (l === 0) {
|
||||
listHtml.append('<p class="layui-iconpicker-tips">无数据</p>');
|
||||
}
|
||||
|
||||
// 判断是否分页
|
||||
if (page){
|
||||
$('#' + PICKER_BODY).addClass('layui-iconpicker-body-page');
|
||||
pageHtml = '<div class="layui-iconpicker-page" id="'+ PAGE_ID +'">' +
|
||||
'<div class="layui-iconpicker-page-count">' +
|
||||
'<span id="'+ PAGE_ID +'-current">1</span>/' +
|
||||
'<span id="'+ PAGE_ID +'-pages">'+ _pages +'</span>' +
|
||||
' (<span id="'+ PAGE_ID +'-length">'+ l +'</span>)' +
|
||||
'</div>' +
|
||||
'<div class="layui-iconpicker-page-operate">' +
|
||||
'<i class="layui-icon" id="'+ PAGE_ID +'-prev" data-index="0" prev></i> ' +
|
||||
'<i class="layui-icon" id="'+ PAGE_ID +'-next" data-index="2" next></i> ' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
|
||||
$('#' + ICON_BODY).find('.layui-anim').find('.' + LIST_BOX).html('').append(listHtml).append(pageHtml);
|
||||
return a;
|
||||
},
|
||||
// 阻止Layui的一些默认事件
|
||||
preventEvent: function() {
|
||||
var item = '#' + ICON_BODY + ' .layui-anim';
|
||||
a.event('click', item, function (e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
return a;
|
||||
},
|
||||
// 分页
|
||||
page: function () {
|
||||
var icon = '#' + PAGE_ID + ' .layui-iconpicker-page-operate .layui-icon';
|
||||
|
||||
$(icon).unbind('click');
|
||||
a.event('click', icon, function (e) {
|
||||
var elem = e.currentTarget,
|
||||
total = parseInt($('#' +PAGE_ID + '-pages').html()),
|
||||
isPrev = $(elem).attr('prev') !== undefined,
|
||||
// 按钮上标的页码
|
||||
index = parseInt($(elem).attr('data-index')),
|
||||
$cur = $('#' +PAGE_ID + '-current'),
|
||||
// 点击时正在显示的页码
|
||||
current = parseInt($cur.html());
|
||||
|
||||
// 分页数据
|
||||
if (isPrev && current > 1) {
|
||||
current=current-1;
|
||||
$(icon + '[prev]').attr('data-index', current);
|
||||
} else if (!isPrev && current < total){
|
||||
current=current+1;
|
||||
$(icon + '[next]').attr('data-index', current);
|
||||
}
|
||||
$cur.html(current);
|
||||
|
||||
// 图标数据
|
||||
$('#'+ ICON_BODY + ' .layui-iconpicker-icon-limit').hide();
|
||||
$('#layui-iconpicker-icon-limit-' + tmp + current).show();
|
||||
e.stopPropagation();
|
||||
});
|
||||
return a;
|
||||
},
|
||||
/**
|
||||
* 搜索
|
||||
*/
|
||||
search: function () {
|
||||
var item = '#' + PICKER_BODY + ' .layui-iconpicker-search .layui-input';
|
||||
a.event('input propertychange', item, function (e) {
|
||||
var elem = e.target,
|
||||
t = $(elem).val();
|
||||
a.createList(t);
|
||||
});
|
||||
return a;
|
||||
},
|
||||
/**
|
||||
* 点击选中图标
|
||||
*/
|
||||
check: function () {
|
||||
var item = '#' + PICKER_BODY + ' .layui-iconpicker-icon-item';
|
||||
a.event('click', item, function (e) {
|
||||
var el = $(e.currentTarget).find('.layui-icon'),
|
||||
icon = '';
|
||||
if (isFontClass) {
|
||||
var clsArr = el.attr('class').split(/[\s\n]/),
|
||||
cls = clsArr[1],
|
||||
icon = cls;
|
||||
$('#' + TITLE_ID).find('.layui-iconpicker-item .layui-icon').html('').attr('class', clsArr.join(' '));
|
||||
} else {
|
||||
var cls = el.html(),
|
||||
icon = cls;
|
||||
$('#' + TITLE_ID).find('.layui-iconpicker-item .layui-icon').html(icon);
|
||||
}
|
||||
|
||||
$('#' + ICON_BODY).removeClass(selected).addClass(unselect);
|
||||
$(elem).val(icon).attr('value', icon);
|
||||
// 回调
|
||||
if (click) {
|
||||
click({
|
||||
icon: icon
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
return a;
|
||||
},
|
||||
// 监听原始input数值改变
|
||||
inputListen: function(){
|
||||
var el = $(elem);
|
||||
a.event('change', elem, function(){
|
||||
var value = el.val();
|
||||
})
|
||||
// el.change(function(){
|
||||
|
||||
// });
|
||||
return a;
|
||||
},
|
||||
event: function (evt, el, fn) {
|
||||
$(BODY).on(evt, el, fn);
|
||||
}
|
||||
};
|
||||
|
||||
var common = {
|
||||
/**
|
||||
* 加载样式表
|
||||
*/
|
||||
loadCss: function () {
|
||||
var css = '.layui-iconpicker {max-width: 280px;}.layui-iconpicker .layui-anim{display:none;position:absolute;left:0;top:42px;padding:5px 0;z-index:899;min-width:100%;border:1px solid #d2d2d2;max-height:300px;overflow-y:auto;background-color:#fff;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.12);box-sizing:border-box;}.layui-iconpicker-item{border:1px solid #e6e6e6;width:90px;height:38px;border-radius:4px;cursor:pointer;position:relative;}.layui-iconpicker-icon{border-right:1px solid #e6e6e6;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;width:60px;height:100%;float:left;text-align:center;background:#fff;transition:all .3s;}.layui-iconpicker-icon i{line-height:38px;font-size:18px;}.layui-iconpicker-item > .layui-edge{left:70px;}.layui-iconpicker-item:hover{border-color:#D2D2D2!important;}.layui-iconpicker-item:hover .layui-iconpicker-icon{border-color:#D2D2D2!important;}.layui-iconpicker.layui-form-selected .layui-anim{display:block;}.layui-iconpicker-body{padding:6px;}.layui-iconpicker .layui-iconpicker-list{background-color:#fff;border:1px solid #ccc;border-radius:4px;}.layui-iconpicker .layui-iconpicker-icon-item{display:inline-block;width:21.1%;line-height:36px;text-align:center;cursor:pointer;vertical-align:top;height:36px;margin:4px;border:1px solid #ddd;border-radius:2px;transition:300ms;}.layui-iconpicker .layui-iconpicker-icon-item i.layui-icon{font-size:17px;}.layui-iconpicker .layui-iconpicker-icon-item:hover{background-color:#eee;border-color:#ccc;-webkit-box-shadow:0 0 2px #aaa,0 0 2px #fff inset;-moz-box-shadow:0 0 2px #aaa,0 0 2px #fff inset;box-shadow:0 0 2px #aaa,0 0 2px #fff inset;text-shadow:0 0 1px #fff;}.layui-iconpicker-search{position:relative;margin:0 0 6px 0;border:1px solid #e6e6e6;border-radius:2px;transition:300ms;}.layui-iconpicker-search:hover{border-color:#D2D2D2!important;}.layui-iconpicker-search .layui-input{cursor:text;display:inline-block;width:86%;border:none;padding-right:0;margin-top:1px;}.layui-iconpicker-search .layui-icon{position:absolute;top:11px;right:4%;}.layui-iconpicker-tips{text-align:center;padding:8px 0;cursor:not-allowed;}.layui-iconpicker-page{margin-top:6px;margin-bottom:-6px;font-size:12px;padding:0 2px;}.layui-iconpicker-page-count{display:inline-block;}.layui-iconpicker-page-operate{display:inline-block;float:right;cursor:default;}.layui-iconpicker-page-operate .layui-icon{font-size:12px;cursor:pointer;}.layui-iconpicker-body-page .layui-iconpicker-icon-limit{display:none;}.layui-iconpicker-body-page .layui-iconpicker-icon-limit:first-child{display:block;}';
|
||||
var $style = $('head').find('style[iconpicker]');
|
||||
if ($style.length === 0) {
|
||||
$('head').append('<style rel="stylesheet" iconpicker>'+css+'</style>');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 获取数据
|
||||
*/
|
||||
getData: {
|
||||
fontClass: function () {
|
||||
var arr = ["layui-icon-rate-half","layui-icon-rate","layui-icon-rate-solid","layui-icon-cellphone","layui-icon-vercode","layui-icon-login-wechat","layui-icon-login-qq","layui-icon-login-weibo","layui-icon-password","layui-icon-username","layui-icon-refresh-3","layui-icon-auz","layui-icon-spread-left","layui-icon-shrink-right","layui-icon-snowflake","layui-icon-tips","layui-icon-note","layui-icon-home","layui-icon-senior","layui-icon-refresh","layui-icon-refresh-1","layui-icon-flag","layui-icon-theme","layui-icon-notice","layui-icon-website","layui-icon-console","layui-icon-face-surprised","layui-icon-set","layui-icon-template-1","layui-icon-app","layui-icon-template","layui-icon-praise","layui-icon-tread","layui-icon-male","layui-icon-female","layui-icon-camera","layui-icon-camera-fill","layui-icon-more","layui-icon-more-vertical","layui-icon-rmb","layui-icon-dollar","layui-icon-diamond","layui-icon-fire","layui-icon-return","layui-icon-location","layui-icon-read","layui-icon-survey","layui-icon-face-smile","layui-icon-face-cry","layui-icon-cart-simple","layui-icon-cart","layui-icon-next","layui-icon-prev","layui-icon-upload-drag","layui-icon-upload","layui-icon-download-circle","layui-icon-component","layui-icon-file-b","layui-icon-user","layui-icon-find-fill","layui-icon-loading","layui-icon-loading-1","layui-icon-add-1","layui-icon-play","layui-icon-pause","layui-icon-headset","layui-icon-video","layui-icon-voice","layui-icon-speaker","layui-icon-fonts-del","layui-icon-fonts-code","layui-icon-fonts-html","layui-icon-fonts-strong","layui-icon-unlink","layui-icon-picture","layui-icon-link","layui-icon-face-smile-b","layui-icon-align-left","layui-icon-align-right","layui-icon-align-center","layui-icon-fonts-u","layui-icon-fonts-i","layui-icon-tabs","layui-icon-radio","layui-icon-circle","layui-icon-edit","layui-icon-share","layui-icon-delete","layui-icon-form","layui-icon-cellphone-fine","layui-icon-dialogue","layui-icon-fonts-clear","layui-icon-layer","layui-icon-date","layui-icon-water","layui-icon-code-circle","layui-icon-carousel","layui-icon-prev-circle","layui-icon-layouts","layui-icon-util","layui-icon-templeate-1","layui-icon-upload-circle","layui-icon-tree","layui-icon-table","layui-icon-chart","layui-icon-chart-screen","layui-icon-engine","layui-icon-triangle-d","layui-icon-triangle-r","layui-icon-file","layui-icon-set-sm","layui-icon-add-circle","layui-icon-404","layui-icon-about","layui-icon-up","layui-icon-down","layui-icon-left","layui-icon-right","layui-icon-circle-dot","layui-icon-search","layui-icon-set-fill","layui-icon-group","layui-icon-friends","layui-icon-reply-fill","layui-icon-menu-fill","layui-icon-log","layui-icon-picture-fine","layui-icon-face-smile-fine","layui-icon-list","layui-icon-release","layui-icon-ok","layui-icon-help","layui-icon-chat","layui-icon-top","layui-icon-star","layui-icon-star-fill","layui-icon-close-fill","layui-icon-close","layui-icon-ok-circle","layui-icon-add-circle-fine"];
|
||||
return arr;
|
||||
},
|
||||
unicode: function () {
|
||||
return ["&#xe6c9;","&#xe67b;","&#xe67a;","&#xe678;","&#xe679;","&#xe677;","&#xe676;","&#xe675;","&#xe673;","&#xe66f;","&#xe9aa;","&#xe672;","&#xe66b;","&#xe668;","&#xe6b1;","&#xe702;","&#xe66e;","&#xe68e;","&#xe674;","&#xe669;","&#xe666;","&#xe66c;","&#xe66a;","&#xe667;","&#xe7ae;","&#xe665;","&#xe664;","&#xe716;","&#xe656;","&#xe653;","&#xe663;","&#xe6c6;","&#xe6c5;","&#xe662;","&#xe661;","&#xe660;","&#xe65d;","&#xe65f;","&#xe671;","&#xe65e;","&#xe659;","&#xe735;","&#xe756;","&#xe65c;","&#xe715;","&#xe705;","&#xe6b2;","&#xe6af;","&#xe69c;","&#xe698;","&#xe657;","&#xe65b;","&#xe65a;","&#xe681;","&#xe67c;","&#xe601;","&#xe857;","&#xe655;","&#xe770;","&#xe670;","&#xe63d;","&#xe63e;","&#xe654;","&#xe652;","&#xe651;","&#xe6fc;","&#xe6ed;","&#xe688;","&#xe645;","&#xe64f;","&#xe64e;","&#xe64b;","&#xe62b;","&#xe64d;","&#xe64a;","&#xe64c;","&#xe650;","&#xe649;","&#xe648;","&#xe647;","&#xe646;","&#xe644;","&#xe62a;","&#xe643;","&#xe63f;","&#xe642;","&#xe641;","&#xe640;","&#xe63c;","&#xe63b;","&#xe63a;","&#xe639;","&#xe638;","&#xe637;","&#xe636;","&#xe635;","&#xe634;","&#xe633;","&#xe632;","&#xe631;","&#xe630;","&#xe62f;","&#xe62e;","&#xe62d;","&#xe62c;","&#xe629;","&#xe628;","&#xe625;","&#xe623;","&#xe621;","&#xe620;","&#xe61f;","&#xe61c;","&#xe60b;","&#xe619;","&#xe61a;","&#xe603;","&#xe602;","&#xe617;","&#xe615;","&#xe614;","&#xe613;","&#xe612;","&#xe611;","&#xe60f;","&#xe60e;","&#xe60d;","&#xe60c;","&#xe60a;","&#xe609;","&#xe605;","&#xe607;","&#xe606;","&#xe604;","&#xe600;","&#xe658;","&#x1007;","&#x1006;","&#x1005;","&#xe608;"];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
a.init();
|
||||
return new IconPicker();
|
||||
};
|
||||
|
||||
/**
|
||||
* 选中图标
|
||||
* @param filter lay-filter
|
||||
* @param iconName 图标名称,自动识别fontClass/unicode
|
||||
*/
|
||||
IconPicker.prototype.checkIcon = function (filter, iconName){
|
||||
var el = $('*[lay-filter='+ filter +']'),
|
||||
p = el.next().find('.layui-iconpicker-item .layui-icon'),
|
||||
c = iconName;
|
||||
|
||||
if (c.indexOf('#xe') > 0){
|
||||
p.html(c);
|
||||
} else {
|
||||
p.html('').attr('class', 'layui-icon ' + c);
|
||||
}
|
||||
el.attr('value', c).val(c);
|
||||
};
|
||||
|
||||
var iconPicker = new IconPicker();
|
||||
exports(_MOD, iconPicker);
|
||||
});
|
||||
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* forum demo
|
||||
*/
|
||||
|
||||
layui.define(['table', 'form'], function(exports){
|
||||
var $ = layui.$
|
||||
,table = layui.table
|
||||
,form = layui.form;
|
||||
|
||||
//帖子管理
|
||||
table.render({
|
||||
elem: '#LAY-app-forum-list'
|
||||
,url: layui.setter.paths.base + 'json/forum/list.js' //模拟接口
|
||||
,cols: [[
|
||||
{type: 'checkbox', fixed: 'left'}
|
||||
,{field: 'id', width: 100, title: 'ID', sort: true}
|
||||
,{field: 'poster', title: '发帖人'}
|
||||
,{field: 'avatar', title: '头像', width: 100, templet: '#imgTpl'}
|
||||
,{field: 'content', title: '发帖内容'}
|
||||
,{field: 'posttime', title: '发帖时间', sort: true}
|
||||
,{field: 'top', title: '置顶', templet: '#buttonTpl', minWidth: 80, align: 'center'}
|
||||
,{title: '操作', width: 150, align: 'center', fixed: 'right', toolbar: '#table-forum-list'}
|
||||
]]
|
||||
,page: true
|
||||
,limit: 10
|
||||
,limits: [10, 15, 20, 25, 30]
|
||||
,text: '加载出现异常'
|
||||
});
|
||||
|
||||
//事件-工具条
|
||||
table.on('tool(LAY-app-forum-list)', function(obj){
|
||||
var data = obj.data;
|
||||
if(obj.event === 'del'){
|
||||
layer.confirm('确定删除此条帖子?', function(index){
|
||||
obj.del();
|
||||
layer.close(index);
|
||||
});
|
||||
} else if(obj.event === 'edit'){
|
||||
var tr = $(obj.tr);
|
||||
|
||||
layer.open({
|
||||
type: 2
|
||||
,title: '编辑帖子'
|
||||
,content: '../../../views/app/forum/listform.html'
|
||||
,area: ['550px', '400px']
|
||||
,btn: ['确定', '取消']
|
||||
,resize: false
|
||||
,yes: function(index, layero){
|
||||
var iframeWindow = window['layui-layer-iframe'+ index]
|
||||
,submitID = 'LAY-app-forum-submit'
|
||||
,submit = layero.find('iframe').contents().find('#'+ submitID);
|
||||
|
||||
//事件-提交
|
||||
iframeWindow.layui.form.on('submit('+ submitID +')', function(data){
|
||||
var field = data.field; //获取提交的字段
|
||||
|
||||
//提交 Ajax 成功后,静态更新表格中的数据
|
||||
//$.ajax({});
|
||||
table.reload('LAY-app-forum-list'); //数据刷新
|
||||
layer.close(index); //关闭弹层
|
||||
});
|
||||
|
||||
submit.trigger('click');
|
||||
}
|
||||
,success: function(layero, index){
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//回帖管理
|
||||
table.render({
|
||||
elem: '#LAY-app-forumreply-list'
|
||||
,url: layui.setter.paths.base + 'json/forum/replys.js' //模拟接口
|
||||
,cols: [[
|
||||
{type: 'checkbox', fixed: 'left'}
|
||||
,{field: 'id', width: 100, title: 'ID', sort: true}
|
||||
,{field: 'replyer', title: '回帖人'}
|
||||
,{field: 'cardid', title: '回帖ID', sort: true}
|
||||
,{field: 'avatar', title: '头像', width: 100, templet: '#imgTpl'}
|
||||
,{field: 'content', title: '回帖内容', width: 200}
|
||||
,{field: 'replytime', title: '回帖时间', sort: true}
|
||||
,{title: '操作', width: 150, align: 'center', fixed: 'right', toolbar: '#table-forum-replys'}
|
||||
]]
|
||||
,page: true
|
||||
,limit: 10
|
||||
,limits: [10, 15, 20, 25, 30]
|
||||
,text: '对不起,加载出现异常!'
|
||||
});
|
||||
|
||||
//事件-工具条
|
||||
table.on('tool(LAY-app-forumreply-list)', function(obj){
|
||||
var data = obj.data;
|
||||
if(obj.event === 'del'){
|
||||
layer.confirm('确定删除此条评论?', function(index){
|
||||
obj.del();
|
||||
layer.close(index);
|
||||
});
|
||||
} else if(obj.event === 'edit'){
|
||||
var tr = $(obj.tr);
|
||||
|
||||
layer.open({
|
||||
type: 2
|
||||
,title: '编辑评论'
|
||||
,content: '../../../views/app/forum/replysform.html'
|
||||
,area: ['550px', '350px']
|
||||
,btn: ['确定', '取消']
|
||||
,resize: false
|
||||
,yes: function(index, layero){
|
||||
//获取iframe元素的值
|
||||
var othis = layero.find('iframe').contents().find("#layuiadmin-form-replys");
|
||||
var content = othis.find('textarea[name="content"]').val();
|
||||
|
||||
//数据更新
|
||||
obj.update({
|
||||
content: content
|
||||
});
|
||||
layer.close(index);
|
||||
}
|
||||
,success: function(layero, index){
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
exports('forum', {})
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 6.6 KiB |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* YwxApp 路由模块(ThinkPHP 风格 URL 生成)
|
||||
* ------------------------------------------------------------------
|
||||
* 通过后端模板注入的全局 layui.app 获取部署配置(root / module /
|
||||
* routeBase / controller / action / assetUrl),对外提供 path / asset /
|
||||
* api / route 四个方法,供任意 layui 模块以模块化方式调用:
|
||||
*
|
||||
* layui.use(['route'], function () {
|
||||
* var route = layui.route;
|
||||
* route.route('edit', 'user', null, { id: 1 }); // -> /admin/user/edit?id=1
|
||||
* route.asset('ywxapp/css/style.css');
|
||||
* });
|
||||
*
|
||||
* 同时本模块会把方法挂回 layui.app,保持对旧代码的向后兼容
|
||||
* (旧的 layui.app.route(...) 仍可正常工作)。
|
||||
*/
|
||||
layui.define(function (exports) {
|
||||
// 基础配置来自后端模板注入的全局 layui.app
|
||||
// 注意:必须在每次调用时实时读取 layui.app,不能在此处缓存。
|
||||
// 因为本模块(route.js)作为 layui 模块被异步加载,若此时 layui.app
|
||||
// 尚未被 layout.html 的同步脚本赋值,缓存到的就是空对象,导致后续
|
||||
// route() 生成的 URL 缺少应用前缀(如 /index/menu 而非 /admin/index/menu)。
|
||||
var getConf = function () {
|
||||
return (typeof layui !== 'undefined' && layui.app) || window.layui.app || {};
|
||||
};
|
||||
|
||||
var trim = function (s) {
|
||||
return String(s).replace(/(^\/+|\/+$)/g, '');
|
||||
};
|
||||
|
||||
/**
|
||||
* 页面路径拼接(ThinkPHP 风格:/模块/控制器/方法)
|
||||
* 控制器、模块参数均可选,模块缺省使用当前模块(即后端注入的虚拟名):
|
||||
* route.path('index') -> /<当前模块>/index
|
||||
* route.path('index', 'user') -> /<当前模块>/user/index
|
||||
* route.path('index', 'user', 'admin') -> /admin/user/index
|
||||
*/
|
||||
var path = function (method, controller, module) {
|
||||
var conf = getConf();
|
||||
var cfgRoot = conf.root || '/';
|
||||
var cfgModule = conf.module || '';
|
||||
var r = String(cfgRoot || '/').replace(/\/+$/, '');
|
||||
if (module === undefined || module === null || module === '') {
|
||||
module = cfgModule;
|
||||
}
|
||||
var parts = [];
|
||||
if (module) parts.push(trim(module));
|
||||
if (controller) parts.push(trim(controller));
|
||||
if (method) parts.push(trim(method));
|
||||
if (parts.length === 0) return r + '/';
|
||||
return r + '/' + parts.join('/');
|
||||
};
|
||||
|
||||
/**
|
||||
* 静态资源路径拼接
|
||||
* route.asset('ywxapp/css/style.css') -> /assets/ywxapp/css/style.css
|
||||
*/
|
||||
var asset = function (p) {
|
||||
var conf = getConf();
|
||||
var base = String(conf.assetUrl || '/assets').replace(/\/+$/, '');
|
||||
p = String(p || '').replace(/^\/+/, '');
|
||||
return base + '/' + p;
|
||||
};
|
||||
|
||||
/**
|
||||
* API 路径拼接,同 path()(控制器、模块均可选,模块缺省为当前模块)
|
||||
* route.api('save') -> /<当前模块>/save
|
||||
* route.api('save', 'user') -> /<当前模块>/user/save
|
||||
* route.api('save', 'user', 'admin')-> /admin/user/save
|
||||
*/
|
||||
var api = function (method, controller, module) {
|
||||
return path(method, controller, module);
|
||||
};
|
||||
|
||||
/**
|
||||
* 后端 Route::buildUrl() 的 JS 版:与后端路由规则完全一致
|
||||
* 签名同 path():route(方法, 控制器, 模块),控制器/模块可选(缺省用当前模块)。
|
||||
* 额外支持参数与锚点:route('edit','user', null, {id:5}, 'top')
|
||||
* -> /admin/user/edit?id=5#top
|
||||
*/
|
||||
var route = function (method, controller, module, vars, anchor) {
|
||||
// 实时读取后端注入的 layui.app(避免模块异步加载时缓存到空的 layui.app)
|
||||
var conf = getConf();
|
||||
var cfgRoot = conf.root || '/';
|
||||
var cfgModule = conf.module || '';
|
||||
var cfgRouteBase = conf.routeBase || '';
|
||||
var cfgController = conf.controller || '';
|
||||
var cfgAction = conf.action || '';
|
||||
// 基准 URL:优先用后端注入的 cfgRouteBase(形如 /admin/{__CTRL__}/{__ACT__})。
|
||||
// fallback(后端未注入时)基于 root+module 拼带占位符的合法基准,
|
||||
// 绝不再用 path('{__CTRL__}','{__ACT__}') —— 那会产出裸 /{__CTRL__}/{__ACT__}。
|
||||
var base;
|
||||
if (cfgRouteBase) {
|
||||
base = String(cfgRouteBase);
|
||||
// 防御:若后端注入的 routeBase 不含占位符(异常),补上后缀,避免漏替换
|
||||
if (base.indexOf('{__CTRL__}') === -1) {
|
||||
base = base.replace(/\/+$/, '') + '/{__CTRL__}/{__ACT__}';
|
||||
}
|
||||
} else {
|
||||
base = String(cfgRoot || '/').replace(/\/+$/, '')
|
||||
+ (cfgModule ? '/' + trim(cfgModule) : '')
|
||||
+ '/{__CTRL__}/{__ACT__}';
|
||||
}
|
||||
if (module === undefined || module === null || module === '') {
|
||||
module = cfgModule;
|
||||
}
|
||||
// 未显式传 controller 时,优先用后端注入的 cfgController,再兜底默认 'index'
|
||||
// (绝不再裸用字面量 '{__CTRL__}',否则会拼出 /{__CTRL__}/xxx 这类非法 URL)
|
||||
var ctrl = controller ? trim(controller) : (cfgController || 'index');
|
||||
var act = method ? trim(method) : (cfgAction || 'index');
|
||||
// 若显式传了 module,则替换掉基准里的应用前缀
|
||||
if (module) {
|
||||
base = base.replace(/\/[^\/]+\/\{__CTRL__\}/, '/' + trim(module) + '/{__CTRL__}');
|
||||
}
|
||||
var url = base
|
||||
.replace('{__CTRL__}', ctrl)
|
||||
.replace('{__ACT__}', act);
|
||||
// 拼装查询参数
|
||||
if (vars && typeof vars === 'object') {
|
||||
var q = [];
|
||||
for (var k in vars) {
|
||||
if (Object.prototype.hasOwnProperty.call(vars, k) && vars[k] !== '' && vars[k] !== null && vars[k] !== undefined) {
|
||||
q.push(encodeURIComponent(k) + '=' + encodeURIComponent(vars[k]));
|
||||
}
|
||||
}
|
||||
if (q.length) url += (url.indexOf('?') >= 0 ? '&' : '?') + q.join('&');
|
||||
}
|
||||
if (anchor) url += '#' + String(anchor).replace(/^#/, '');
|
||||
return url;
|
||||
};
|
||||
|
||||
var routeModule = {
|
||||
// 配置快照(只读,便于调试)—— 实时读取 layui.app
|
||||
config: function () {
|
||||
var c = getConf();
|
||||
return {
|
||||
root: c.root || '/',
|
||||
assetUrl: c.assetUrl || '/assets',
|
||||
module: c.module || '',
|
||||
routeBase: c.routeBase || '',
|
||||
controller: c.controller || '',
|
||||
action: c.action || ''
|
||||
};
|
||||
},
|
||||
path: path,
|
||||
asset: asset,
|
||||
api: api,
|
||||
route: route
|
||||
};
|
||||
|
||||
// 向后兼容:把方法挂回全局 layui.app,旧代码 layui.app.route() 继续可用
|
||||
if (layui.app) {
|
||||
layui.app.path = path;
|
||||
layui.app.asset = asset;
|
||||
layui.app.api = api;
|
||||
layui.app.route = route;
|
||||
layui.app.config = routeModule.config;
|
||||
}
|
||||
|
||||
exports('route', routeModule);
|
||||
});
|
||||
@@ -0,0 +1,360 @@
|
||||
layui.define(["jquery", "component", "http"], function (exports) {
|
||||
"use strict";
|
||||
|
||||
var $ = layui.$;
|
||||
// 创建UEditorPlus组件
|
||||
var ueditorPlus = layui.component({
|
||||
name: "ueditor",
|
||||
// 组件配置项
|
||||
config: {
|
||||
// 默认配置
|
||||
UEDITOR_HOME_URL: "/assets/library/ueditor/",
|
||||
serverUrl: "/ueditor", // 上传接口地址
|
||||
loadConfigFromServer: true,
|
||||
|
||||
toolbars: [
|
||||
[
|
||||
"fullscreen",
|
||||
"source",
|
||||
"|",
|
||||
"undo",
|
||||
"redo",
|
||||
"|",
|
||||
"bold",
|
||||
"italic",
|
||||
"underline",
|
||||
"fontborder",
|
||||
"strikethrough",
|
||||
"superscript",
|
||||
"subscript",
|
||||
"removeformat",
|
||||
"formatmatch",
|
||||
"autotypeset",
|
||||
"blockquote",
|
||||
"pasteplain",
|
||||
"|",
|
||||
"forecolor",
|
||||
"backcolor",
|
||||
"insertorderedlist",
|
||||
"insertunorderedlist",
|
||||
"selectall",
|
||||
"cleardoc",
|
||||
"|",
|
||||
"rowspacingtop",
|
||||
"rowspacingbottom",
|
||||
"lineheight",
|
||||
"letterspacing",
|
||||
"|",
|
||||
"customstyle",
|
||||
"paragraph",
|
||||
"fontfamily",
|
||||
"fontsize",
|
||||
"|",
|
||||
"directionalityltr",
|
||||
"directionalityrtl",
|
||||
"indent",
|
||||
"|",
|
||||
"justifyleft",
|
||||
"justifycenter",
|
||||
"justifyright",
|
||||
"justifyjustify",
|
||||
"|",
|
||||
"touppercase",
|
||||
"tolowercase",
|
||||
"|",
|
||||
"link",
|
||||
"unlink",
|
||||
"anchor",
|
||||
"|",
|
||||
"imagenone",
|
||||
"imageleft",
|
||||
"imagecenter",
|
||||
"imageright",
|
||||
"|",
|
||||
"simpleupload",
|
||||
"insertimage",
|
||||
"emotion",
|
||||
"scrawl",
|
||||
"insertvideo",
|
||||
"insertaudio",
|
||||
"attachment",
|
||||
"insertframe",
|
||||
"insertcode",
|
||||
"pagebreak",
|
||||
"template",
|
||||
"background",
|
||||
"formula",
|
||||
"|",
|
||||
"horizontal",
|
||||
"date",
|
||||
"time",
|
||||
// "spechars", // 特殊字符
|
||||
"wordimage",
|
||||
"|",
|
||||
"inserttable",
|
||||
"deletetable",
|
||||
"insertparagraphbeforetable",
|
||||
"insertrow",
|
||||
"deleterow",
|
||||
"insertcol",
|
||||
"deletecol",
|
||||
"mergecells",
|
||||
"mergeright",
|
||||
"mergedown",
|
||||
"splittocells",
|
||||
"splittorows",
|
||||
"splittocols",
|
||||
"|",
|
||||
"print",
|
||||
"preview",
|
||||
"searchreplace",
|
||||
"|",
|
||||
"contentimport",
|
||||
"ai",
|
||||
"help",
|
||||
],
|
||||
],
|
||||
toolbarShow: {
|
||||
ai: false,
|
||||
},
|
||||
toolbarCallback: function (cmd, editor) {
|
||||
// switch(cmd){
|
||||
// case 'insertimage':
|
||||
// editor.execCommand('insertHtml', '<p><img src="xxxxx" /></p>');
|
||||
// console.log('toolbarCallback',cmd, editor)
|
||||
// return true;
|
||||
// case 'insertvideo':
|
||||
// editor.execCommand('insertHtml', '<p><img src="xxxxx" /></p>');
|
||||
// console.log('toolbarCallback',cmd, editor)
|
||||
// return true;
|
||||
// }
|
||||
},
|
||||
imageConfig: {
|
||||
// 禁止本地上传
|
||||
disableUpload: false,
|
||||
// 禁止在线管理
|
||||
disableOnline: false,
|
||||
// 自定义选择按钮
|
||||
selectCallback: null,
|
||||
// selectCallback: function(editor,cb){
|
||||
// console.log('selectCallback',cb);
|
||||
// setTimeout(function(){
|
||||
// cb({
|
||||
// path:'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png',
|
||||
// name:'测试图片'
|
||||
// });
|
||||
// },1000);
|
||||
// }
|
||||
},
|
||||
videoConfig: {
|
||||
// 禁止本地上传,
|
||||
disableUpload: false,
|
||||
// 自定义选择按钮
|
||||
selectCallback: null,
|
||||
// selectCallback: function(editor,cb){
|
||||
// console.log('selectCallback',cb);
|
||||
// setTimeout(function(){
|
||||
// cb({
|
||||
// path:'https://www.bilibili.com/video/BV1y44y1g7NR?spm_id_from=333.1007.tianma.1-1-1.click',
|
||||
// name:'测试视频'
|
||||
// });
|
||||
// },1000);
|
||||
// }
|
||||
},
|
||||
formulaConfig: {
|
||||
// 公式渲染链接模板
|
||||
imageUrlTemplate: "https://latex.codecogs.com/svg.image?{}",
|
||||
// 为了更稳定的公式渲染服务,可以使用
|
||||
// 魔众官方公式转图片渲染引擎 https://api.tecmz.com
|
||||
},
|
||||
autoSaveEnable: true,
|
||||
autoSaveRestore: true,
|
||||
autoSaveKey: null,
|
||||
initialContent: "<p>初始化内容</p>",
|
||||
uploadServiceEnable: false,
|
||||
uploadServiceUpload: function (type, file, callback, option) {
|
||||
var i = 0;
|
||||
var call = function () {
|
||||
i++;
|
||||
if (i > 3) {
|
||||
callback.success({
|
||||
state: "SUCCESS",
|
||||
url: "https://ms-assets.modstart.com/demo/modstart.jpg",
|
||||
});
|
||||
return;
|
||||
}
|
||||
setTimeout(function () {
|
||||
callback.progress(0.3 * i);
|
||||
call();
|
||||
}, 500);
|
||||
};
|
||||
call();
|
||||
},
|
||||
focus: true,
|
||||
initialStyle: "p{line-height:1.5em}",
|
||||
iframeCssStylesAddition: ["body { background-color: #f0f0f0; }", ".custom-class { font-size: 14px; color: #333; }", "p { line-height: 1.8; }"],
|
||||
initialFrameWidth: "100%", // 初始宽度
|
||||
initialFrameHeight: 320, // 初始高度
|
||||
readonly: false,
|
||||
enableContextMenu: true,
|
||||
|
||||
autoHeightEnabled: true, // 自动高度
|
||||
autotypeset: {
|
||||
// 合并空行
|
||||
mergeEmptyline: true,
|
||||
// 去掉冗余的class
|
||||
removeClass: true,
|
||||
// 去掉空行
|
||||
removeEmptyline: false,
|
||||
// 段落的排版方式,可以是 left,right,center,justify 去掉这个属性表示不执行排版
|
||||
textAlign: "left",
|
||||
// 图片的浮动方式,独占一行剧中,左右浮动,默认: center,left,right,none 去掉这个属性表示不执行排版
|
||||
imageBlockLine: "center",
|
||||
// 根据规则过滤没事粘贴进来的内容
|
||||
pasteFilter: false,
|
||||
// 去掉所有的内嵌字号,使用编辑器默认的字号
|
||||
clearFontSize: false,
|
||||
// 去掉所有的内嵌字体,使用编辑器默认的字体
|
||||
clearFontFamily: false,
|
||||
// 去掉空节点
|
||||
removeEmptyNode: false,
|
||||
// 可以去掉的标签
|
||||
removeTagNames: { div: 1 },
|
||||
// 行首缩进
|
||||
indent: false,
|
||||
// 行首缩进的大小
|
||||
indentValue: "2em",
|
||||
// 全角转半角
|
||||
bdc2sb: false,
|
||||
// 半角转全角
|
||||
tobdc: false,
|
||||
},
|
||||
// UEditorPlus文件路径配置
|
||||
},
|
||||
|
||||
// 常量定义
|
||||
CONST: {
|
||||
MOD_NAME: "ueditor",
|
||||
ELEM: "layui-ueditor-plus",
|
||||
CLASS_EDITOR: "layui-ueditor-editor",
|
||||
},
|
||||
|
||||
render: function (options) {
|
||||
var that = this;
|
||||
const { access_token } = layui.http.getTokens();
|
||||
that.config.serverHeaders = {
|
||||
Authorization: "Bearer " + access_token,
|
||||
};
|
||||
var config = $.extend(true, {}, this.config, options);
|
||||
if (!config.elem) {
|
||||
layui.hint().error("UEditorPlus组件: elem参数不能为空");
|
||||
return that;
|
||||
}
|
||||
var elem = $(config.elem);
|
||||
if (!elem.length) {
|
||||
layui.hint().error("UEditorPlus组件: 元素 " + config.elem + " 不存在");
|
||||
return that;
|
||||
}
|
||||
var id = config.id; //|| "ueditor-plus-" + that.index;
|
||||
// 缓存配置
|
||||
that.cache[id] = config;
|
||||
// 创建编辑器容器
|
||||
// var editorContainer = $('<div class="' + "layui-ueditor-editor" + '" id="' + id + '-container"></div>');
|
||||
// elem.html(editorContainer);
|
||||
|
||||
that.loadUEditorPlus(config, function () {
|
||||
that.initEditor(id, config);
|
||||
});
|
||||
return ueditorPlus.getInst(id);
|
||||
},
|
||||
beforeInit: function (options) {},
|
||||
beforeRender: function (options) {},
|
||||
extendsInstance: function (that) {
|
||||
return {
|
||||
close: function () {
|
||||
that.remove(); // 调用组件原型中的 remove 方法
|
||||
},
|
||||
setContent: function (id,content) {
|
||||
var that = ueditorPlus.getInst(id);
|
||||
return that.ueditor.setContent(content);
|
||||
},
|
||||
getContent: function (id) {
|
||||
var that = ueditorPlus.getInst(id);
|
||||
return that.ueditor.getContent();
|
||||
},
|
||||
destroy: function (id) {
|
||||
var that = ueditorPlus.getInst(id);
|
||||
if (!that) return this;
|
||||
that.ueditor.destroy();
|
||||
},
|
||||
getEditor: function (id) {
|
||||
var that = ueditorPlus.getInst(id);
|
||||
return that.ueditor || null;
|
||||
},
|
||||
reload: function (params) {},
|
||||
};
|
||||
},
|
||||
});
|
||||
// 获取构造器
|
||||
var Class = ueditorPlus.Class;
|
||||
|
||||
// 初始化编辑器
|
||||
Class.prototype.initEditor = function (id, config) {
|
||||
var that = this;
|
||||
var container = document.getElementById(id);
|
||||
if (!container || !window.UE) {
|
||||
layui.hint().error("UEditorPlus组件: 初始化失败,容器或编辑器未就绪");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// // 创建编辑器实例
|
||||
var editor = UE.getEditor(id, config);
|
||||
// 缓存编辑器实例
|
||||
that.cache[id].editor = that.ueditor = editor;
|
||||
} catch (e) {
|
||||
layui.hint().error("UEditorPlus组件: 初始化异常 - " + e.message);
|
||||
}
|
||||
};
|
||||
|
||||
Class.prototype.ueditor = null;
|
||||
Class.prototype.loadUEditorPlus = function (config, callback) {
|
||||
var that = this;
|
||||
// 检查是否已加载
|
||||
if (window.UEDITOR_CONFIG || window.UEditorPlus) {
|
||||
callback && callback();
|
||||
return;
|
||||
}
|
||||
// 设置UEditorPlus全局配置
|
||||
window.UEDITOR_HOME_URL = config.UEDITOR_HOME_URL || "./ueditor-plus/";
|
||||
layui.link(window.UEDITOR_HOME_URL + "themes/default/css/ueditor.css");
|
||||
// 加载配置文件
|
||||
that.loadScript(window.UEDITOR_HOME_URL + "ueditor.config.js", function () {
|
||||
// 加载编辑器主文件
|
||||
that.loadScript(window.UEDITOR_HOME_URL + "ueditor.all.js", function () {
|
||||
// 加载完成后执行回调
|
||||
setTimeout(function () {
|
||||
callback && callback();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
};
|
||||
// 动态加载JS文件
|
||||
Class.prototype.loadScript = function (url, callback) {
|
||||
var script = document.createElement("script");
|
||||
script.type = "text/javascript";
|
||||
script.src = url;
|
||||
script.onload = script.onreadystatechange = function () {
|
||||
if (!this.readyState || this.readyState === "loaded" || this.readyState === "complete") {
|
||||
callback && callback();
|
||||
script.onload = script.onreadystatechange = null;
|
||||
}
|
||||
};
|
||||
script.onerror = function () {
|
||||
layui.hint().error("UEditorPlus组件: 加载文件失败: " + url);
|
||||
};
|
||||
document.getElementsByTagName("head")[0].appendChild(script);
|
||||
};
|
||||
|
||||
// 输出组件
|
||||
exports(ueditorPlus.CONST.MOD_NAME, ueditorPlus);
|
||||
});
|
||||
@@ -0,0 +1,302 @@
|
||||
/**
|
||||
* The Web UI Theme-v2.7.1
|
||||
*/
|
||||
layui.define(["laytpl", "layer", "http"], function (exports) {
|
||||
// 构造函数 u - 主要接口
|
||||
function View(e) {
|
||||
return new Template(e);
|
||||
}
|
||||
// 构造函数 t - 模板类
|
||||
function Template(e) {
|
||||
this.id = e;
|
||||
this.container = layui.jquery("#" + (e || "LAY_app_body"));
|
||||
}
|
||||
// 引入依赖模块
|
||||
var $ = layui.jquery,
|
||||
laytpl = layui.laytpl,
|
||||
layer = layui.layer,
|
||||
request = layui.http,
|
||||
setter = layui.setter,
|
||||
device = layui.device(),
|
||||
hint = layui.hint(),
|
||||
DEFAULT_CONTAINER_ID = "LAY_app_body";
|
||||
// 加载动画
|
||||
View.loading = function (container) {
|
||||
container.append(
|
||||
this.elemLoad = $('<i class="layui-anim layui-anim-rotate layui-anim-loop layui-icon layui-icon-loading layadmin-loading"></i>')
|
||||
);
|
||||
};
|
||||
// 移除加载动画
|
||||
View.removeLoad = function () {
|
||||
this.elemLoad && this.elemLoad.remove();
|
||||
};
|
||||
// 退出登录
|
||||
View.exit = function (callback) {
|
||||
// setter.request.tokenName 当前为 false,不能作为键,否则等于没清理。
|
||||
// 统一清空本应用命名空间下的登录态与菜单缓存(对齐 layuiadmin 的退出清理)。
|
||||
var appName = setter.appName;
|
||||
['access_token', 'refresh_token', 'menus', 'user', 'note'].forEach(function (key) {
|
||||
layui.data(appName, { key: key, remove: true });
|
||||
});
|
||||
callback && callback();
|
||||
};
|
||||
// AJAX 请求封装
|
||||
View.req = function (options) {
|
||||
function getUrlInfo() {
|
||||
return setter.debug ? "<br><cite>URL:</cite>" + options.url : "";
|
||||
}
|
||||
var successCallback = options.success,
|
||||
errorCallback = options.error,
|
||||
requestConfig = setter.request,
|
||||
responseConfig = setter.response;
|
||||
// 处理请求数据
|
||||
options.data = options.data || {};
|
||||
options.headers = options.headers || {};
|
||||
// 处理 token
|
||||
if (requestConfig.tokenName) {
|
||||
var data = typeof options.data === "string" ? JSON.parse(options.data) : options.data;
|
||||
options.data[requestConfig.tokenName] =
|
||||
requestConfig.tokenName in data ?
|
||||
options.data[requestConfig.tokenName] :
|
||||
layui.data(setter.appName)[requestConfig.tokenName] || "";
|
||||
|
||||
options.headers[requestConfig.tokenName] =
|
||||
requestConfig.tokenName in options.headers ?
|
||||
options.headers[requestConfig.tokenName] :
|
||||
layui.data(setter.appName)[requestConfig.tokenName] || "";
|
||||
}
|
||||
// 清除原始回调
|
||||
delete options.success;
|
||||
delete options.error;
|
||||
// 执行 AJAX 请求
|
||||
return $.ajax($.extend({
|
||||
type: "get",
|
||||
dataType: "json",
|
||||
success: function (response) {
|
||||
var statusCode = responseConfig.statusCode;
|
||||
if (response[responseConfig.statusName] == statusCode.ok) {
|
||||
// 成功
|
||||
typeof options.done === "function" && options.done(response);
|
||||
} else if (response[responseConfig.statusName] == statusCode.logout) {
|
||||
// 退出登录
|
||||
View.exit();
|
||||
} else {
|
||||
// 错误处理
|
||||
var errorMsg = [
|
||||
"<cite>Error:</cite> " + (response[responseConfig.msgName] || "返回状态码异常"),
|
||||
getUrlInfo()
|
||||
].join("");
|
||||
View.error(errorMsg);
|
||||
}
|
||||
typeof successCallback === "function" && successCallback(response);
|
||||
},
|
||||
error: function (xhr, status) {
|
||||
var errorMsg = [
|
||||
"请求异常,请重试<br><cite>错误信息:</cite>" + status,
|
||||
getUrlInfo()
|
||||
].join("");
|
||||
View.error(errorMsg);
|
||||
typeof errorCallback === "function" && errorCallback.apply(this, arguments);
|
||||
}
|
||||
}, options));
|
||||
};
|
||||
// 弹窗
|
||||
View.popup = function (options) {
|
||||
var successCallback = options.success, skin = options.skin;
|
||||
delete options.success;
|
||||
delete options.skin;
|
||||
return layer.open($.extend({
|
||||
type: 1,
|
||||
title: "提示",
|
||||
content: "",
|
||||
id: "LAY-system-view-popup",
|
||||
skin: "layui-layer-admin" + (skin ? " " + skin : ""),
|
||||
shadeClose: true,
|
||||
closeBtn: false,
|
||||
success: function (layero, index) {
|
||||
var closeBtn = $('<i class="layui-icon" close>ဆ</i>');
|
||||
layero.append(closeBtn);
|
||||
closeBtn.on("click", function () {
|
||||
layer.close(index);
|
||||
});
|
||||
typeof successCallback === "function" && successCallback.apply(this, arguments);
|
||||
}
|
||||
}, options));
|
||||
};
|
||||
// 错误提示
|
||||
View.error = function (content, options) {
|
||||
return View.popup($.extend({
|
||||
content: content,
|
||||
maxWidth: 300,
|
||||
offset: "t",
|
||||
anim: 6,
|
||||
id: "LAY_adminError"
|
||||
}, options));
|
||||
};
|
||||
// Template 原型方法
|
||||
// 渲染视图
|
||||
Template.prototype.render = function (templatePath, params) {
|
||||
var self = this;
|
||||
layui.hash();
|
||||
|
||||
// 构建完整模板路径
|
||||
templatePath = (setter.paths && setter.paths.views ? setter.paths : setter).views +
|
||||
templatePath + setter.engine;
|
||||
// 移除加载动画
|
||||
$("#" + DEFAULT_CONTAINER_ID).children(".layadmin-loading").remove();
|
||||
View.loading(self.container);
|
||||
// AJAX 获取模板
|
||||
request.get(templatePath, { v: layui.cache.version }).then(function (html) {
|
||||
// 提取标题
|
||||
var titleElem = $(html = "<div>" + html + "</div>").find("title");
|
||||
var viewData = {
|
||||
title: titleElem.text() || (html.match(/<title>([\s\S]*)<\/title>/) || [])[1],
|
||||
body: html
|
||||
};
|
||||
titleElem.remove();
|
||||
// 设置参数
|
||||
self.params = params || {};
|
||||
// 执行 then 回调
|
||||
self.then && (self.then(viewData), delete self.then);
|
||||
// 解析模板
|
||||
self.parse(html);
|
||||
View.removeLoad();
|
||||
// 执行 done 回调
|
||||
self.done && (self.done(viewData), delete self.done);
|
||||
}).catch(function (xhr) {
|
||||
View.removeLoad();
|
||||
// 防止重复错误处理
|
||||
if (self.render.isError) {
|
||||
return View.error("请求视图文件异常,状态:" + xhr.status);
|
||||
}
|
||||
// 根据状态码显示不同错误页面
|
||||
if (xhr.status === 404) {
|
||||
self.render("template/tips/404");
|
||||
} else {
|
||||
self.render("template/tips/error");
|
||||
}
|
||||
self.render.isError = true;
|
||||
});
|
||||
|
||||
return self;
|
||||
};
|
||||
// 解析模板
|
||||
Template.prototype.parse = function (html, isRefresh, callback) {
|
||||
function processTemplate(templateInfo) {
|
||||
var compiledTemplate = laytpl(templateInfo.dataElem.html());
|
||||
var renderData = $.extend({ params: self.params }, templateInfo.res);
|
||||
// 渲染并插入 DOM
|
||||
templateInfo.dataElem.after(compiledTemplate.render(renderData));
|
||||
// 执行回调
|
||||
typeof callback === "function" && callback();
|
||||
// 执行 done 回调(可能是 JavaScript 代码)
|
||||
try {
|
||||
templateInfo.done && new Function("d", templateInfo.done)(renderData);
|
||||
} catch (error) {
|
||||
console.error(templateInfo.dataElem[0], "\n存在错误回调脚本\n\n", error);
|
||||
}
|
||||
}
|
||||
var self = this;
|
||||
var isObject = typeof html === "object";
|
||||
var $html = isObject ? html : $(html);
|
||||
var templateElems = isObject ? html : $html.find("*[template]");
|
||||
var hashData = layui.hash();
|
||||
// 移除标题
|
||||
$html.find("title").remove();
|
||||
// 插入 HTML
|
||||
self.container[isRefresh ? "after" : "html"]($html.children());
|
||||
// 设置参数
|
||||
hashData.params = self.params || {};
|
||||
// 处理所有模板元素
|
||||
for (var i = templateElems.length; i > 0; i--) {
|
||||
(function () {
|
||||
var templateElem = templateElems.eq(i - 1);
|
||||
var doneCallback = templateElem.attr("lay-done") || templateElem.attr("lay-then");
|
||||
// 解析模板属性
|
||||
var urlTemplate = templateElem.attr("lay-url") || "";
|
||||
var dataTemplate = templateElem.attr("lay-data") || "";
|
||||
var headersTemplate = templateElem.attr("lay-headers") || "";
|
||||
var url = laytpl(urlTemplate).render(hashData);
|
||||
var data = laytpl(dataTemplate).render(hashData);
|
||||
var headers = laytpl(headersTemplate).render(hashData);
|
||||
// 解析数据对象
|
||||
try {
|
||||
data = new Function("return " + data + ";")();
|
||||
} catch (error) {
|
||||
hint.error("lay-data: " + error.message);
|
||||
data = {};
|
||||
}
|
||||
// 解析头部对象
|
||||
try {
|
||||
headers = new Function("return " + headers + ";")();
|
||||
} catch (error) {
|
||||
hint.error("lay-headers: " + error.message);
|
||||
headers = headers || {};
|
||||
}
|
||||
// 如果有 URL,则发起请求
|
||||
if (url) {
|
||||
View.req({
|
||||
type: templateElem.attr("lay-type") || "get",
|
||||
url: url,
|
||||
data: data,
|
||||
dataType: "json",
|
||||
headers: headers,
|
||||
success: function (response) {
|
||||
processTemplate({
|
||||
dataElem: templateElem,
|
||||
res: response,
|
||||
done: doneCallback
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 直接处理模板
|
||||
processTemplate({
|
||||
dataElem: templateElem,
|
||||
done: doneCallback
|
||||
});
|
||||
}
|
||||
})();
|
||||
}
|
||||
return self;
|
||||
};
|
||||
// 自动渲染
|
||||
Template.prototype.autoRender = function (selector, callback) {
|
||||
var self = this;
|
||||
$(selector || "body").find("*[template]").each(function () {
|
||||
var $this = $(this);
|
||||
self.container = $this;
|
||||
self.parse($this, "refresh");
|
||||
});
|
||||
};
|
||||
// 发送数据到模板
|
||||
Template.prototype.send = function (template, data) {
|
||||
template = laytpl(template || this.container.html()).render(data || {});
|
||||
this.container.html(template);
|
||||
return this;
|
||||
};
|
||||
// 刷新
|
||||
Template.prototype.refresh = function (callback) {
|
||||
var self = this;
|
||||
var nextTemplateId = self.container.next().attr("lay-templateid");
|
||||
if (self.id == nextTemplateId) {
|
||||
self.parse(self.container, "refresh", function () {
|
||||
self.container.siblings('[lay-templateid="' + self.id + '"]:last').remove();
|
||||
typeof callback === "function" && callback();
|
||||
});
|
||||
}
|
||||
return self;
|
||||
};
|
||||
// 设置 then 回调
|
||||
Template.prototype.then = function (callback) {
|
||||
this.then = callback;
|
||||
return this;
|
||||
};
|
||||
// 设置 done 回调
|
||||
Template.prototype.done = function (callback) {
|
||||
this.done = callback;
|
||||
return this;
|
||||
};
|
||||
// 导出模块
|
||||
exports("view", View);
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,835 @@
|
||||
/**
|
||||
* Layui Admin Module - Theme v2.7.1
|
||||
* 功能:后台管理系统的核心模块,负责布局、导航、标签页、主题、事件管理等。
|
||||
*/
|
||||
layui.define("view", function (exports) {
|
||||
// 依赖模块
|
||||
var $ = layui.jquery,
|
||||
laytpl = layui.laytpl,
|
||||
table = layui.table,
|
||||
element = layui.element,
|
||||
util = layui.util,
|
||||
upload = layui.upload,
|
||||
setter = layui.setter,
|
||||
view = layui.view,
|
||||
device = layui.device();
|
||||
|
||||
// DOM 元素缓存
|
||||
var win = $(window),
|
||||
doc = $(document),
|
||||
body = $("body"),
|
||||
container = $("#" + setter.container),
|
||||
MAIN_BODY_ID = "#LAY_app_body",
|
||||
FLEXIBLE_BTN_ID = "LAY_app_flexible",
|
||||
TABS_CONTAINER_ID = "layadmin-layout-tabs",
|
||||
SIDE_SPREAD_CLASS = "layadmin-side-spread-sm",
|
||||
TABS_BODY_ITEM_CLASS = "layadmin-tabsbody-item",
|
||||
SHRINK_ICON_CLASS = "layui-icon-shrink-right",
|
||||
SPREAD_ICON_CLASS = "layui-icon-spread-left",
|
||||
SIDE_SHRINK_CLASS = "layadmin-side-shrink";
|
||||
|
||||
// 核心 Admin 对象
|
||||
var admin = {
|
||||
version: "2.7.1",
|
||||
mode: "iframe", // 默认使用 iframe 模式
|
||||
req: view.req, // 复用 view 模块的请求方法
|
||||
exit: view.exit, // 复用 view 模块的退出方法
|
||||
escape: util.escape, // 复用工具方法
|
||||
|
||||
// 事件监听快捷方式
|
||||
on: function (eventName, callback) {
|
||||
return layui.onevent.call(this, setter.appName, eventName, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据当前路由高亮侧边栏菜单
|
||||
* @param {Object} hashObj - 路由哈希对象(可选,默认从当前URL解析)
|
||||
*/
|
||||
setSideMenuStatus: function (hashObj) {
|
||||
hashObj = hashObj || layui.hash();
|
||||
var $sideMenu = $("#LAY-system-side-menu"),
|
||||
activeClass = "layui-nav-itemed";
|
||||
|
||||
// 移除所有激活状态
|
||||
$sideMenu.find(".layui-this").removeClass("layui-this");
|
||||
|
||||
// 如果是小屏幕,自动处理侧边栏伸缩
|
||||
admin.screen() < 2 && admin.sideFlexible();
|
||||
|
||||
var currentPath = hashObj.path.join("/");
|
||||
|
||||
// 遍历所有菜单项,匹配当前路由
|
||||
$sideMenu.find("li, dd").each(function () {
|
||||
var $this = $(this),
|
||||
$link = $this.children("a"),
|
||||
href = admin.correctRouter($link.attr("lay-href"));
|
||||
|
||||
// 匹配逻辑:完全匹配或路径包含
|
||||
if (href === hashObj.href || href === currentPath || (href !== "/" && hashObj.href.indexOf(href) !== -1)) {
|
||||
// 激活当前项
|
||||
$sideMenu.find(".layui-this").removeClass("layui-this");
|
||||
$this.addClass("layui-this");
|
||||
|
||||
// 展开父级菜单
|
||||
var $parentItem = $this.closest(".layui-nav-item"),
|
||||
$parentDd = $this.parent().closest("dd");
|
||||
|
||||
function expandItem($elem) {
|
||||
$elem.addClass(activeClass).siblings().removeClass(activeClass);
|
||||
}
|
||||
expandItem($parentItem);
|
||||
expandItem($parentDd);
|
||||
|
||||
// 滚动到可视区域
|
||||
util.toVisibleArea({
|
||||
scrollElem: container.find(".layui-side-scroll"),
|
||||
thisElem: $this
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 获取屏幕尺寸等级
|
||||
* @returns {Number} 3: >1200px, 2: 992-1200px, 1: 768-992px, 0: <768px
|
||||
*/
|
||||
screen: function () {
|
||||
var width = win.width();
|
||||
if (width > 1200) return 3;
|
||||
if (width > 992) return 2;
|
||||
if (width > 768) return 1;
|
||||
return 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* 侧边栏伸缩切换
|
||||
* @param {String} status - 'spread' 展开,其他或空为收缩
|
||||
*/
|
||||
sideFlexible: function (status) {
|
||||
var $flexBtn = $("#" + FLEXIBLE_BTN_ID),
|
||||
screenSize = admin.screen();
|
||||
|
||||
if (status === "spread") {
|
||||
$flexBtn.removeClass(SPREAD_ICON_CLASS).addClass(SHRINK_ICON_CLASS);
|
||||
if (screenSize < 2) {
|
||||
container.addClass(SIDE_SPREAD_CLASS);
|
||||
} else {
|
||||
container.removeClass(SIDE_SPREAD_CLASS);
|
||||
}
|
||||
container.removeClass(SIDE_SHRINK_CLASS);
|
||||
} else {
|
||||
$flexBtn.removeClass(SHRINK_ICON_CLASS).addClass(SPREAD_ICON_CLASS);
|
||||
if (screenSize < 2) {
|
||||
container.removeClass(SIDE_SHRINK_CLASS);
|
||||
} else {
|
||||
container.addClass(SIDE_SHRINK_CLASS);
|
||||
}
|
||||
container.removeClass(SIDE_SPREAD_CLASS);
|
||||
}
|
||||
|
||||
// 触发自定义事件
|
||||
layui.event.call(this, setter.appName, "side({*})", { status: status });
|
||||
},
|
||||
|
||||
// 弹窗相关方法(复用 view 模块)
|
||||
popup: view.popup,
|
||||
popupRight: function (options) {
|
||||
return admin.popup.index = layer.open($.extend({
|
||||
type: 1,
|
||||
id: "LAY_adminPopupR",
|
||||
anim: -1,
|
||||
title: false,
|
||||
closeBtn: false,
|
||||
offset: "r",
|
||||
shade: 0.1,
|
||||
shadeClose: true,
|
||||
skin: "layui-anim layui-anim-rl layui-layer-adminRight",
|
||||
area: "300px"
|
||||
}, options));
|
||||
},
|
||||
|
||||
/**
|
||||
* 动态切换主题
|
||||
* @param {Object} themeConfig - 主题配置对象
|
||||
*/
|
||||
theme: function (themeConfig) {
|
||||
var localTheme = layui.data(setter.appName).theme;
|
||||
var styleId = "LAY_layadmin_theme";
|
||||
var oldStyle = document.getElementById(styleId);
|
||||
var newStyle = document.createElement("style");
|
||||
|
||||
// 清除主题
|
||||
if (themeConfig.CLEAR) {
|
||||
$(oldStyle).remove();
|
||||
layui.data(setter.appName, { key: "theme", remove: true });
|
||||
return;
|
||||
}
|
||||
|
||||
// 合并配置并生成 CSS
|
||||
themeConfig = $.extend({}, localTheme, themeConfig);
|
||||
var cssTemplate = [
|
||||
".layui-side-menu,",
|
||||
".layui-layer-admin .layui-layer-title,",
|
||||
".layadmin-side-shrink .layui-side-menu .layui-nav>.layui-nav-item>.layui-nav-child",
|
||||
"{background-color:{{d.color.main}} !important;}",
|
||||
".layadmin-pagetabs .layui-tab-title li:after,",
|
||||
".layadmin-pagetabs .layui-tab-title li.layui-this:after,",
|
||||
".layui-nav-tree .layui-this,",
|
||||
".layui-nav-tree .layui-this>a,",
|
||||
".layui-nav-tree .layui-nav-child dd.layui-this,",
|
||||
".layui-nav-tree .layui-nav-child dd.layui-this a,",
|
||||
".layui-nav-tree .layui-nav-bar",
|
||||
"{background-color:{{d.color.selected}} !important;}",
|
||||
".layadmin-pagetabs .layui-tab-title li:hover,",
|
||||
".layadmin-pagetabs .layui-tab-title li.layui-this",
|
||||
"{color: {{d.color.selected}} !important;}",
|
||||
".layui-layout-admin .layui-logo{background-color:{{d.color.logo || d.color.main}} !important;}",
|
||||
"{{# if(d.color.header){ }}",
|
||||
".layui-layout-admin .layui-header{background-color:{{ d.color.header }};}",
|
||||
".layui-layout-admin .layui-header a,",
|
||||
".layui-layout-admin .layui-header a cite{color: #f8f8f8;}",
|
||||
".layui-layout-admin .layui-header a:hover{color: #fff;}",
|
||||
".layui-layout-admin .layui-header .layui-nav .layui-nav-more{border-top-color: #fbfbfb;}",
|
||||
".layui-layout-admin .layui-header .layui-nav .layui-nav-mored{border-color: transparent; border-bottom-color: #fbfbfb;}",
|
||||
".layui-layout-admin .layui-header .layui-this:after, .layui-layout-admin .layui-header .layui-nav-bar{background-color: #fff; background-color: rgba(255,255,255,.5);}",
|
||||
".layadmin-pagetabs .layui-tab-title li:after{display: none;}",
|
||||
"{{# } }}"
|
||||
].join("");
|
||||
var cssContent = laytpl(cssTemplate).render(themeConfig);
|
||||
|
||||
// 插入样式
|
||||
if ("styleSheet" in newStyle) {
|
||||
newStyle.setAttribute("type", "text/css");
|
||||
newStyle.styleSheet.cssText = cssContent;
|
||||
} else {
|
||||
newStyle.innerHTML = cssContent;
|
||||
}
|
||||
newStyle.id = styleId;
|
||||
|
||||
if (oldStyle && body.removeChild) {
|
||||
body[0].removeChild(oldStyle);
|
||||
}
|
||||
console.log(body);
|
||||
body[0].appendChild(newStyle);
|
||||
|
||||
// 更新主题别名和本地存储
|
||||
if (themeConfig.color) {
|
||||
body.attr("layadmin-themealias", themeConfig.color.alias);
|
||||
}
|
||||
localTheme = localTheme || {};
|
||||
$.each(themeConfig, function (key, value) {
|
||||
localTheme[key] = value;
|
||||
});
|
||||
layui.data(setter.appName, { key: "theme", value: localTheme });
|
||||
},
|
||||
|
||||
/**
|
||||
* 初始化主题(从配置中读取)
|
||||
* @param {Number} index - 主题颜色索引
|
||||
*/
|
||||
initTheme: function (index) {
|
||||
var themeConfig = setter.theme;
|
||||
if (themeConfig.color && themeConfig.color[index]) {
|
||||
themeConfig.color[index].index = index;
|
||||
admin.theme({ color: themeConfig.color[index] });
|
||||
}
|
||||
},
|
||||
|
||||
// 标签页状态
|
||||
tabsPage: {},
|
||||
|
||||
/**
|
||||
* 获取标签页内容区域
|
||||
* @param {Number} index - 标签页索引
|
||||
*/
|
||||
tabsBody: function (index) {
|
||||
return $(MAIN_BODY_ID).find("." + TABS_BODY_ITEM_CLASS).eq(index || 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* 切换标签页内容
|
||||
* @param {Number} index - 要切换到的标签页索引
|
||||
* @param {Object} data - 标签页数据 {url, title}
|
||||
*/
|
||||
tabsBodyChange: function (index, data) {
|
||||
data = data || {};
|
||||
admin.tabsBody(index).addClass("layui-show").siblings().removeClass("layui-show");
|
||||
admin.events.rollPage("auto", index);
|
||||
admin.recordURL(data);
|
||||
layui.event.call(this, setter.appName, "tabsPage({*})", data);
|
||||
},
|
||||
|
||||
/**
|
||||
* 记录当前URL到本地存储(用于刷新恢复)
|
||||
* @param {Object} data - {url, title}
|
||||
*/
|
||||
recordURL: function (data) {
|
||||
var recordData;
|
||||
if ((setter.record || {}).url && data.url) {
|
||||
// 过滤外部链接
|
||||
if (/^(\w*:)*\/\/.+/.test(data.url)) {
|
||||
data.url = "";
|
||||
}
|
||||
// 更新 hash
|
||||
location.hash = admin.correctRouter(data.url);
|
||||
// 存储标题
|
||||
if (data.url && data.title) {
|
||||
(recordData = {})[data.url] = data.title;
|
||||
layui.data(setter.appName, { key: "record", value: recordData });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 窗口大小变化监听管理
|
||||
* @param {Function} fn - 回调函数,传入 'off' 则移除监听
|
||||
*/
|
||||
resize: function (fn) {
|
||||
var routeKey = layui.hash().path.join("-");
|
||||
if (admin.resizeFn[routeKey]) {
|
||||
win.off("resize", admin.resizeFn[routeKey]);
|
||||
delete admin.resizeFn[routeKey];
|
||||
}
|
||||
if (fn !== "off") {
|
||||
fn();
|
||||
admin.resizeFn[routeKey] = fn;
|
||||
win.on("resize", admin.resizeFn[routeKey]);
|
||||
}
|
||||
},
|
||||
|
||||
// 存储 resize 回调函数
|
||||
resizeFn: {},
|
||||
|
||||
/**
|
||||
* 手动触发当前路由对应的 resize 回调
|
||||
*/
|
||||
runResize: function () {
|
||||
var routeKey = layui.hash().path.join("-");
|
||||
admin.resizeFn[routeKey] && admin.resizeFn[routeKey];
|
||||
},
|
||||
|
||||
/**
|
||||
* 移除当前路由的 resize 监听
|
||||
*/
|
||||
delResize: function () {
|
||||
this.resize("off");
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭当前标签页
|
||||
*/
|
||||
closeThisTabs: function () {
|
||||
admin.tabsPage.index && $(".layui-tab-title li").eq(admin.tabsPage.index).find(".layui-tab-close").trigger("click");
|
||||
},
|
||||
|
||||
/**
|
||||
* 全屏
|
||||
*/
|
||||
fullScreen: function () {
|
||||
var docEl = document.documentElement;
|
||||
var requestFullscreen = docEl.requestFullscreen || docEl.webkitRequestFullScreen || docEl.mozRequestFullScreen || docEl.msRequestFullscreen;
|
||||
if (typeof requestFullscreen !== "undefined" && requestFullscreen) {
|
||||
requestFullscreen.call(docEl);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 退出全屏
|
||||
*/
|
||||
exitScreen: function () {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.webkitCancelFullScreen) {
|
||||
document.webkitCancelFullScreen();
|
||||
} else if (document.msExitFullscreen) {
|
||||
document.msExitFullscreen();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 修正路由路径
|
||||
* @param {String} path - 原始路径
|
||||
*/
|
||||
correctRouter: function (path) {
|
||||
path = /^\//.test(path) ? path : "/" + path;
|
||||
path = path.replace(/^(\/+)/, "/").replace(new RegExp("/" + setter.entry + "$"), "/");
|
||||
return path;
|
||||
}
|
||||
};
|
||||
|
||||
// 事件处理对象
|
||||
var events = admin.events = {
|
||||
/**
|
||||
* 侧边栏伸缩按钮事件
|
||||
*/
|
||||
flexible: function ($elem) {
|
||||
var isSpread = $elem.find("#" + FLEXIBLE_BTN_ID).hasClass(SPREAD_ICON_CLASS);
|
||||
admin.sideFlexible(isSpread ? "spread" : null);
|
||||
},
|
||||
|
||||
/**
|
||||
* 刷新当前页
|
||||
*/
|
||||
refresh: function () {
|
||||
var tabCount = $("." + TABS_BODY_ITEM_CLASS).length;
|
||||
// 防止索引越界
|
||||
if (admin.tabsPage.index >= tabCount) {
|
||||
admin.tabsPage.index = tabCount - 1;
|
||||
}
|
||||
admin.tabsBody(admin.tabsPage.index).find(".layadmin-iframe")[0].contentWindow.location.reload(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* 顶部搜索框事件
|
||||
*/
|
||||
serach: function ($searchInput) {
|
||||
$searchInput.off("keypress").on("keypress", function (e) {
|
||||
var actionUrl, title;
|
||||
if (this.value.replace(/\s/g, "") && e.keyCode === 13) { // 回车键
|
||||
actionUrl = $searchInput.attr("lay-action");
|
||||
title = $searchInput.attr("lay-title") || $searchInput.attr("lay-text") || "搜索";
|
||||
actionUrl += this.value;
|
||||
title = title + ": " + admin.escape(this.value);
|
||||
admin.openTabsPage({
|
||||
url: actionUrl,
|
||||
title: title,
|
||||
highlight: "color: #FF5722;"
|
||||
});
|
||||
// 记录搜索关键词
|
||||
events.serach.keys || (events.serach.keys = {});
|
||||
events.serach.keys[admin.tabsPage.index] = this.value;
|
||||
// 如果与上次搜索相同,则刷新页面
|
||||
if (this.value === events.serach.keys[admin.tabsPage.index]) {
|
||||
events.refresh($searchInput);
|
||||
}
|
||||
this.value = "";
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 清除消息红点
|
||||
*/
|
||||
message: function ($elem) {
|
||||
$elem.find(".layui-badge-dot").remove();
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开主题设置面板
|
||||
*/
|
||||
theme: function () {
|
||||
admin.popupRight({
|
||||
id: "LAY_adminPopupTheme",
|
||||
success: function () {
|
||||
view(this.id).render("theme");
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开便签面板
|
||||
*/
|
||||
note: function ($btn) {
|
||||
var isSmallScreen = admin.screen() < 2;
|
||||
var savedNote = layui.data(setter.appName).note;
|
||||
events.note.index = admin.popup({
|
||||
title: "本地便签",
|
||||
shade: 0,
|
||||
offset: ["41px", isSmallScreen ? null : $btn.offset().left - 250 + "px"],
|
||||
anim: -1,
|
||||
id: "LAY_adminNote",
|
||||
skin: "layadmin-note layui-anim layui-anim-upbit",
|
||||
content: '<textarea placeholder="内容"></textarea>',
|
||||
resize: false,
|
||||
success: function (elem, index) {
|
||||
elem.find("textarea")
|
||||
.val(typeof savedNote === "undefined" ? "便签中的内容会存储在本地,这样即便你关掉了浏览器,在下次打开时,依然会读取到上一次的记录。是个非常小巧实用的本地备忘录。" : savedNote)
|
||||
.focus()
|
||||
.on("keyup", function () {
|
||||
layui.data(setter.appName, { key: "note", value: this.value });
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 全屏/退出全屏切换
|
||||
*/
|
||||
fullscreen: function ($btn, data) {
|
||||
function toggleIcon(isFullscreen) {
|
||||
isFullscreen ? $btn.addClass("layui-icon-screen-restore").removeClass("layui-icon-screen-full") :
|
||||
$btn.addClass("layui-icon-screen-full").removeClass("layui-icon-screen-restore");
|
||||
}
|
||||
var $icon = $btn.children("i");
|
||||
var isFullscreen = $icon.hasClass("layui-icon-screen-full");
|
||||
if (data) {
|
||||
return toggleIcon(data.status);
|
||||
}
|
||||
toggleIcon(isFullscreen);
|
||||
isFullscreen ? admin.fullScreen() : admin.exitScreen();
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开关于面板
|
||||
*/
|
||||
about: function () {
|
||||
admin.popupRight({
|
||||
id: "LAY_adminPopupAbout",
|
||||
success: function () {
|
||||
view(this.id).render("about");
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 退出登录(对齐 layuiadmin 标准)
|
||||
* 之前的 events 中没有 logout,导致后台/会员中心点击"退出"无反应。
|
||||
* 这里统一:清本地登录态与菜单缓存 -> 调后端登出 -> 跳登录页。
|
||||
*/
|
||||
logout: function () {
|
||||
var appName = setter.appName;
|
||||
var segs = location.pathname.split('/').filter(Boolean);
|
||||
var prefix = segs.length ? '/' + segs[0] : '';
|
||||
|
||||
// 1. 清空本应用命名空间下的登录态与菜单缓存,避免同浏览器串号、残留旧菜单
|
||||
['access_token', 'refresh_token', 'menus', 'user', 'note', 'theme', 'record']
|
||||
.forEach(function (key) {
|
||||
layui.data(appName, { key: key, remove: true });
|
||||
});
|
||||
|
||||
// 2. 调后端登出接口(best-effort,失败也不影响前端清理)
|
||||
try {
|
||||
layui.http.post(prefix + '/login/logout');
|
||||
} catch (e) {}
|
||||
|
||||
// 3. 跳登录页(优先顶级窗口,兼容 iframe 版)
|
||||
var loginUrl = prefix + '/login/index';
|
||||
if (window.self !== window.top) {
|
||||
window.top.location.href = loginUrl;
|
||||
} else {
|
||||
location.href = loginUrl;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开更多面板
|
||||
*/
|
||||
more: function () {
|
||||
admin.popupRight({
|
||||
id: "LAY_adminPopupMore",
|
||||
success: function () {
|
||||
view(this.id).render("system/more");
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 浏览器后退
|
||||
*/
|
||||
back: function () {
|
||||
history.back();
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置主题颜色
|
||||
*/
|
||||
setTheme: function ($colorBtn) {
|
||||
var colorIndex = $colorBtn.data("index");
|
||||
var prevIndex = $colorBtn.siblings(".layui-this").data("index");
|
||||
if (!$colorBtn.hasClass("layui-this")) {
|
||||
$colorBtn.addClass("layui-this").siblings(".layui-this").removeClass("layui-this");
|
||||
admin.initTheme(colorIndex);
|
||||
view("LAY_adminPopupTheme").render("theme");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 标签页滚动
|
||||
* @param {String} type - 'left' 向左,'auto' 自动,其他向右
|
||||
* @param {Number} index - 目标标签页索引(用于 auto 模式)
|
||||
*/
|
||||
rollPage: function (type, index) {
|
||||
var $tabsHeader = $("#LAY_app_tabsheader");
|
||||
var $tabItems = $tabsHeader.children("li");
|
||||
var headerWidth = $tabsHeader.outerWidth();
|
||||
var currentLeft = parseFloat($tabsHeader.css("left"));
|
||||
|
||||
if (type === "left") {
|
||||
if (currentLeft && currentLeft <= 0) {
|
||||
var targetPos = -currentLeft - headerWidth;
|
||||
$tabItems.each(function (i, elem) {
|
||||
var pos = $(elem).position().left;
|
||||
if (targetPos <= pos) {
|
||||
$tabsHeader.css("left", -pos);
|
||||
return false; // 退出循环
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (type === "auto") {
|
||||
var $targetItem = $tabItems.eq(index);
|
||||
if ($targetItem) {
|
||||
var itemLeft = $targetItem.position().left;
|
||||
// 如果目标项在可视区域左侧之外,滚动到它
|
||||
if (itemLeft < -currentLeft) {
|
||||
$tabsHeader.css("left", -itemLeft);
|
||||
return;
|
||||
}
|
||||
// 如果目标项在可视区域右侧之外,滚动到它
|
||||
var itemRight = itemLeft + $targetItem.outerWidth();
|
||||
if (itemRight >= headerWidth - currentLeft) {
|
||||
var overflow = itemRight - (headerWidth - currentLeft);
|
||||
$tabItems.each(function (i, elem) {
|
||||
var pos = $(elem).position().left;
|
||||
if (0 < pos + currentLeft && overflow < pos - currentLeft) {
|
||||
$tabsHeader.css("left", -pos);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 向右滚动到第一个在可视区域右侧的项
|
||||
$tabItems.each(function (i, elem) {
|
||||
var $elem = $(elem);
|
||||
var pos = $elem.position().left;
|
||||
if (pos + $elem.outerWidth() >= headerWidth - currentLeft) {
|
||||
$tabsHeader.css("left", -pos);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
leftPage: function () {
|
||||
events.rollPage("left");
|
||||
},
|
||||
rightPage: function () {
|
||||
events.rollPage();
|
||||
},
|
||||
closeThisTabs: function () {
|
||||
(parent === self ? admin : parent.layui.admin).closeThisTabs();
|
||||
},
|
||||
closeOtherTabs: function (type) {
|
||||
var removeClass = "LAY-system-pagetabs-remove";
|
||||
var currentIndex = admin.tabsPage.index;
|
||||
var $allTabs = $(".layui-tab-title li");
|
||||
var $allBodies = $(MAIN_BODY_ID).find("." + TABS_BODY_ITEM_CLASS);
|
||||
|
||||
if (type === "all") {
|
||||
// 关闭所有(保留第一个)
|
||||
$allTabs.filter(":gt(0)").remove();
|
||||
$allBodies.filter(":gt(0)").remove();
|
||||
$allTabs.eq(0).trigger("click");
|
||||
} else if (type === "right") {
|
||||
// 关闭右侧所有
|
||||
$allTabs.filter(":gt(" + currentIndex + ")").remove();
|
||||
$allBodies.filter(":gt(" + currentIndex + ")").remove();
|
||||
} else {
|
||||
// 关闭其他(保留当前)
|
||||
$allTabs.each(function (i) {
|
||||
if (i && i != currentIndex) {
|
||||
$(this).addClass(removeClass);
|
||||
admin.tabsBody(i).addClass(removeClass);
|
||||
}
|
||||
});
|
||||
$("." + removeClass).remove();
|
||||
}
|
||||
},
|
||||
closeRightTabs: function () {
|
||||
events.closeOtherTabs("right");
|
||||
},
|
||||
closeAllTabs: function () {
|
||||
events.closeOtherTabs("all");
|
||||
},
|
||||
shade: function () {
|
||||
admin.sideFlexible();
|
||||
},
|
||||
im: function () {
|
||||
admin.popup({
|
||||
id: "LAY-popup-layim-demo",
|
||||
shade: 0,
|
||||
area: ["800px", "300px"],
|
||||
title: "面板外的操作示例",
|
||||
offset: "lb",
|
||||
success: function () {
|
||||
layui.view(this.id).render("layim/demo").then(function () {
|
||||
layui.use("im");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化逻辑
|
||||
(function init() {
|
||||
var $tabHeaders = $("#LAY_app_tabsheader > li");
|
||||
var $win = $(window);
|
||||
var $doc = $(document);
|
||||
|
||||
// 标签页配置检查
|
||||
if (!("pageTabs" in layui.setter)) {
|
||||
layui.setter.pageTabs = true;
|
||||
}
|
||||
if (!setter.pageTabs) {
|
||||
$("#LAY_app_tabs").addClass("layui-hide");
|
||||
container.addClass("layadmin-tabspage-none");
|
||||
}
|
||||
|
||||
// IE 版本提示
|
||||
if (device.ie && device.ie < 10) {
|
||||
view.error("IE" + device.ie + "下访问可能不佳,推荐使用:Chrome / Firefox / Edge 等高级浏览器", {
|
||||
offset: "auto",
|
||||
id: "LAY_errorIE"
|
||||
});
|
||||
}
|
||||
|
||||
// 主题初始化
|
||||
var localData = layui.data(setter.appName);
|
||||
if (localData.theme) {
|
||||
admin.theme(localData.theme);
|
||||
} else if (setter.theme) {
|
||||
admin.initTheme(setter.theme.initColorIndex);
|
||||
}
|
||||
|
||||
// 标签页切换事件
|
||||
element.on("tab(" + TABS_CONTAINER_ID + ")", function (data) {
|
||||
admin.tabsPage.index = data.index;
|
||||
});
|
||||
|
||||
// 监听标签页切换,更新侧边栏菜单状态
|
||||
admin.on("tabsPage(setMenustatus)", function (data) {
|
||||
var hashObj = layui.hash("#" + admin.correctRouter(data.url));
|
||||
admin.setSideMenuStatus(hashObj);
|
||||
});
|
||||
|
||||
// 侧边栏菜单点击事件(小屏自动展开)
|
||||
element.on("nav(layadmin-system-side-menu)", function (data) {
|
||||
if (data.siblings(".layui-nav-child") && container.hasClass(SIDE_SHRINK_CLASS)) {
|
||||
admin.sideFlexible("spread");
|
||||
layer.close(data.data("index"));
|
||||
}
|
||||
admin.tabsPage.type = "nav";
|
||||
});
|
||||
|
||||
// 标签页导航点击事件
|
||||
element.on("nav(layadmin-pagetabs-nav)", function (data) {
|
||||
var $parent = $(data.elem).parent();
|
||||
$parent.removeClass("layui-this");
|
||||
$parent.parent().removeClass("layui-show");
|
||||
});
|
||||
|
||||
// 标签页头点击事件
|
||||
body.on("click", "#LAY_app_tabsheader > li", function () {
|
||||
var $this = $(this);
|
||||
var index = $this.index();
|
||||
admin.tabsPage.type = "tab";
|
||||
admin.tabsPage.index = index;
|
||||
// 切换标签页内容
|
||||
var layId = $this.attr("lay-id");
|
||||
$this.attr("lay-attr"); // 未使用
|
||||
admin.tabsBodyChange(index, {
|
||||
url: layId,
|
||||
title: $this.children("span").text()
|
||||
});
|
||||
});
|
||||
|
||||
// 标签页关闭事件
|
||||
element.on("tabDelete(" + TABS_CONTAINER_ID + ")", function (data) {
|
||||
var $activeTab = $(".layui-tab-title li.layui-this");
|
||||
if (data.index) {
|
||||
admin.tabsBody(data.index).remove();
|
||||
}
|
||||
// 切换到上一个标签
|
||||
var layId = $activeTab.attr("lay-id");
|
||||
$activeTab.attr("lay-attr"); // 未使用
|
||||
admin.tabsBodyChange($activeTab.index(), {
|
||||
url: layId,
|
||||
title: $activeTab.children("span").text()
|
||||
});
|
||||
admin.delResize();
|
||||
});
|
||||
|
||||
// 带 lay-href 属性的链接点击事件(打开新标签页)
|
||||
body.on("click", "*[lay-href]", function () {
|
||||
var $this = $(this);
|
||||
var href = $this.attr("lay-href");
|
||||
var title = $this.attr("lay-title") || $this.attr("lay-text") || $this.text();
|
||||
admin.tabsPage.elem = $this;
|
||||
(parent === self ? layui : setter.parentLayui || top.layui).ywxapp.openTabsPage({
|
||||
url: href,
|
||||
title: title
|
||||
});
|
||||
// 如果开启标签页且点击的是当前页链接,则刷新
|
||||
if (setter.pageTabs && setter.refreshCurrPage && href === admin.tabsBody(admin.tabsPage.index).find("iframe").attr("src")) {
|
||||
events.refresh();
|
||||
}
|
||||
});
|
||||
|
||||
// 自定义事件委托(layadmin-event 属性)
|
||||
body.on("click", "*[layadmin-event]", function () {
|
||||
var $this = $(this);
|
||||
var eventName = $this.attr("layadmin-event");
|
||||
if (events[eventName]) {
|
||||
events[eventName].call(this, $this);
|
||||
}
|
||||
});
|
||||
|
||||
// 提示框(lay-tips 属性)
|
||||
body.on("mouseenter", "*[lay-tips]", function () {
|
||||
var $this = $(this);
|
||||
// 侧边栏收缩状态下或非导航项才显示提示
|
||||
if (!$this.parent().hasClass("layui-nav-item") || container.hasClass(SIDE_SHRINK_CLASS)) {
|
||||
var tips = $this.attr("lay-tips");
|
||||
var offset = $this.attr("lay-offset");
|
||||
var direction = $this.attr("lay-direction");
|
||||
var tipsIndex = layer.tips(tips, this, {
|
||||
tips: direction || 1,
|
||||
time: -1,
|
||||
success: function (elem, index) {
|
||||
if (offset) {
|
||||
elem.css("margin-left", offset + "px");
|
||||
}
|
||||
}
|
||||
});
|
||||
$this.data("index", tipsIndex);
|
||||
}
|
||||
}).on("mouseleave", "*[lay-tips]", function () {
|
||||
layer.close($(this).data("index"));
|
||||
});
|
||||
|
||||
// 窗口大小变化处理(防抖)
|
||||
layui.data.resizeSystem = function () {
|
||||
layer.closeAll("tips");
|
||||
if (!layui.data.resizeSystem.lock) {
|
||||
setTimeout(function () {
|
||||
admin.sideFlexible(admin.screen() < 2 ? "" : "spread");
|
||||
delete layui.data.resizeSystem.lock;
|
||||
}, 100);
|
||||
layui.data.resizeSystem.lock = true;
|
||||
}
|
||||
};
|
||||
$win.on("resize", layui.data.resizeSystem);
|
||||
|
||||
// 全屏变化事件
|
||||
$doc.on("fullscreenchange", function () {
|
||||
events.fullscreen($('[layadmin-event="fullscreen"]'), {
|
||||
status: document.fullscreenElement
|
||||
});
|
||||
});
|
||||
|
||||
// 全局请求 Token 注入
|
||||
var reqConfig = setter.request;
|
||||
if (reqConfig.tokenName) {
|
||||
var headers = {};
|
||||
headers[reqConfig.tokenName] = layui.data(setter.appName)[reqConfig.tokenName] || "";
|
||||
table.set({ headers: headers, where: headers });
|
||||
upload.set({ headers: headers, data: headers });
|
||||
}
|
||||
})();
|
||||
|
||||
// 导出模块
|
||||
exports("ywxapp", admin);
|
||||
});
|
||||
@@ -0,0 +1,245 @@
|
||||
// 在脚本【同步执行阶段】捕获当前 <script> 标签(异步回调中 document.currentScript 会失效)。
|
||||
// 注意:appName 真正取自 ThinkPHP 注入的 layui.app(真实应用目录名),
|
||||
// 这里仅作为兜底缓存 <script module> 标签上的属性值。
|
||||
var _scriptEl = document.currentScript || document.scripts[document.scripts.length - 1];
|
||||
var _appNameFromTag = _scriptEl ? _scriptEl.getAttribute('module') : null;
|
||||
|
||||
// 【关键】在脚本顶层同步配置 layui 模块 base 目录。
|
||||
// 原因:普通业务模块(如 login,未在下方 extend 列表中)由 layui 用 base + 名称 + '.js' 解析路径。
|
||||
// 若 base 未提前配置,页面里直接 layui.use('login') 时 base 为 undefined,
|
||||
// 会请求 /当前路径/undefinedlogin.js 而 404。
|
||||
// 之前只在 define 回调里配置 base,而该回调是异步触发的(需有人 use('ywxapp') 才执行),
|
||||
// 页面 use 业务模块时它往往还没跑,导致 base 缺失。此处同步先行配置即可规避执行顺序问题。
|
||||
var _appName = (layui.app && layui.app.app) || _appNameFromTag || null;
|
||||
if (layui && _appName) {
|
||||
layui.config({ base: '/static/' + _appName + '/modules/' });
|
||||
}
|
||||
|
||||
layui.extend({
|
||||
ywxapp: "{/}/assets/ywxapp/modules/ywxapp",
|
||||
route: "{/}/assets/ywxapp/modules/route",
|
||||
view: "{/}/assets/ywxapp/modules/view",
|
||||
wxapp: "{/}/assets/ywxapp/modules/wxapp",
|
||||
auth: "{/}/assets/ywxapp/modules/auth",
|
||||
iconPicker: "{/}/assets/ywxapp/modules/iconPicker",
|
||||
ueditor: "{/}/assets/ywxapp/modules/ueditor",
|
||||
http: "{/}/assets/ywxapp/modules/http",
|
||||
wxedit: "{/}/assets/ywxapp/modules/wxedit",
|
||||
chatClient: "{/}/assets/ywxapp/modules/chat-client",
|
||||
JsonCrypto: "{/}/assets/ywxapp/modules/JsonCrypto",
|
||||
cropper: "{/}/assets/ywxapp/modules/cropper/cropper",
|
||||
croppers: "{/}/assets/ywxapp/modules/cropper/croppers",
|
||||
echartsTheme: "{/}/assets/ywxapp/modules/echartsTheme",
|
||||
echarts: "{/}/assets/ywxapp/modules/echarts",
|
||||
}).define(["all", "route"], function (exports) {
|
||||
var $ = layui.$,
|
||||
table = layui.table, http = layui.http;
|
||||
var loadIndex;
|
||||
/** 设置Ajax */
|
||||
var Setter = {
|
||||
layerArea: function () {
|
||||
const width = window.innerWidth;
|
||||
// if (width >= 1400) return ["1330px", "99%"];
|
||||
// if (width >= 1200) return ["1170px", "99%"];
|
||||
if (width >= 992) return ["970px", "99%"];
|
||||
if (width >= 768) return ["750px", "99%"];
|
||||
return ["100%", "99%"];
|
||||
},
|
||||
paths: {
|
||||
// core: layui.cache.base + 'adminui/dist/', // 核心库所在目录
|
||||
// views: '/static/backend/views/', // 动态模板所在目录
|
||||
// modules: layui.cache.base + 'modules/', // 业务模块所在目录
|
||||
// base: layui.cache.base // 记录静态资源所在基础目录
|
||||
},
|
||||
container: 'LAY_app', // 容器 ID
|
||||
entry: 'index', // 默认视图文件名
|
||||
engine: '.html', // 视图文件后缀名
|
||||
pageTabs: true, // 是否开启页面选项卡功能。 iframe 版推荐开启
|
||||
refreshCurrPage: true, // 当跳转页面 url 与当前页 url 相同时,是否自动执行刷新 --- 2.0+
|
||||
name: 'YwxApp', // 系统名称 --- 2.0+
|
||||
appName: 'admin',
|
||||
|
||||
// 记录
|
||||
record: {
|
||||
url: true // 是否开启 iframe 跳转时 url 记忆功能
|
||||
},
|
||||
debug: true, // 是否开启调试模式。如开启,接口异常时会抛出异常 URL 等信息
|
||||
// 自定义请求字段
|
||||
request: {
|
||||
tokenName: false // 自动携带 token 的字段名(如:access_token)。可设置 false 不携带。
|
||||
},
|
||||
// 配置业务模块目录中的特殊模块
|
||||
extend: {
|
||||
layim: 'layim/layim' // layim
|
||||
},
|
||||
// 主题配置
|
||||
theme: {
|
||||
// 内置主题配色方案
|
||||
color: [{
|
||||
main: '#20222A', // 主题色
|
||||
selected: '#16baaa', // 选中色
|
||||
alias: 'default' // 默认别名
|
||||
}, {
|
||||
main: '#03152A',
|
||||
selected: '#3B91FF',
|
||||
alias: 'dark-blue' // 藏蓝
|
||||
}, {
|
||||
main: '#2E241B',
|
||||
selected: '#A48566',
|
||||
alias: 'coffee' // 咖啡
|
||||
}, {
|
||||
main: '#50314F',
|
||||
selected: '#7A4D7B',
|
||||
alias: 'purple-red' // 紫红
|
||||
}, {
|
||||
main: '#344058',
|
||||
logo: '#1E9FFF',
|
||||
selected: '#1E9FFF',
|
||||
alias: 'ocean' // 海洋
|
||||
}, {
|
||||
main: '#3A3D49',
|
||||
logo: '#2F9688',
|
||||
selected: '#16b777',
|
||||
alias: 'green' // 墨绿
|
||||
}, {
|
||||
main: '#20222A',
|
||||
logo: '#F78400',
|
||||
selected: '#F78400',
|
||||
alias: 'red' // 橙色
|
||||
}, {
|
||||
main: '#28333E',
|
||||
logo: '#AA3130',
|
||||
selected: '#AA3130',
|
||||
alias: 'fashion-red' // 时尚红
|
||||
}, {
|
||||
main: '#24262F',
|
||||
logo: '#3A3D49',
|
||||
selected: '#16baaa',
|
||||
alias: 'classic-black' // 经典黑
|
||||
}, {
|
||||
logo: '#226A62',
|
||||
header: '#2F9688',
|
||||
alias: 'green-header' // 墨绿头
|
||||
}, {
|
||||
main: '#344058',
|
||||
logo: '#0085E8',
|
||||
selected: '#1E9FFF',
|
||||
header: '#1E9FFF',
|
||||
alias: 'ocean-header' // 海洋头
|
||||
}, {
|
||||
header: '#393D49',
|
||||
alias: 'classic-black-header' // 经典黑头
|
||||
}, {
|
||||
main: '#50314F',
|
||||
logo: '#50314F',
|
||||
selected: '#7A4D7B',
|
||||
header: '#50314F',
|
||||
alias: 'purple-red-header' // 紫红头
|
||||
}, {
|
||||
main: '#28333E',
|
||||
logo: '#28333E',
|
||||
selected: '#AA3130',
|
||||
header: '#AA3130',
|
||||
alias: 'fashion-red-header' // 时尚红头
|
||||
}, {
|
||||
main: '#28333E',
|
||||
logo: '#16baaa',
|
||||
selected: '#16baaa',
|
||||
header: '#16baaa',
|
||||
alias: 'green-header' // 墨绿头
|
||||
}, {
|
||||
main: '#393D49',
|
||||
logo: '#393D49',
|
||||
selected: '#16baaa',
|
||||
header: '#23262E',
|
||||
alias: 'Classic-style1' // 经典风格1
|
||||
}, {
|
||||
main: '#001529',
|
||||
logo: '#001529',
|
||||
selected: '#1890FF',
|
||||
header: '#1890FF',
|
||||
alias: 'Classic-style2' // 经典风格2
|
||||
}, {
|
||||
main: '#25282A',
|
||||
logo: '#25282A',
|
||||
selected: '#35BDB2',
|
||||
header: '#35BDB2',
|
||||
alias: 'Classic-style3' // 经典风格3
|
||||
}],
|
||||
|
||||
// 初始的颜色索引,对应上面的配色方案数组索引
|
||||
// 如果本地已经有主题色记录,则以本地记录为优先,除非请求本地数据(localStorage)
|
||||
initColorIndex: 0
|
||||
}
|
||||
};
|
||||
Setter.paths = {
|
||||
base: '/static/' + _appName + '/', // 静态资源根目录(供业务模块拼接 json 等相对资源)
|
||||
modules: '/static/' + _appName + '/modules/', // 业务模块所在目录
|
||||
views: '/static/' + _appName + '/views/', // 动态模板所在目录
|
||||
}
|
||||
Setter.common = {
|
||||
layerArea: function (url) {
|
||||
return [window.innerWidth > 1200 ? '30%' : (window.innerWidth > 768 ? '50%' : '99%'), '99%'];
|
||||
}
|
||||
};
|
||||
|
||||
let localData = layui.data(Setter.appName);
|
||||
localData.setter && (Setter = $.extend(Setter, localData.setter));
|
||||
|
||||
|
||||
/**
|
||||
* 获取Tokens
|
||||
* @returns
|
||||
*/
|
||||
Setter.getTokens = function () {
|
||||
let localData = layui.data(Setter.appName);
|
||||
return {
|
||||
access_token: localData.access_token,
|
||||
refresh_token: localData.refresh_token,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* 保存Tokens
|
||||
* @param {*} tokens
|
||||
*/
|
||||
Setter.saveTokens = function (tokens) {
|
||||
if (tokens.access_token)
|
||||
layui.data(Setter.appName, {
|
||||
key: "access_token",
|
||||
value: tokens.access_token,
|
||||
});
|
||||
if (tokens.refresh_token)
|
||||
layui.data(Setter.appName, {
|
||||
key: "refresh_token",
|
||||
value: tokens.refresh_token,
|
||||
});
|
||||
}
|
||||
Setter.setMenus = function (menus) {
|
||||
layui.data(Setter.appName, {
|
||||
key: "menus",
|
||||
value: menus,
|
||||
});
|
||||
}
|
||||
|
||||
Setter.permissions = function (permission) {
|
||||
layui.data(Setter.appName, {
|
||||
key: "permission",
|
||||
value: permission,
|
||||
});
|
||||
}
|
||||
|
||||
// table.set({
|
||||
// ajax: function (Options, type) {
|
||||
// layui.http
|
||||
// .get(Options.url, Options.data, Options)
|
||||
// .then((res) => {
|
||||
// Options.success(res);
|
||||
// })
|
||||
// .catch((res) => {
|
||||
// console.log(res);
|
||||
// //Options.error(xhr, status, error);
|
||||
// });
|
||||
// },
|
||||
// });
|
||||
exports("setter", Setter);
|
||||
});
|
||||
Reference in New Issue
Block a user