Compare commits

..

No commits in common. 'main' and 'branch__lmm' have entirely different histories.

File diff suppressed because it is too large Load Diff

@ -1,166 +1,167 @@
@charset "utf-8"; @charset "utf-8";
* { * {
box-sizing: border-box; /* 设置所有元素的盒子模型为border-box */ box-sizing: border-box;
} }
#app-menu ul { #app-menu ul {
padding: 0; /* 移除列表的默认内边距 */ padding: 0;
} }
#app-menu li { #app-menu li {
list-style: none; /* 移除列表项的默认样式 */ list-style: none;
} }
#app-menu { #app-menu {
overflow: hidden; /* 隐藏溢出的内容 */ overflow: hidden;
width: 100%; /* 设置宽度为100% */ width: 100%;
} }
.weixin-preview { .weixin-preview {
position: relative; /* 相对定位 */ position: relative;
width: 320px; /* 设置宽度 */ width: 320px;
height: 540px; /* 设置高度 */ height: 540px;
float: left; /* 左浮动 */ float: left;
margin-right: 10px; /* 右边距 */ margin-right: 10px;
border: 1px solid #e7e7eb; /* 边框颜色和宽度 */ border: 1px solid #e7e7eb;
} }
.weixin-preview a { .weixin-preview a {
text-decoration: none; /* 移除链接下划线 */ text-decoration: none;
color: #616161; /* 文字颜色 */ color: #616161;
} }
.weixin-preview .weixin-hd .weixin-title { .weixin-preview .weixin-hd .weixin-title {
color: #fff; /* 文字颜色 */ color: #fff;
font-size: 15px; /* 字体大小 */ font-size: 15px;
width: 100%; /* 宽度 */ width: 100%;
text-align: center; /* 文字居中对齐 */ text-align: center;
position: absolute; /* 绝对定位 */ position: absolute;
top: 33px; /* 顶部位置 */ top: 33px;
left: 0px; /* 左侧位置 */ left: 0px;
} }
.weixin-preview .weixin-header{ .weixin-preview .weixin-header{
text-align: center; /* 文字居中对齐 */ text-align: center;
padding: 10px 0; /* 上下内边距 */ padding: 10px 0;
background-color: #616161; /* 背景颜色 */ background-color: #616161;
color: #ffffff; /* 文字颜色 */ color: #ffffff;
} }
.weixin-preview .weixin-menu { .weixin-preview .weixin-menu {
position: absolute; /* 绝对定位 */ position: absolute;
bottom: 0; /* 底部位置 */ bottom: 0;
left: 0; /* 左侧位置 */ left: 0;
right: 0; /* 右侧位置 */ right: 0;
border-top: 1px solid #e7e7e7; /* 上边框颜色和宽度 */ border-top: 1px solid #e7e7e7;
background-position: 0 0; /* 背景图像位置 */ background-position: 0 0;
background-repeat: no-repeat; /* 背景图像不重复 */ background-repeat: no-repeat;
margin-bottom: 0px; /* 底部外边距 */ margin-bottom: 0px;
} }
/*一级*/ /*一级*/
.weixin-preview .weixin-menu .menu-item { .weixin-preview .weixin-menu .menu-item {
position: relative; /* 相对定位 */ position: relative;
float: left; /* 左浮动 */ float: left;
line-height: 50px; /* 行高 */ line-height: 50px;
height: 50px; /* 高度 */ height: 50px;
text-align: center; /* 文字居中对齐 */ text-align: center;
width: 33.33%; /* 宽度 */ width: 33.33%;
border-left: 1px solid #e7e7e7; /* 左边框颜色和宽度 */ border-left: 1px solid #e7e7e7;
cursor: pointer; /* 鼠标指针变为手型 */ cursor: pointer;
color: #616161; /* 文字颜色 */ color: #616161;
} }
/*二级*/ /*二级*/
.weixin-preview .weixin-sub-menu { .weixin-preview .weixin-sub-menu {
position: absolute; /* 绝对定位 */ position: absolute;
bottom: 60px; /* 底部位置 */ bottom: 60px;
left: 0; /* 左侧位置 */ left: 0;
right: 0; /* 右侧位置 */ right: 0;
border-top: 1px solid #d0d0d0; /* 上边框颜色和宽度 */ border-top: 1px solid #d0d0d0;
margin-bottom: 0px; /* 底部外边距 */ margin-bottom: 0px;
background: #fafafa; /* 背景颜色 */ background: #fafafa;
display: block; /* 块级显示 */ display: block;
padding: 0; /* 内边距 */ padding: 0;
} }
.weixin-preview .weixin-sub-menu .menu-sub-item { .weixin-preview .weixin-sub-menu .menu-sub-item {
line-height: 50px; /* 行高 */ line-height: 50px;
height: 50px; /* 高度 */ height: 50px;
text-align: center; /* 文字居中对齐 */ text-align: center;
width: 100%; /* 宽度 */ width: 100%;
border: 1px solid #d0d0d0; /* 边框颜色和宽度 */ border: 1px solid #d0d0d0;
border-top-width: 0px; /* 顶部边框宽度 */ border-top-width: 0px;
cursor: pointer; /* 鼠标指针变为手型 */ cursor: pointer;
position: relative; /* 相对定位 */ position: relative;
color: #616161; /* 文字颜色 */ color: #616161;
} }
.weixin-preview .weixin-sub-menu .menu-sub-item.on-drag-over{ .weixin-preview .weixin-sub-menu .menu-sub-item.on-drag-over{
border-top: 2px solid #44b549; /* 顶部边框颜色和宽度 */ border-top: 2px solid #44b549;
} }
.weixin-preview .menu-arrow { .weixin-preview .menu-arrow {
position: absolute; /* 绝对定位 */ position: absolute;
left: 50%; /* 左侧位置 */ left: 50%;
margin-left: -6px; /* 左边距调整 */ margin-left: -6px;
} }
.weixin-preview .arrow_in { .weixin-preview .arrow_in {
bottom: -4px; /* 底部位置 */ bottom: -4px;
display: inline-block; /* 行内块级显示 */ display: inline-block;
width: 0px; /* 宽度 */ width: 0px;
height: 0px; /* 高度 */ height: 0px;
border-width: 6px 6px 0px; /* 边框宽度 */ border-width: 6px 6px 0px;
border-style: solid dashed dashed; /* 边框样式 */ border-style: solid dashed dashed;
border-color: #fafafa transparent transparent; /* 边框颜色 */ border-color: #fafafa transparent transparent;
} }
.weixin-preview .arrow_out { .weixin-preview .arrow_out {
bottom: -5px; /* 底部位置 */ bottom: -5px;
display: inline-block; /* 行内块级显示 */ display: inline-block;
width: 0px; /* 宽度 */ width: 0px;
height: 0px; /* 高度 */ height: 0px;
border-width: 6px 6px 0px; /* 边框宽度 */ border-width: 6px 6px 0px;
border-style: solid dashed dashed; /* 边框样式 */ border-style: solid dashed dashed;
border-color: #d0d0d0 transparent transparent; /* 边框颜色 */ border-color: #d0d0d0 transparent transparent;
} }
.weixin-preview .menu-item .menu-item-title, .weixin-preview .menu-sub-item .menu-item-title { .weixin-preview .menu-item .menu-item-title, .weixin-preview .menu-sub-item .menu-item-title {
width: 100%; /* 宽度 */ width: 100%;
overflow: hidden; /* 内容溢出时隐藏 */ overflow: hidden;
white-space: nowrap; /* 不换行 */ white-space: nowrap;
text-overflow: ellipsis; /* 文本溢出时显示省略号 */ text-overflow: ellipsis;
box-sizing: border-box; /* 盒子模型为border-box */ box-sizing: border-box;
} }
.weixin-preview .menu-item.current, .weixin-preview .menu-sub-item.current { .weixin-preview .menu-item.current, .weixin-preview .menu-sub-item.current {
border: 1px solid #44b549; /* 边框颜色和宽度 */ border: 1px solid #44b549;
background: #fff; /* 背景颜色 */ background: #fff;
color: #44b549; /* 文字颜色 */ color: #44b549;
} }
.weixin-preview .weixin-sub-menu.show { .weixin-preview .weixin-sub-menu.show {
display: block; /* 块级显示 */ display: block;
} }
.weixin-preview .icon_menu_dot { .weixin-preview .icon_menu_dot {
/* background: url(../images/index_z354723.png) 0px -36px no-repeat; */ /* background: url(../images/index_z354723.png) 0px -36px no-repeat; */
width: 7px; /* 宽度 */ width: 7px;
height: 7px; /* 高度 */ height: 7px;
vertical-align: middle; /* 垂直居中对齐 */ vertical-align: middle;
display: inline-block; /* 行内块级显示 */ display: inline-block;
margin-right: 2px; /* 右边距 */ margin-right: 2px;
margin-top: -2px; /* 顶部外边距调整 */ margin-top: -2px;
} }
.weixin-preview .icon14_menu_add { .weixin-preview .icon14_menu_add {
/* background: url(../images/index_z354723.png) 0px 0px no-repeat; */ /* background: url(../images/index_z354723.png) 0px 0px no-repeat; */
width: 14px; /* 宽度 */ width: 14px;
height: 14px; /* 高度 */ height: 14px;
vertical-align: middle; /* 垂直居中对齐 */ vertical-align: middle;
display: inline-block; /* 行内块级显示 */ display: inline-block;
margin-top: -2px; /* 顶部外边距调整 */ margin-top: -2px;
} }
.weixin-preview li:hover .icon14_menu_add { .weixin-preview li:hover .icon14_menu_add {
@ -168,197 +169,197 @@
} }
.weixin-preview .menu-item:hover { .weixin-preview .menu-item:hover {
color: #000; /* 文字颜色 */ color: #000;
} }
.weixin-preview .menu-sub-item:hover { .weixin-preview .menu-sub-item:hover {
background: #eee; /* 背景颜色 */ background: #eee;
} }
.weixin-preview li.current:hover { .weixin-preview li.current:hover {
background: #fff; /* 背景颜色 */ background: #fff;
color: #44b549; /* 文字颜色 */ color: #44b549;
} }
/*菜单内容*/ /*菜单内容*/
.weixin-menu-detail { .weixin-menu-detail {
width: 600px; /* 宽度 */ width: 600px;
padding: 0px 20px 5px; /* 内边距 */ padding: 0px 20px 5px;
background-color: #f4f5f9; /* 背景颜色 */ background-color: #f4f5f9;
border: 1px solid #e7e7eb; /* 边框颜色和宽度 */ border: 1px solid #e7e7eb;
float: left; /* 左浮动 */ float: left;
min-height: 540px; /* 最小高度 */ min-height: 540px;
} }
.weixin-menu-detail .menu-name { .weixin-menu-detail .menu-name {
float: left; /* 左浮动 */ float: left;
height: 40px; /* 高度 */ height: 40px;
line-height: 40px; /* 行高 */ line-height: 40px;
font-size: 18px; /* 字体大小 */ font-size: 18px;
} }
.weixin-menu-detail .menu-del { .weixin-menu-detail .menu-del {
float: right; /* 右浮动 */ float: right;
height: 40px; /* 高度 */ height: 40px;
line-height: 40px; /* 行高 */ line-height: 40px;
color: #459ae9; /* 文字颜色 */ color: #459ae9;
cursor: pointer; /* 鼠标指针变为手型 */ cursor: pointer;
} }
.weixin-menu-detail .menu-input-group { .weixin-menu-detail .menu-input-group {
width: 540px; /* 宽度 */ width: 540px;
margin: 10px 0 30px 0; /* 外边距 */ margin: 10px 0 30px 0;
overflow: hidden; /* 隐藏溢出的内容 */ overflow: hidden;
} }
.weixin-menu-detail .menu-label { .weixin-menu-detail .menu-label {
float: left; /* 左浮动 */ float: left;
height: 30px; /* 高度 */ height: 30px;
line-height: 30px; /* 行高 */ line-height: 30px;
width: 80px; /* 宽度 */ width: 80px;
text-align: right; /* 文字右对齐 */ text-align: right;
} }
.weixin-menu-detail .menu-input { .weixin-menu-detail .menu-input {
float: left; /* 左浮动 */ float: left;
width: 380px; /* 宽度 */ width: 380px
} }
.weixin-menu-detail .menu-input-text { .weixin-menu-detail .menu-input-text {
border: 0px; /* 边框宽度 */ border: 0px;
outline: 0px; /* focus时的轮廓宽度 */ outline: 0px;
background: #fff; /* 背景颜色 */ background: #fff;
width: 300px; /* 宽度 */ width: 300px;
padding: 5px 0px 5px 0px; /* 内边距 */ padding: 5px 0px 5px 0px;
margin-left: 10px; /* 左边距 */ margin-left: 10px;
text-indent: 10px; /* text缩进 */ text-indent: 10px;
height: 35px; /* 高度 */ height: 35px;
} }
.weixin-menu-detail .menu-tips { .weixin-menu-detail .menu-tips {
color: #8d8d8d; /* 文字颜色 */ color: #8d8d8d;
padding-top: 4px; /* top方向的内边距 */ padding-top: 4px;
margin: 0; /* margin属性值设置为0 */ margin: 0;
} }
.weixin-menu-detail .menu-tips.cursor { .weixin-menu-detail .menu-tips.cursor {
color: #459ae9; /* 文字颜色 */ color: #459ae9;
cursor: pointer; /* mouse指针变为手型图标 */ cursor: pointer;
} }
.weixin-menu-detail .menu-input .menu-tips { .weixin-menu-detail .menu-input .menu-tips {
margin: 0 0 0 10px; /* margin属性值设置为0最后一个参数为10px */ margin: 0 0 0 10px;
} }
.weixin-menu-detail .menu-content { .weixin-menu-detail .menu-content {
padding: 16px 20px; /* Padding属性值设置为16px和20px */ padding: 16px 20px;
border: 1px solid #e7e7eb; /* Border属性值设置为1px和#e7e7eb颜色值 */ border: 1px solid #e7e7eb;
background-color: #fff; /* background-color属性值设置为白色 */ background-color: #fff;
} }
.weixin-menu-detail .menu-content .menu-input-group { .weixin-menu-detail .menu-content .menu-input-group {
margin: 0px 0 10px 0; /* margin属性值设置为0px,10px和0px */ margin: 0px 0 10px 0;
} }
.weixin-menu-detail .menu-content .menu-label { .weixin-menu-detail .menu-content .menu-label {
text-align: left; /* text对齐方式设置为左对齐 */ text-align: left;
width: 100px; /* width属性值设置为100px */ width: 100px;
} }
.weixin-menu-detail .menu-content .menu-input-text { .weixin-menu-detail .menu-content .menu-input-text {
border: 1px solid #e7e7eb; /* border属性值设置为1px和#e7e7eb颜色值 */ border: 1px solid #e7e7eb;
} }
.weixin-menu-detail .menu-content .menu-tips { .weixin-menu-detail .menu-content .menu-tips {
padding-bottom: 10px; /* bottom方向的内边距设置为10px */ padding-bottom: 10px;
} }
.weixin-menu-detail .menu-msg-content { .weixin-menu-detail .menu-msg-content {
padding: 0; /* Padding属性值设置为0 */ padding: 0;
border: 1px solid #e7e7eb; /* Border属性值设置为1px和#e7e7eb颜色值 */ border: 1px solid #e7e7eb;
background-color: #fff; /* background-color属性值设置为白色 */ background-color: #fff;
} }
.weixin-menu-detail .menu-msg-content .menu-msg-head { .weixin-menu-detail .menu-msg-content .menu-msg-head {
overflow: hidden; /* Overflow属性值设置为hidden */ overflow: hidden;
border-bottom: 1px solid #e7e7eb; /* Border属性值设置为1px和#e7e7eb颜色值 */ border-bottom: 1px solid #e7e7eb;
line-height: 38px; /* Line height属性值设置为38px */ line-height: 38px;
height: 38px; /* height属性值设置为38px */ height: 38px;
padding: 0 20px; /* Padding属性值设置为0和20px */ padding: 0 20px;
} }
.weixin-menu-detail .menu-msg-content .menu-msg-panel { .weixin-menu-detail .menu-msg-content .menu-msg-panel {
padding: 30px 50px; /* Padding属性值设置为30px和50px */ padding: 30px 50px;
} }
.weixin-menu-detail .menu-msg-content .menu-msg-select { .weixin-menu-detail .menu-msg-content .menu-msg-select {
padding: 40px 20px; /* Padding属性值设置为40px和20px */ padding: 40px 20px;
border: 2px dotted #d9dadc; /* Border属性值设置为2px虚线和#d9dadc颜色值 */ border: 2px dotted #d9dadc;
text-align: center; /* text对齐方式设置为居中对齐 */ text-align: center;
} }
.weixin-menu-detail .menu-msg-content .menu-msg-select:hover { .weixin-menu-detail .menu-msg-content .menu-msg-select:hover {
border-color: #b3b3b3; /* border颜色值设置为#b3b3b3 */ border-color: #b3b3b3;
} }
.weixin-menu-detail .menu-msg-content strong { .weixin-menu-detail .menu-msg-content strong {
display: block; /* Display属性值设置为block */ display: block;
padding-top: 3px; /* Top方向的内边距设置为3px */ padding-top: 3px;
font-weight: 400; /* font weight属性值设置为400 */ font-weight: 400;
font-style: normal; /* font style属性值设置为normal */ font-style: normal;
} }
.weixin-menu-detail .menu-msg-content .menu-msg-title { .weixin-menu-detail .menu-msg-content .menu-msg-title {
float: left; /* float属性值设置为left */ float: left;
width: 310px; /* width属性值设置为310px */ width: 310px;
height: 30px; /* height属性值设置为30px */ height: 30px;
line-height: 30px; /* line height属性值设置为30px */ line-height: 30px;
overflow: hidden; /* Overflow属性值设置为hidden */ overflow: hidden;
text-overflow: ellipsis; /* TextOverflow属性值设置为ellipsis */ text-overflow: ellipsis;
white-space: nowrap; /* WhiteSpace属性值设置为nowrap */ white-space: nowrap;
} }
.icon36_common { .icon36_common {
width: 36px; /* width属性值设置为36px */ width: 36px;
height: 36px; /* height属性值设置为36px */ height: 36px;
vertical-align: middle; /* vertical align属性值设置为middle */ vertical-align: middle;
display: inline-block; /* Display属性值设置为inline block */ display: inline-block;
} }
.icon_msg_sender { .icon_msg_sender {
margin-right: 3px; /* Right方向的外边距设置为3px */ margin-right: 3px;
margin-top: -2px; /* Top方向的外边距设置为负2px */ margin-top: -2px;
width: 20px; /* width属性值设置为20px */ width: 20px;
height: 20px; /* height属性值设置为20px */ height: 20px;
vertical-align: middle; /* vertical align属性值设置为middle */ vertical-align: middle;
display: inline-block; /* Display属性值设置为inline block */ display: inline-block;
/* background: url(../images/msg_tab_z25df2d.png) 0 -270px no-repeat; */ /* background: url(../images/msg_tab_z25df2d.png) 0 -270px no-repeat; */
} }
.weixin-btn-group { .weixin-btn-group {
text-align: center; /* text对齐方式设置为居中对齐 */ text-align: center;
width: 100%; /* width属性值设置为100% */ width: 100%;
margin: 30px 0px; /* margin属性值设置为30px和0px */ margin: 30px 0px;
overflow: hidden; /* Overflow属性值设置为hidden */ overflow: hidden;
} }
.weixin-btn-group .btn { .weixin-btn-group .btn {
width: 100px; /* width属性值设置为100px */ width: 100px;
border-radius: 0px; /* border radius属性值设置为0px */ border-radius: 0px;
} }
#material-list { #material-list {
padding: 20px; /* Padding属性值设置为20px */ padding: 20px;
overflow-y: scroll; /* Overflow y属性值设置为scroll使内容可滚动 */ overflow-y: scroll;
height: 558px; /* height属性值设置为558px */ height: 558px;
} }
#news-list { #news-list {
padding: 20px; /* Padding属性值设置为20px */ padding: 20px;
overflow-y: scroll; /* Overflow y属性值设置为scroll使内容可滚动 */ overflow-y: scroll;
height: 558px; /* height属性值设置为558px */ height: 558px;
} }
#material-list table { #material-list table {
width: 100%; /* width属性值设置为100% */ width: 100%;
} }

@ -1,11 +1,9 @@
/* 设置所有元素和伪元素的 box-sizing 为 border-box */
*, *,
*:before, *:before,
*:after { *:after {
box-sizing: border-box; box-sizing: border-box;
} }
/* 设置 body 的字体、大小、行高和颜色 */
body { body {
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
font-size: 14px; font-size: 14px;
@ -14,12 +12,10 @@ body {
background-color: #fff; background-color: #fff;
} }
/* 设置链接的颜色和文本装饰 */
a { a {
color: mix(#fff, $--color-primary, 20%); /* 使用混合色函数设置颜色 */ color: mix(#fff, $--color-primary, 20%);
text-decoration: none; /* 去掉下划线 */ text-decoration: none;
/* 当链接获得焦点或悬停时,改变颜色并添加下划线 */
&:focus, &:focus,
&:hover { &:hover {
color: $--color-primary; color: $--color-primary;
@ -27,104 +23,97 @@ a {
} }
} }
/* 设置图片垂直对齐方式为中间 */
img { img {
vertical-align: middle; vertical-align: middle;
} }
/* Utils /* Utils
------------------------------ */ ------------------------------ */
/* 清除浮动工具类 */
.clearfix:before, .clearfix:before,
.clearfix:after { .clearfix:after {
content: " "; /* 生成内容以触发 clearfix */ content: " ";
display: table; /* 设置为表格布局 */ display: table;
} }
.clearfix:after { .clearfix:after {
clear: both; /* 清除浮动 */ clear: both;
} }
/* Animation /* Animation
------------------------------ */ ------------------------------ */
/* 淡入淡出动画过渡效果 */
.fade-enter-active, .fade-enter-active,
.fade-leave-active { .fade-leave-active {
transition: opacity .5s; /* 设置透明度变化的过渡时间 */ transition: opacity .5s;
} }
.fade-enter, .fade-enter,
.fade-leave-to { .fade-leave-to {
opacity: 0; /* 初始透明度为 0 */ opacity: 0;
} }
/* Reset element-ui /* Reset element-ui
------------------------------ */ ------------------------------ */
/* 重置 element-ui 样式 */
.site-wrapper { .site-wrapper {
.el-pagination { .el-pagination {
margin-top: 15px; /* 设置分页组件的上边距 */ margin-top: 15px;
text-align: right; /* 设置分页组件的文本对齐方式 */ text-align: right;
} }
} }
/* Layout /* Layout
------------------------------ */ ------------------------------ */
/* 设置布局相关样式 */
.site-wrapper { .site-wrapper {
position: relative; /* 相对定位 */ position: relative;
min-width: 1180px; /* 最小宽度 */ min-width: 1180px;
} }
/* Sidebar fold /* Sidebar fold
------------------------------ */ ------------------------------ */
/* 侧边栏折叠样式 */
.site-sidebar--fold { .site-sidebar--fold {
/* 设置导航栏头部、品牌、侧边栏及其内部元素的宽度 */
.site-navbar__header, .site-navbar__header,
.site-navbar__brand, .site-navbar__brand,
.site-sidebar, .site-sidebar,
.site-sidebar__inner, .site-sidebar__inner,
.el-menu.site-sidebar__menu { .el-menu.site-sidebar__menu {
width: 64px; /* 宽度为 64px */ width: 64px;
} }
/* 设置导航栏主体和内容包装器的左边距 */
.site-navbar__body, .site-navbar__body,
.site-content__wrapper { .site-content__wrapper {
margin-left: 64px; /* 左边距为 64px */ margin-left: 64px;
} }
/* 设置品牌图标在不同状态下的显示方式 */
.site-navbar__brand { .site-navbar__brand {
&-lg { &-lg {
display: none; /* 大尺寸时隐藏 */ display: none;
} }
&-mini { &-mini {
display: inline-block; /* 小尺寸时内联显示 */ display: inline-block;
} }
} }
/* 设置侧边栏和其内部元素的溢出处理 */
.site-sidebar, .site-sidebar,
.site-sidebar__inner { .site-sidebar__inner {
overflow: initial; /* 初始溢出处理 */ overflow: initial;
} }
/* 设置侧边栏菜单图标的右边距和字体大小 */
.site-sidebar__menu-icon { .site-sidebar__menu-icon {
margin-right: 0; /* 右边距为 0 */ margin-right: 0;
font-size: 20px; /* 字体大小为 20px */ font-size: 20px;
} }
/* 设置标签页内容的左边距 */
.site-content--tabs > .el-tabs > .el-tabs__header { .site-content--tabs > .el-tabs > .el-tabs__header {
left: 64px; /* 左边距为 64px */ left: 64px;
} }
} }
//* animation */ // animation
.site-navbar__header, .site-navbar__header,
.site-navbar__brand, .site-navbar__brand,
.site-navbar__body, .site-navbar__body,
@ -134,154 +123,154 @@ img {
.site-sidebar__menu-icon, .site-sidebar__menu-icon,
.site-content__wrapper, .site-content__wrapper,
.site-content--tabs > .el-tabs .el-tabs__header { .site-content--tabs > .el-tabs .el-tabs__header {
//
transition: inline-block .3s, left .3s, width .3s, margin-left .3s, font-size .3s; transition: inline-block .3s, left .3s, width .3s, margin-left .3s, font-size .3s;
} }
/* Navbar */
/* Navbar
------------------------------ */
.site-navbar { .site-navbar {
//
position: fixed; position: fixed;
top: 0; top: 0;
right: 0; right: 0;
left: 0; left: 0;
z-index: 1030; // z-index: 1030;
height: 50px; // 50px height: 50px;
box-shadow: 0 2px 4px rgba(0, 0, 0, .08); // box-shadow: 0 2px 4px rgba(0, 0, 0, .08);
background-color: $navbar--background-color; // background-color: $navbar--background-color;
&--inverse { &--inverse {
.site-navbar__body { .site-navbar__body {
background-color: transparent; // background-color: transparent;
} }
.el-menu { .el-menu {
> .el-menu-item, > .el-menu-item,
> .el-submenu > .el-submenu__title { > .el-submenu > .el-submenu__title {
color: #fff; // color: #fff;
&:focus, &:focus,
&:hover { &:hover {
color: #fff; // color: #fff;
background-color: mix(#000, $navbar--background-color, 15%); // background-color: mix(#000, $navbar--background-color, 15%);
} }
} }
> .el-menu-item.is-active, > .el-menu-item.is-active,
> .el-submenu.is-active > .el-submenu__title { > .el-submenu.is-active > .el-submenu__title {
border-bottom-color: mix(#fff, $navbar--background-color, 85%); // border-bottom-color: mix(#fff, $navbar--background-color, 85%);
} }
.el-menu-item i, .el-menu-item i,
.el-submenu__title i, .el-submenu__title i,
.el-dropdown { .el-dropdown {
color: #fff; // color: #fff;
} }
} }
.el-menu--popup-bottom-start { .el-menu--popup-bottom-start {
background-color: $navbar--background-color; // background-color: $navbar--background-color;
} }
} }
&__header { &__header {
position: relative; // position: relative;
float: left; // float: left;
width: 230px; // 230px width: 230px;
height: 50px; // 50px height: 50px;
overflow: hidden; // overflow: hidden;
} }
&__brand { &__brand {
display: table-cell; // display: table-cell;
vertical-align: middle; // vertical-align: middle;
width: 230px; // 230px width: 230px;
height: 50px; // 50px height: 50px;
margin: 0; // margin: 0;
line-height: 50px; // 50px line-height: 50px;
font-size: 20px; // 20px font-size: 20px;
text-align: center; // text-align: center;
text-transform: uppercase; // text-transform: uppercase;
white-space: nowrap; // white-space: nowrap;
color: #fff; // color: #fff;
&-lg, &-lg,
&-mini { &-mini {
margin: 0 5px; // 5px margin: 0 5px;
color: #fff; // color: #fff;
&:focus, &:focus,
&:hover { &:hover {
color: #fff; // color: #fff;
text-decoration: none; // 线 text-decoration: none;
} }
} }
&-mini { &-mini {
display: none; // display: none;
} }
} }
&__switch { &__switch {
font-size: 18px; // 18px font-size: 18px;
border-bottom: none !important; // border-bottom: none !important;
} }
&__avatar { &__avatar {
border-bottom: none !important; // border-bottom: none !important;
* { * {
vertical-align: inherit; // vertical-align: inherit;
} }
.el-dropdown-link { .el-dropdown-link {
> img { > img {
width: 36px; // 36px width: 36px;
height: auto; // height: auto;
margin-right: 5px; // 5px margin-right: 5px;
border-radius: 100%; // border-radius: 100%;
vertical-align: middle; // vertical-align: middle;
} }
} }
} }
&__body { &__body {
position: relative; // position: relative;
margin-left: 230px; // 230px margin-left: 230px;
padding-right: 15px; // 15px padding-right: 15px;
background-color: #fff; // background-color: #fff;
} }
&__menu { &__menu {
float: left; // float: left;
background-color: transparent; // background-color: transparent;
border-bottom: 0; // border-bottom: 0;
&--right { &--right {
float: right; // float: right;
} }
a:focus, a:focus,
a:hover { a:hover {
text-decoration: none; // 线 text-decoration: none;
} }
.el-menu-item, .el-menu-item,
.el-submenu > .el-submenu__title { .el-submenu > .el-submenu__title {
height: 50px; // 50px height: 50px;
line-height: 50px; // 50px line-height: 50px;
} }
.el-submenu > .el-menu { .el-submenu > .el-menu {
top: 55px; // 55px top: 55px;
} }
.el-badge { .el-badge {
display: inline; // display: inline;
z-index: 2; // z2 z-index: 2;
&__content { &__content {
line-height: 16px; // 16px line-height: 16px;
} }
} }
} }
@ -291,123 +280,124 @@ img {
/* Sidebar /* Sidebar
------------------------------ */ ------------------------------ */
.site-sidebar { .site-sidebar {
position: fixed; // position: fixed;
top: 50px; // 50px top: 50px;
left: 0; // 0 left: 0;
bottom: 0; // bottom: 0;
z-index: 1020; // z z-index: 1020;
width: 230px; // 230px width: 230px;
overflow: hidden; // overflow: hidden;
&--dark, &--dark,
&--dark-popper { &--dark-popper {
background-color: $sidebar--background-color-dark; // background-color: $sidebar--background-color-dark;
.site-sidebar__menu.el-menu, .site-sidebar__menu.el-menu,
> .el-menu--popup { > .el-menu--popup {
background-color: $sidebar--background-color-dark; // background-color: $sidebar--background-color-dark;
.el-menu-item, .el-menu-item,
.el-submenu > .el-submenu__title { .el-submenu > .el-submenu__title {
color: $sidebar--color-text-dark; // color: $sidebar--color-text-dark;
&:focus, &:focus,
&:hover { &:hover {
color: mix(#fff, $sidebar--color-text-dark, 50%); // color: mix(#fff, $sidebar--color-text-dark, 50%);
background-color: mix(#fff, $sidebar--background-color-dark, 2.5%); // background-color: mix(#fff, $sidebar--background-color-dark, 2.5%);
} }
} }
.el-menu, .el-menu,
.el-submenu.is-opened { .el-submenu.is-opened {
background-color: mix(#000, $sidebar--background-color-dark, 15%); // background-color: mix(#000, $sidebar--background-color-dark, 15%);
} }
.el-menu-item.is-active, .el-menu-item.is-active,
.el-submenu.is-active > .el-submenu__title { .el-submenu.is-active > .el-submenu__title {
color: mix(#fff, $sidebar--color-text-dark, 80%); // color: mix(#fff, $sidebar--color-text-dark, 80%);
} }
} }
} }
&__inner { &__inner {
position: relative; // position: relative;
z-index: 1; // z z-index: 1;
width: 250px; // 250px width: 250px;
height: 100%; // 100% height: 100%;
padding-bottom: 15px; // 15px padding-bottom: 15px;
overflow-y: scroll; // y overflow-y: scroll;
} }
&__menu.el-menu { &__menu.el-menu {
width: 230px; // 230px width: 230px;
border-right: 0; // border-right: 0;
} }
&__menu-icon { &__menu-icon {
width: 24px; // 24px width: 24px;
margin-right: 5px; // 5px margin-right: 5px;
text-align: center; // text-align: center;
font-size: 16px; // 16px font-size: 16px;
color: inherit !important; // color: inherit !important;
} }
} }
/* Content /* Content
------------------------------ */ ------------------------------ */
.site-content { .site-content {
position: relative; // position: relative;
padding: 15px; // 15px padding: 15px;
&__wrapper { &__wrapper {
position: relative; // position: relative;
padding-top: 50px; // 50px padding-top: 50px;
margin-left: 230px; // 230px margin-left: 230px;
min-height: 100%; // 100% min-height: 100%;
background: $content--background-color; // background: $content--background-color;
} }
&--tabs { &--tabs {
padding: 55px 0 0; // 55px00 padding: 55px 0 0;
} }
> .el-tabs { > .el-tabs {
> .el-tabs__header { > .el-tabs__header {
position: fixed; // position: fixed;
top: 50px; // 50px top: 50px;
left: 230px; // 230px left: 230px;
right: 0; // 0 right: 0;
z-index: 930; // z z-index: 930;
padding: 0 55px 0 15px; // padding: 0 55px 0 15px;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .12), 0 0 6px 0 rgba(0, 0, 0, .04); // box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .12), 0 0 6px 0 rgba(0, 0, 0, .04);
background-color: #fff; // background-color: #fff;
> .el-tabs__nav-wrap { > .el-tabs__nav-wrap {
margin-bottom: 0; // 0 margin-bottom: 0;
&:after { &:after {
display: none; // display: none;
} }
} }
} }
> .el-tabs__content { > .el-tabs__content {
padding: 0 15px 15px; // padding: 0 15px 15px;
> .site-tabs__tools { > .site-tabs__tools {
position: fixed; // position: fixed;
top: 50px; // 50px top: 50px;
right: 0; // 0 right: 0;
z-index: 931; // z z-index: 931;
height: 40px; // 40px height: 40px;
padding: 0 12px; // padding: 0 12px;
font-size: 16px; // 16px font-size: 16px;
line-height: 40px; // 40px line-height: 40px;
background-color: $content--background-color; // background-color: $content--background-color;
cursor: pointer; // cursor: pointer;
.el-icon--right { .el-icon--right {
margin-left: 0; // 0 margin-left: 0;
} }
} }
} }
@ -415,8 +405,8 @@ img {
} }
.el-table__expand-icon { .el-table__expand-icon {
display: inline-block; // display: inline-block;
width: 14px; // 14px width: 14px;
vertical-align: middle; // vertical-align: middle;
margin-right: 5px; // 5px margin-right: 5px;
} }

@ -1,11 +1,12 @@
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */ /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
//* Document /* Document
========================================================================== */ ========================================================================== */
/** /**
* 1. * 1. Correct the line height in all browsers.
* 2. Windows Phone iOS IE * 2. Prevent adjustments of font size after orientation changes in
* IE on Windows Phone and in iOS.
*/ */
html { html {
@ -18,7 +19,7 @@ html {
========================================================================== */ ========================================================================== */
/** /**
* * Remove the margin in all browsers (opinionated).
*/ */
body { body {
@ -26,7 +27,7 @@ body {
} }
/** /**
* IE 9- * Add the correct display in IE 9-.
*/ */
article, article,
@ -39,7 +40,8 @@ section {
} }
/** /**
* ChromeFirefox Safari `h1` `section` `article` * Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/ */
h1 { h1 {
@ -51,8 +53,8 @@ h1 {
========================================================================== */ ========================================================================== */
/** /**
* IE 9- * Add the correct display in IE 9-.
* 1. IE * 1. Add the correct display in IE.
*/ */
figcaption, figcaption,
@ -62,7 +64,7 @@ main { /* 1 */
} }
/** /**
* IE 8 * Add the correct margin in IE 8.
*/ */
figure { figure {
@ -70,8 +72,8 @@ figure {
} }
/** /**
* 1. Firefox * 1. Add the correct box sizing in Firefox.
* 2. Edge IE * 2. Show the overflow in Edge and IE.
*/ */
hr { hr {
@ -81,8 +83,8 @@ hr {
} }
/** /**
* 1. * 1. Correct the inheritance and scaling of font size in all browsers.
* 2. `em` * 2. Correct the odd `em` font sizing in all browsers.
*/ */
pre { pre {
@ -94,8 +96,8 @@ pre {
========================================================================== */ ========================================================================== */
/** /**
* 1. IE 10 * 1. Remove the gray background on active links in IE 10.
* 2. iOS 8+ Safari 8+ 线 * 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
*/ */
a { a {
@ -104,8 +106,8 @@ a {
} }
/** /**
* 1. Chrome 57- Firefox 39- * 1. Remove the bottom border in Chrome 57- and Firefox 39-.
* 2. ChromeEdgeIEOpera Safari * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/ */
abbr[title] { abbr[title] {
@ -115,7 +117,7 @@ abbr[title] {
} }
/** /**
* Safari 6 `bolder` * Prevent the duplicate application of `bolder` by the next rule in Safari 6.
*/ */
b, b,
@ -124,7 +126,7 @@ strong {
} }
/** /**
* ChromeEdge Safari * Add the correct font weight in Chrome, Edge, and Safari.
*/ */
b, b,
@ -133,8 +135,8 @@ strong {
} }
/** /**
* 1. * 1. Correct the inheritance and scaling of font size in all browsers.
* 2. `em` * 2. Correct the odd `em` font sizing in all browsers.
*/ */
code, code,
@ -145,7 +147,7 @@ samp {
} }
/** /**
* Android 4.3- * Add the correct font style in Android 4.3-.
*/ */
dfn { dfn {
@ -153,7 +155,7 @@ dfn {
} }
/** /**
* IE 9- * Add the correct background and color in IE 9-.
*/ */
mark { mark {
@ -162,7 +164,7 @@ mark {
} }
/** /**
* * Add the correct font size in all browsers.
*/ */
small { small {
@ -170,7 +172,8 @@ small {
} }
/** /**
* `sub` `sup` * Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/ */
sub, sub,
@ -193,7 +196,7 @@ sup {
========================================================================== */ ========================================================================== */
/** /**
* IE 9- * Add the correct display in IE 9-.
*/ */
audio, audio,
@ -202,7 +205,7 @@ video {
} }
/** /**
* iOS 4-7 * Add the correct display in iOS 4-7.
*/ */
audio:not([controls]) { audio:not([controls]) {
@ -211,7 +214,7 @@ audio:not([controls]) {
} }
/** /**
* IE 10- * Remove the border on images inside links in IE 10-.
*/ */
img { img {
@ -219,20 +222,19 @@ img {
} }
/** /**
* IE * Hide the overflow in IE.
*/ */
svg:not(:root) { svg:not(:root) {
overflow: hidden; overflow: hidden;
} }
/* Forms /* Forms
========================================================================== */ ========================================================================== */
/** /**
* 1. * 1. Change the font styles in all browsers (opinionated).
* 2. Firefox Safari * 2. Remove the margin in Firefox and Safari.
*/ */
button, button,
@ -247,8 +249,8 @@ textarea {
} }
/** /**
* IE * Show the overflow in IE.
* 1. Edge * 1. Show the overflow in Edge.
*/ */
button, button,
@ -257,8 +259,8 @@ input { /* 1 */
} }
/** /**
* EdgeFirefox IE * Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Firefox * 1. Remove the inheritance of text transform in Firefox.
*/ */
button, button,
@ -267,8 +269,9 @@ select { /* 1 */
} }
/** /**
* 1. WebKit bug bug Android 4 `audio` `video` * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
* 2. iOS Safari * controls in Android 4.
* 2. Correct the inability to style clickable types in iOS and Safari.
*/ */
button, button,
@ -279,7 +282,7 @@ html [type="button"], /* 1 */
} }
/** /**
* Firefox * Remove the inner border and padding in Firefox.
*/ */
button::-moz-focus-inner, button::-moz-focus-inner,
@ -291,7 +294,7 @@ button::-moz-focus-inner,
} }
/** /**
* * Restore the focus styles unset by the previous rule.
*/ */
button:-moz-focusring, button:-moz-focusring,
@ -302,7 +305,7 @@ button:-moz-focusring,
} }
/** /**
* Firefox * Correct the padding in Firefox.
*/ */
fieldset { fieldset {
@ -310,9 +313,10 @@ fieldset {
} }
/** /**
* 1. Edge IE * 1. Correct the text wrapping in Edge and IE.
* 2. IE `fieldset` * 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. `fieldset` * 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/ */
legend { legend {
@ -325,8 +329,8 @@ legend {
} }
/** /**
* 1. IE 9+ * 1. Add the correct display in IE 9-.
* 2. ChromeFirefox Opera * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/ */
progress { progress {
@ -335,7 +339,7 @@ progress {
} }
/** /**
* IE * Remove the default vertical scrollbar in IE.
*/ */
textarea { textarea {
@ -343,8 +347,8 @@ textarea {
} }
/** /**
* 1. IE 10+ * 1. Add the correct box sizing in IE 10-.
* 2. IE 10+ * 2. Remove the padding in IE 10-.
*/ */
[type="checkbox"], [type="checkbox"],
@ -354,7 +358,7 @@ textarea {
} }
/** /**
* Chrome * Correct the cursor style of increment and decrement buttons in Chrome.
*/ */
[type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-inner-spin-button,
@ -363,8 +367,8 @@ textarea {
} }
/** /**
* 1. Chrome Safari * 1. Correct the odd appearance in Chrome and Safari.
* 2. Safari * 2. Correct the outline style in Safari.
*/ */
[type="search"] { [type="search"] {
@ -373,7 +377,7 @@ textarea {
} }
/** /**
* Chrome Safari on macOS * Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
*/ */
[type="search"]::-webkit-search-cancel-button, [type="search"]::-webkit-search-cancel-button,
@ -382,8 +386,8 @@ textarea {
} }
/** /**
* 1. iOS Safari * 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Safari `inherit` * 2. Change font properties to `inherit` in Safari.
*/ */
::-webkit-file-upload-button { ::-webkit-file-upload-button {
@ -395,8 +399,8 @@ textarea {
========================================================================== */ ========================================================================== */
/* /*
* IE 9+ * Add the correct display in IE 9-.
* 1. EdgeIE Firefox * 1. Add the correct display in Edge, IE, and Firefox.
*/ */
details, /* 1 */ details, /* 1 */
@ -405,7 +409,7 @@ menu {
} }
/* /*
* * Add the correct display in all browsers.
*/ */
summary { summary {
@ -416,7 +420,7 @@ summary {
========================================================================== */ ========================================================================== */
/** /**
* IE 9+ * Add the correct display in IE 9-.
*/ */
canvas { canvas {
@ -424,7 +428,7 @@ canvas {
} }
/** /**
* IE * Add the correct display in IE.
*/ */
template { template {
@ -435,9 +439,9 @@ template {
========================================================================== */ ========================================================================== */
/** /**
* IE 10+ * Add the correct display in IE 10-.
*/ */
[hidden] { [hidden] {
display: none; display: none;
} }

@ -3,15 +3,11 @@
$--color-primary: #409EFF; $--color-primary: #409EFF;
// Navbar // Navbar
//
$navbar--background-color: $--color-primary; $navbar--background-color: $--color-primary;
// Sidebar // Sidebar
//
$sidebar--background-color-dark: #263238; $sidebar--background-color-dark: #263238;
//
$sidebar--color-text-dark: #8a979e; $sidebar--color-text-dark: #8a979e;
// Content // Content
//
$content--background-color: #f1f4f5; $content--background-color: #f1f4f5;

@ -1,7 +1,5 @@
@import "normalize"; @import "normalize";
// normalize.css
// api: https://github.com/necolas/normalize.css/ // api: https://github.com/necolas/normalize.css/
@import "variables"; @import "variables";
// 使 //
@import "base"; @import "base";
//

@ -1,40 +1,36 @@
<template> <template>
<!-- SVG元素使用动态绑定的类名宽度和高度属性 -->
<svg :class="getClassName" :width="width" :height="height" aria-hidden="true"> <svg :class="getClassName" :width="width" :height="height" aria-hidden="true">
<!-- 使用xlink:href属性引用外部定义的SVG符号 -->
<use :xlink:href="getName"></use> <use :xlink:href="getName"></use>
</svg> </svg>
</template> </template>
<script> <script>
export default { export default {
name: 'icon-svg', // 'icon-svg' name: 'icon-svg',
props: { props: {
name: { name: {
type: String, // 'name' type: String,
required: true // 'name' required: true
}, },
className: { className: {
type: String // 'className' type: String
}, },
width: { width: {
type: String // 'width' type: String
}, },
height: { height: {
type: String // 'height' type: String
} }
}, },
computed: { computed: {
// 'getName'ID
getName() { getName() {
return `#icon-${this.name}` return `#icon-${this.name}`
}, },
// 'getClassName'
getClassName() { getClassName() {
return [ return [
'icon-svg', // 'icon-svg',
`icon-svg__${this.name}`, // 'name' `icon-svg__${this.name}`,
this.className && /\S/.test(this.className) ? `${this.className}` : '' // 'className' this.className && /\S/.test(this.className) ? `${this.className}` : ''
] ]
} }
} }
@ -42,11 +38,10 @@ export default {
</script> </script>
<style> <style>
/* 样式定义 */
.icon-svg { .icon-svg {
width: 1em; // 1em width: 1em;
height: 1em; // 1em height: 1em;
fill: currentColor; // fill: currentColor;
overflow: hidden; // overflow: hidden;
} }
</style> </style>

@ -1,11 +1,7 @@
<template> <template>
<!-- 定义一个表格列绑定属性和插槽 -->
<el-table-column :prop="prop" v-bind="$attrs"> <el-table-column :prop="prop" v-bind="$attrs">
<!-- 自定义单元格内容 -->
<template slot-scope="scope"> <template slot-scope="scope">
<!-- 点击时触发toggleHandle方法并应用样式 -->
<span @click.prevent="toggleHandle(scope.$index, scope.row)" :style="childStyles(scope.row)"> <span @click.prevent="toggleHandle(scope.$index, scope.row)" :style="childStyles(scope.row)">
<!-- 根据行数据动态设置图标类和样式 -->
<i :class="iconClasses(scope.row)" :style="iconStyles(scope.row)"></i> <i :class="iconClasses(scope.row)" :style="iconStyles(scope.row)"></i>
{{ scope.row[prop] }} {{ scope.row[prop] }}
</span> </span>
@ -14,81 +10,74 @@
</template> </template>
<script> <script>
// lodashisArray
import isArray from 'lodash/isArray' import isArray from 'lodash/isArray'
export default { export default {
name: 'table-tree-column', // name: 'table-tree-column',
props: { props: {
prop: { prop: {
type: String // type: String
}, },
treeKey: { treeKey: {
type: String, type: String,
default: 'id' // 'id' default: 'id'
}, },
parentKey: { parentKey: {
type: String, type: String,
default: 'parentId' // 'parentId' default: 'parentId'
}, },
levelKey: { levelKey: {
type: String, type: String,
default: '_level' // '_level' default: '_level'
}, },
childKey: { childKey: {
type: String, type: String,
default: 'children' // 'children' default: 'children'
} }
}, },
methods: { methods: {
//
childStyles(row) { childStyles(row) {
return { 'padding-left': (row[this.levelKey] > 1 ? row[this.levelKey] * 7 : 0) + 'px' } return { 'padding-left': (row[this.levelKey] > 1 ? row[this.levelKey] * 7 : 0) + 'px' }
}, },
//
iconClasses(row) { iconClasses(row) {
return [!row._expanded ? 'el-icon-caret-right' : 'el-icon-caret-bottom'] return [!row._expanded ? 'el-icon-caret-right' : 'el-icon-caret-bottom']
}, },
//
iconStyles(row) { iconStyles(row) {
return { 'visibility': this.hasChild(row) ? 'visible' : 'hidden' } return { 'visibility': this.hasChild(row) ? 'visible' : 'hidden' }
}, },
//
hasChild(row) { hasChild(row) {
return (isArray(row[this.childKey]) && row[this.childKey].length >= 1) || false return (isArray(row[this.childKey]) && row[this.childKey].length >= 1) || false
}, },
// //
toggleHandle(index, row) { toggleHandle(index, row) {
if (this.hasChild(row)) { if (this.hasChild(row)) {
var data = this.$parent.store.states.data.slice(0) // var data = this.$parent.store.states.data.slice(0)
data[index]._expanded = !data[index]._expanded // data[index]._expanded = !data[index]._expanded
if (data[index]._expanded) { if (data[index]._expanded) {
//
data = data.splice(0, index + 1).concat(row[this.childKey]).concat(data) data = data.splice(0, index + 1).concat(row[this.childKey]).concat(data)
} else { } else {
//
data = this.removeChildNode(data, row[this.treeKey]) data = this.removeChildNode(data, row[this.treeKey])
} }
this.$parent.store.commit('setData', data) // this.$parent.store.commit('setData', data)
this.$nextTick(() => { this.$nextTick(() => {
this.$parent.doLayout() // this.$parent.doLayout()
}) })
} }
}, },
// //
removeChildNode(data, parentId) { removeChildNode(data, parentId) {
var parentIds = isArray(parentId) ? parentId : [parentId] // parentId var parentIds = isArray(parentId) ? parentId : [parentId]
if (parentId.length <= 0) { if (parentId.length <= 0) {
return data // parentId return data
} }
var ids = [] // ID var ids = []
for (var i = 0; i < data.length; i++) { for (var i = 0; i < data.length; i++) {
if (parentIds.indexOf(data[i][this.parentKey]) !== -1 && parentIds.indexOf(data[i][this.treeKey]) === -1) { if (parentIds.indexOf(data[i][this.parentKey]) !== -1 && parentIds.indexOf(data[i][this.treeKey]) === -1) {
data[i]._expanded = false // data[i]._expanded = false
ids.push(data.splice(i, 1)[0][this.treeKey]) // ID ids.push(data.splice(i, 1)[0][this.treeKey])
i-- // i--
} }
} }
return this.removeChildNode(data, ids) // return this.removeChildNode(data, ids)
} }
} }
} }

@ -1,83 +1,77 @@
<template> <template>
<!-- 面板容器使用flex布局 --> <div class="panel flex flex-wrap">
<div class="panel flex flex-wrap">
<!-- 动态生成的标签列表 -->
<el-tag v-for="tag in dynamicTags" closable @close="handleClose(tag)" :disable-transitions="false" :key="tag"> <el-tag v-for="tag in dynamicTags" closable @close="handleClose(tag)" :disable-transitions="false" :key="tag">
{{tag}} {{tag}}
</el-tag> </el-tag>
<!-- 输入框用于添加新标签 -->
<el-input class="input-new-tag" v-if="inputVisible" v-model="inputValue" ref="saveTagInput" size="small" @keyup.enter.native="handleInputConfirm" @blur="handleInputConfirm"> <el-input class="input-new-tag" v-if="inputVisible" v-model="inputValue" ref="saveTagInput" size="small" @keyup.enter.native="handleInputConfirm" @blur="handleInputConfirm">
</el-input> </el-input>
<!-- 按钮点击后显示输入框 -->
<el-button v-else class="button-new-tag" size="small" @click="showInput">+ </el-button> <el-button v-else class="button-new-tag" size="small" @click="showInput">+ </el-button>
</div> </div>
</template> </template>
<script>
/** /**
* 标签编辑器组件 * 标签编辑器
*/ */
let touchMoved = false; // let touchMoved = false;
export default { export default {
name: 'tags-editor', // name: 'tags-editor',
props: { props: {
value: { // value: {
type: String, type: String,
required: true, required: true,
default: "" default: ""
}, },
size: { // [small:,large:] size: {//[small:,large:]
type: String, type: String,
default: 'small' default: 'small'
} }
}, },
data() { data() {
return { return {
inputVisible: false, // inputVisible: false,
inputValue: '' // inputValue: ''
} }
}, },
computed: { computed: {
dynamicTags() { // value dynamicTags() {
if (this.value != "") return this.value.split(',') if (this.value != "") return this.value.split(',')
return [] return []
} }
}, },
methods: { methods: {
handleClose(tag) { // handleClose(tag) {
let newTags = this.dynamicTags; // let newTags = this.dynamicTags;
newTags.splice(newTags.indexOf(tag), 1); // newTags.splice(newTags.indexOf(tag), 1);
this.$emit('input', newTags.join(",")); // this.$emit('input', newTags.join(","));
}, },
showInput() { // showInput() {
this.inputVisible = true; // this.inputVisible = true;
this.$nextTick(_ => { // DOM this.$nextTick(_ => {
this.$refs.saveTagInput.$refs.input.focus(); // this.$refs.saveTagInput.$refs.input.focus();
}); });
}, },
handleInputConfirm() { // handleInputConfirm() {
let inputValue = this.inputValue; // let inputValue = this.inputValue;
let newTags = this.dynamicTags; // let newTags = this.dynamicTags;
if (inputValue && newTags.indexOf(inputValue) < 0) { // if (inputValue && newTags.indexOf(inputValue) < 0) {
newTags.push(inputValue); // newTags.push(inputValue);
} }
this.inputVisible = false; // this.inputVisible = false;
this.inputValue = ''; // this.inputValue = '';
this.$emit('input', newTags.join(",")); // this.$emit('input', newTags.join(","));
} }
} }
} }
</script>
<style scoped> <style scoped>
/* 面板样式 */
.panel { .panel {
flex: 1; /* 弹性布局,占满剩余空间 */ flex: 1;
} }
/* 标签和按钮样式 */
.el-tag,.button-new-tag{ .el-tag,.button-new-tag{
margin: 5px; /* 外边距 */ margin: 5px;
} }
/* 输入框样式 */
.input-new-tag { .input-new-tag {
width: inherit; /* 继承父元素的宽度 */ width: inherit;
} }
</style> </style>

@ -1,207 +1,185 @@
<template> <template>
<!-- 模态对话框用于筛选模板消息目标用户 -->
<el-dialog title="筛选模板消息目标用户" :close-on-click-modal="false" :visible.sync="visible"> <el-dialog title="筛选模板消息目标用户" :close-on-click-modal="false" :visible.sync="visible">
<!-- 表单布局为行内绑定数据模型dataForm清除按钮可清空输入框 -->
<el-form :inline="true" :model="dataForm" ref="dataForm" clearable @keyup.enter.native="getWxUsers()"> <el-form :inline="true" :model="dataForm" ref="dataForm" clearable @keyup.enter.native="getWxUsers()">
<!-- 用户标签选择框 -->
<el-form-item> <el-form-item>
<el-select v-model="dataForm.tagid" filterable placeholder="用户标签" @change="getWxUsers()"> <el-select v-model="dataForm.tagid" filterable placeholder="用户标签" @change="getWxUsers()">
<!-- 遍历用户标签列表生成选项 -->
<el-option v-for="item in wxUserTags" :key="item.id" :label="item.name" :value="item.id+''"></el-option> <el-option v-for="item in wxUserTags" :key="item.id" :label="item.name" :value="item.id+''"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- 昵称输入框 -->
<el-form-item> <el-form-item>
<el-input v-model="dataForm.nickname" placeholder="昵称" @change="getWxUsers()" clearable></el-input> <el-input v-model="dataForm.nickname" placeholder="昵称" @change="getWxUsers()" clearable></el-input>
</el-form-item> </el-form-item>
<!-- 省份输入框 -->
<el-form-item> <el-form-item>
<el-input v-model="dataForm.province" placeholder="省份" @change="getWxUsers()" clearable></el-input> <el-input v-model="dataForm.province" placeholder="省份" @change="getWxUsers()" clearable></el-input>
</el-form-item> </el-form-item>
<!-- 城市输入框 -->
<el-form-item> <el-form-item>
<el-input v-model="dataForm.city" placeholder="城市" @change="getWxUsers()" clearable></el-input> <el-input v-model="dataForm.city" placeholder="城市" @change="getWxUsers()" clearable></el-input>
</el-form-item> </el-form-item>
<!-- 备注输入框 -->
<el-form-item> <el-form-item>
<el-input v-model="dataForm.remark" placeholder="备注" @change="getWxUsers()" clearable></el-input> <el-input v-model="dataForm.remark" placeholder="备注" @change="getWxUsers()" clearable></el-input>
</el-form-item> </el-form-item>
<!-- 扫码场景值输入框 -->
<el-form-item> <el-form-item>
<el-input v-model="dataForm.qrScene" placeholder="扫码场景值" @change="getWxUsers()" clearable></el-input> <el-input v-model="dataForm.qrScene" placeholder="扫码场景值" @change="getWxUsers()" clearable></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 显示将发送的消息预览 -->
<div class="text-bold">本消息将发送给</div> <div class="text-bold">本消息将发送给</div>
<!-- 用户列表区域加载状态时显示加载动画 -->
<div class="user-list" v-loading="wxUsersLoading"> <div class="user-list" v-loading="wxUsersLoading">
<!-- 遍历用户列表生成用户卡片 -->
<div class="user-card" v-for="item in wxUserList" :key="item.openid"> <div class="user-card" v-for="item in wxUserList" :key="item.openid">
<!-- 用户头像 -->
<el-avatar :src="item.headimgurl"></el-avatar> <el-avatar :src="item.headimgurl"></el-avatar>
<!-- 用户昵称 -->
<div class="nickname">{{item.nickname}}</div> <div class="nickname">{{item.nickname}}</div>
</div> </div>
<!-- 当用户数量超过10个时显示省略号和总用户数 -->
<div class="text-bold"> <div class="text-bold">
<span v-show="totalCount>10">...</span> <span v-show="totalCount>10">...</span>
等共<span class="text-success">{{totalCount}}</span>个用户 等共<span class="text-success">{{totalCount}}</span>个用户
</div> </div>
</div> </div>
<!-- 消息预览标题 -->
<div class="margin-top text-bold">消息预览</div> <div class="margin-top text-bold">消息预览</div>
<!-- 消息预览内容禁用编辑且自动调整大小 -->
<div class="margin-top-xs"> <div class="margin-top-xs">
<el-input type="textarea" disabled autosize v-model="msgReview" placeholder="模版"></el-input> <el-input type="textarea" disabled autosize v-model="msgReview" placeholder="模版"></el-input>
</div> </div>
<!-- 底部操作栏 -->
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
<!-- 发送按钮根据发送状态显示不同文本 -->
<el-button @click="send" type="success" :disabled="totalCount<=0 || sending">{{sending?'发送中...':'发送'}}</el-button> <el-button @click="send" type="success" :disabled="totalCount<=0 || sending">{{sending?'发送中...':'发送'}}</el-button>
<!-- 关闭按钮 -->
<el-button @click="visible=false"></el-button> <el-button @click="visible=false"></el-button>
</span> </span>
</div> </el-dialog>
</template> </template>
<script> <script>
import { mapState } from 'vuex' import { mapState } from 'vuex'
export default { export default {
name: 'template-msg', // name:'template-msg-task',
props: { props:{
// wxUserTagName:{
wxUserTagName: String, type:String,
required:false
}
}, },
data() { data(){
return { return{
visible: false, // visible:false,
wxUsersLoading: false, // wxUsersLoading:false,
dataForm: { // sending:false,
page: 1, // msgTemplate:{},
sidx: 'subscribe_time', // dataForm: {
order: 'desc', // page:1,
tagid: '', // ID sidx: 'subscribe_time',
nickname: '', // order: 'desc',
province: '', // tagid:'',
city: '', // nickname: '',
remark: '', // city:'',
qrScene: '', // province:'',
remark:'',
qrScene:''
}, },
msgTemplate: {}, // wxUserList:[],
wxUserList: [], // totalCount:0
totalCount: 0, //
} }
}, },
computed: mapState({ computed: mapState({
wxUserTags: state => state.wxUserTags.tags, // Vuex wxUserTags:state=>state.wxUserTags.tags,
msgReview(): string { // msgReview(){
if (!this.msgTemplate.data) return "" // if(!this.msgTemplate.data) return ""
let content = this.msgTemplate.content // let content = this.msgTemplate.content
this.msgTemplate.data.forEach(item => { // this.msgTemplate.data.forEach(item=>{
content = content.replace("{{" + item.name + ".DATA}}", item.value) // content = content.replace("{{"+item.name+".DATA}}",item.value)
}) })
return content // return content
} }
}), }),
mounted() { mounted() {
this.getWxUserTags().then((taglist) => { // this.getWxUserTags().then((taglist)=>{
if (this.wxUserTagName) { // if(this.wxUserTagName){
let tagItem = taglist.find(tag => tag.name === this.wxUserTagName) // let tagItem = taglist.find(tag=>tag.name==this.wxUserTagName)
if (tagItem) { // console.log(tagItem)
this.dataForm.tagid = tagItem.id + '' // IDID if(tagItem) {
this.dataForm.tagid=tagItem.id+''
} }
} }
this.getWxUsers(); // this.getWxUsers()
}); });
}, },
methods: { methods:{
init(msgTemplate) { // init(msgTemplate){
if (!msgTemplate || !msgTemplate.templateId) { // ID if(!msgTemplate || !msgTemplate.templateId){
this.$message.error('消息模板无效') // this.$message.error('消息模板无效')
return // return
} }
if (!msgTemplate.data || !(msgTemplate.data instanceof Array)) { // if(!msgTemplate.data || !(msgTemplate.data instanceof Array)){
this.$message.error('请现配置此模板填充数据') // this.$message.error('请现配置此模板填充数据')
return // return
} }
this.msgTemplate = msgTemplate // this.msgTemplate=msgTemplate
this.visible = true; // this.visible=true;
}, },
getWxUserTags() { // getWxUserTags() {
return new Promise((resolve, reject) => { // Promise return new Promise((resolve,reject)=>{
this.$http({ // HTTP this.$http({
url: this.$adorn.url('/manage/wxUserTags/list'), // URL url: this.$http.adornUrl('/manage/wxUserTags/list'),
method: 'get', // HTTPGET method: 'get',
params: this.$adorn.params({}) // }).then(({ data }) => {
}).then((response) => { // if (data && data.code === 200) {
if (response.data.code == 200) { // 200 this.$store.commit('wxUserTags/updateTags', data.list)
this.$store.dispatch('wxUserTags/updateTags', response.data.list) // Vuex resolve(data.list)
resolve(response.data.list) // Promise
} else { } else {
this.$message.error(response.data.msg) // this.$message.error(data.msg)
reject(response.data.msg) // Promise reject(data.msg)
} }
}).catch(err=>reject(err)) // Promise }).catch(err=>reject(err))
}); })
}, },
getWxUsers() { // getWxUsers() {
this.wxUsersLoading = true // true this.wxUsersLoading = true
this.$http({ // HTTP this.$http({
url: this.$adorn.url('/manage/wxUser/list'), // URL url: this.$http.adornUrl('/manage/wxUser/list'),
method: 'get', // HTTPGET method: 'get',
params: this.$adorn.params(this.dataForm) // params: this.$http.adornParams(this.dataForm)
}).then((response) => { // }).then(({ data }) => {
if (response.data.code == 200) { // 200 if (data && data.code === 200) {
this.wxUserList = response.data.page.list // this.wxUserList = data.page.list
this.totalCount = response.data.page.totalCount // this.totalCount = data.page.totalCount
} else { } else {
this.$message.error(response.data.msg) // this.$message.error(data.msg)
} }
this.wxUsersLoading = false // false this.wxUsersLoading = false
}).catch(err=>reject(err)) // Promise })
}, },
send() { // send(){
if (this.sending) return // if(this.sending)return
this.sending = true // true this.sending=true
this.$http({ // HTTP this.$http({
url: this.$adorn.url('/manage/msgTemplate/sendMsgBatch'), // URL url: this.$http.adornUrl('/manage/msgTemplate/sendMsgBatch'),
method: 'post', // HTTPPOST method: 'post',
data: this.$adorn.data({ // data:this.$http.adornData({
wxUserFilterParams: this.dataForm, // wxUserFilterParams : this.dataForm,
templateId: this.msgTemplate.templateId, // IDID templateId : this.msgTemplate.templateId,
url: this.msgTemplate.url, // URLURL url : this.msgTemplate.url,
miniprogram: this.msgTemplate.miniprogram, // miniprogram miniprogram : this.msgTemplate.miniprogram,
data: this.msgTemplate.data, // data data : this.msgTemplate.data,
}) })
}).then((response) => { // }).then(({ data }) => {
this.sending = false // false this.sending = false
if (response.data.code == 200) { // 200 if (data && data.code === 200) {
this.$message.success("消息将在后台发送") // this.$message.success("消息将在后台发送")
this.visible = false; // this.visible=false
} else { } else {
this.$message.error(response.data.msg) // this.$message.error(data.msg)
} }
}).catch(err=>reject(err)) // Promise })
} }
} }
} }
</script> </script>
<style scoped> <style scoped>
/* 用户列表样式 */
.user-list{ .user-list{
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
align-items: center; align-items: center;
} }
/* 用户卡片样式 */
.user-card{ .user-card{
overflow: hidden; overflow: hidden;
max-width: 60px; max-width: 60px;
margin: 5px; margin: 5px;
text-align: center; text-align: center;
} }
/* 昵称样式 */
.nickname{ .nickname{
color: #999999; color: #999999;
overflow: hidden; overflow: hidden;

@ -1,102 +1,99 @@
<template> <template>
<!-- 定义一个包含TinyMCE编辑器的div容器 -->
<div class="tinymce-editor"> <div class="tinymce-editor">
<!-- 使用TinyMCE组件绑定v-model为myValue初始化配置为init监听onExecCommand事件 -->
<editor v-model="myValue" :init="init" @onExecCommand="onExecCommand"></editor> <editor v-model="myValue" :init="init" @onExecCommand="onExecCommand"></editor>
</div> </div>
</template> </template>
<script>
import Editor from "@tinymce/tinymce-vue";
import Editor from "@tinymce/tinymce-vue"; // TinyMCE Vue var cos;
var cos; // cos
export default { export default {
name: "tinymce-editor", // name: "tinymce-editor",
components: { components: {
Editor // TinyMCE Editor
}, },
props: { props: {
value: { value: {
type: String, // value type: String,
default: "" // default: ""
} }
}, },
data() { data() {
return { return {
// TinyMCE
init: { init: {
language_url: "./tinymce/zh_CN.js", // language_url: "./tinymce/zh_CN.js", //public
language: "zh_CN", // language: "zh_CN",
height: 500, // height: 500,
plugins: "lists image media table paste link searchreplace anchor code preview pagebreak importcss", // plugins: "lists image media table paste link searchreplace anchor code preview pagebreak importcss",
toolbar: "undo redo searchreplace | formatselect pagebreak | bold italic forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | lists link anchor image media table | removeformat code preview", // toolbar: "undo redo searchreplace | formatselect pagebreak | bold italic forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | lists link anchor image media table | removeformat code preview", //
toolbar_drawer: false, // toolbar_drawer: false,
image_advtab: true, // image_advtab: true,
object_resizing: false, // object_resizing: false,
paste_data_images: true, // paste_data_images: true,
content_css: "./tinymce/article.css", // content_css: "./tinymce/article.css",
images_upload_handler: (blobInfo, success, failure) => { images_upload_handler: (blobInfo, success, failure) => {
//
this.uploadFile(blobInfo.blob()).then(fileUrl => success(fileUrl)).catch(err => failure(err)) this.uploadFile(blobInfo.blob()).then(fileUrl => success(fileUrl)).catch(err => failure(err))
} }
}, },
myValue: this.value, // valuemyValue myValue: this.value,
uploading: false, // uploading: false,
cosConfig: [] // cosConfig: []
}; };
}, },
mounted() { mounted() {
// console.log('tinymce-editor mounted:',this.value) // value // console.log('tinymce-editor mounted:',this.value)
tinymce.init({}); // TinyMCE tinymce.init({});
this.cosInit(); // cosInit this.cosInit();
}, },
methods: { methods: {
cosInit() { cosInit() {
// HTTP
this.$http({ this.$http({
url: this.$http.adornUrl("/sys/oss/config"), // URL url: this.$http.adornUrl("/sys/oss/config"),
method: "get", // HTTPGET method: "get",
params: this.$http.adornParams() // params: this.$http.adornParams()
}).then(({ data }) => { }).then(({ data }) => {
if (data && data.code === 200) { if (data && data.code === 200) {
this.cosConfig = data.config; // cosConfig this.cosConfig = data.config;
} else { } else {
this.$message.error("请先配置云存储相关信息!"); // this.$message.error("请先配置云存储相关信息!");
} }
}); });
}, },
onExecCommand(e) { onExecCommand(e) {
//console.log(e) // //console.log(e)
}, },
uploadFile(file) { uploadFile(file) {
this.uploading = true; // true this.uploading = true;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let formData = new FormData(); // FormData let formData = new FormData();
formData.append("file", file); // FormData formData.append("file", file);
// HTTP
this.$http({ this.$http({
url: this.$http.adornUrl('/sys/oss/upload'), // URL url: this.$http.adornUrl('/sys/oss/upload'),
method: 'post', // HTTPPOST method: 'post',
data: formData // FormData data: formData
}).then(({ data }) => { }).then(({ data }) => {
console.log(data) // console.log(data)
if (data && data.code === 200) { if (data && data.code === 200) {
this.$emit('uploaded', data.url) // uploadedURL this.$emit('uploaded', data.url)
resolve(data.url) // PromiseURL resolve(data.url)
} else { } else {
this.$message.error("文件上传失败:" + data.msg) // this.$message.error("文件上传失败:" + data.msg)
reject(data.msg) // Promise reject(data.msg)
} }
this.uploading = false; // false this.uploading = false;
}).catch(err=>reject(err)) // Promise }).catch(err=>reject(err))
}); });
} }
}, },
watch: { watch: {
value(newValue) { value(newValue) {
this.myValue = newValue; // valuemyValue this.myValue = newValue;
}, },
myValue(newValue) { myValue(newValue) {
this.$emit("input", newValue); // myValueinput this.$emit("input", newValue);
} }
} }
}; };
</script>

@ -1,46 +1,45 @@
<template> <template>
<!-- 使用Element UI的el-select组件绑定v-model为selectedAppid设置size为small并添加v-loading指令来控制加载状态 --> <el-select v-model="selectedAppid" size="small" v-loading="dataListLoading" @change="selectAccount" filterable>
<el-select v-model="selectedAppid" size="small" v-loading="dataListLoading" @change="selectAccount" filterable>
<!-- 遍历accountList数组生成el-option选项 -->
<el-option v-for="item in accountList" :key="item.appid" :label="item.name+''+ACCOUNT_TYPES[item.type]+''" :value="item.appid"></el-option> <el-option v-for="item in accountList" :key="item.appid" :label="item.name+''+ACCOUNT_TYPES[item.type]+''" :value="item.appid"></el-option>
</el-select> </el-select>
</template> </template>
<script>
import { mapState } from 'vuex' import { mapState } from 'vuex'
export default { export default {
data() { data() {
return { return {
dataListLoading: false // dataListLoading dataListLoading: false
} }
}, },
computed: mapState({ computed: mapState({
accountList: state=>state.wxAccount.accountList, // VuexaccountList accountList: state=>state.wxAccount.accountList,
ACCOUNT_TYPES: state=>state.wxAccount.ACCOUNT_TYPES, // VuexACCOUNT_TYPES ACCOUNT_TYPES: state=>state.wxAccount.ACCOUNT_TYPES,
selectedAppid:state=>state.wxAccount.selectedAppid // VuexselectedAppid selectedAppid:state=>state.wxAccount.selectedAppid
}), }),
mounted(){ mounted(){
this.getDataList() // getDataList this.getDataList()
}, },
methods:{ methods:{
getDataList() { getDataList() {
this.dataListLoading = true // dataListLoadingtrue this.dataListLoading = true
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/wxAccount/list'), // URL url: this.$http.adornUrl('/manage/wxAccount/list'),
method: 'get' // HTTPGET method: 'get'
}).then(({ data }) => { }).then(({ data }) => {
if (data && data.code === 200) { // 200 if (data && data.code === 200) {
this.$store.commit('wxAccount/updateAccountList', data.list) // mutationaccountList this.$store.commit('wxAccount/updateAccountList', data.list)
if(!data.list.length){ // if(!data.list.length){
this.$message.info("公众号列表为空,请先添加") // this.$message.info("公众号列表为空,请先添加")
} }
} }
this.dataListLoading = false // dataListLoadingfalse this.dataListLoading = false
}) })
}, },
selectAccount(appid){ selectAccount(appid){
if(this.selectedAppid!=appid){ // appidappid if(this.selectedAppid!=appid){
this.$store.commit('wxAccount/selectAccount', appid) // mutationselectedAppid this.$store.commit('wxAccount/selectAccount', appid)
} }
} }
} }
} }
</script>

@ -1,28 +1,20 @@
<template> <template>
<!-- 消息预览面板 -->
<div class="panel"> <div class="panel">
<!-- 工具提示显示消息方向 -->
<el-tooltip class="item" effect="dark" :content="msg.inOut?'公众号发出的消息':'来自用户的消息'" placement="right"> <el-tooltip class="item" effect="dark" :content="msg.inOut?'公众号发出的消息':'来自用户的消息'" placement="right">
<!-- 根据消息方向显示不同的图标和标签 --> <el-tag size="mini" v-if="msg.inOut" class="margin-right el-icon-upload2" type="info"></el-tag>
<el-tag size="mini" v-if="msg.inOut" class="margin-right el-icon-upload2" type="info"></el-tag>
<el-tag size="mini" v-else class="margin-right el-icon-download"></el-tag> <el-tag size="mini" v-else class="margin-right el-icon-download"></el-tag>
</el-tooltip> </el-tooltip>
<!-- 消息内容展示区域 -->
<span class="panel-content"> <span class="panel-content">
<!-- 文本消息类型 -->
<span v-if="msg.msgType=='text'" v-html="msg.detail.content"></span> <span v-if="msg.msgType=='text'" v-html="msg.detail.content"></span>
<!-- 事件消息类型 --> <span v-else-if="msg.msgType=='event'" >
<span v-else-if="msg.msgType=='event'">
<el-tag size="mini" type="warning" effect="plain">事件</el-tag> <el-tag size="mini" type="warning" effect="plain">事件</el-tag>
<el-tag size="mini" type="info" effect="plain">{{msg.detail.event}}</el-tag> <el-tag size="mini" type="info" effect="plain">{{msg.detail.event}}</el-tag>
{{msg.detail.eventKey}} {{msg.detail.eventKey}}
</span> </span>
<!-- 转客服事件 -->
<span v-else-if="msg.msgType=='transfer_customer_service'"> <span v-else-if="msg.msgType=='transfer_customer_service'">
<el-tag size="mini" type="warning" effect="plain">事件</el-tag> <el-tag size="mini" type="warning" effect="plain">事件</el-tag>
<el-tag size="mini" type="info" effect="plain">消息转客服</el-tag> <el-tag size="mini" type="info" effect="plain">消息转客服</el-tag>
</span> </span>
<!-- 其他消息类型 -->
<span v-else> <span v-else>
<el-tag size="mini" effect="plain">{{XmlMsgType[msg.msgType]}}</el-tag> <el-tag size="mini" effect="plain">{{XmlMsgType[msg.msgType]}}</el-tag>
后台不支持预览 后台不支持预览
@ -30,25 +22,21 @@
</span> </span>
</div> </div>
</template> </template>
<script> <script>
import { mapState } from 'vuex' import { mapState } from 'vuex'
export default { export default {
name:'wx-msg-preview', // name:'wx-msg-preview',
props:{ props:{
msg:Object // msg:Object
}, },
computed:mapState({ computed:mapState({
// VuexXmlMsgType
XmlMsgType:state=>state.message.XmlMsgType, XmlMsgType:state=>state.message.XmlMsgType,
}) })
} }
</script> </script>
<style scoped> <style scoped>
/* 面板样式 */
.panel,.panel a{ .panel,.panel a{
color: #999; // color: #999;
word-break: break-all; // word-break: break-all;
} }
</style> </style>

@ -1,157 +1,139 @@
<template> <template>
<!-- 模态框用于显示公众号用户标签管理界面 -->
<el-dialog title="公众号用户标签管理" :close-on-click-modal="false" :visible.sync="dialogVisible"> <el-dialog title="公众号用户标签管理" :close-on-click-modal="false" :visible.sync="dialogVisible">
<!-- 面板容器包含标签和输入框 --> <div class="panel flex flex-wrap" v-loading="submitting">
<div class="panel flex flex-wrap" v-loading="submitting">
<!-- 遍历并显示所有标签 -->
<el-tag v-for="tag in wxUserTags" closable @click="editTag(tag.id,tag.name)" @close="deleteTag(tag.id)" :disable-transitions="false" :key="tag.id"> <el-tag v-for="tag in wxUserTags" closable @click="editTag(tag.id,tag.name)" @close="deleteTag(tag.id)" :disable-transitions="false" :key="tag.id">
{{tag.id}} {{tag.name}} {{tag.id}} {{tag.name}}
</el-tag> </el-tag>
<!-- 当inputVisible为true时显示输入框否则显示添加按钮 -->
<el-input class="input-new-tag" v-if="inputVisible" placeholder="回车确认" v-model="inputValue" ref="saveTagInput" size="small" @keyup.enter.native="addTag"> <el-input class="input-new-tag" v-if="inputVisible" placeholder="回车确认" v-model="inputValue" ref="saveTagInput" size="small" @keyup.enter.native="addTag">
</el-input> </el-input>
<el-button v-else class="button-new-tag" size="small" @click="showInput">+ </el-button> <el-button v-else class="button-new-tag" size="small" @click="showInput">+ </el-button>
</div> </div>
<!-- 对话框底部的关闭按钮 -->
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible=false"></el-button> <el-button @click="dialogVisible=false"></el-button>
</span> </span>
</el-dialog> </el-dialog>
</template> </template>
<script>
import { mapState } from 'vuex' import { mapState } from 'vuex'
export default { export default {
name: 'wx-user-tags-manager', // name: 'wx-user-tags-manager',
props: { props: {
visible: { visible: {
type: Boolean, // type: Boolean,
default: true // true default: true
} }
}, },
data() { data() {
return { return {
dialogVisible: false, // dialogVisible:false,
inputVisible: false, // inputVisible: false,
inputValue: '', // inputValue: '',
submitting: false, // submitting:false,
} }
}, },
computed: mapState({ computed: mapState({
// VuexwxUserTags wxUserTags:state=>state.wxUserTags.tags
wxUserTags: state => state.wxUserTags.tags
}), }),
mounted() { mounted() {
//
this.getWxUserTags(); this.getWxUserTags();
}, },
methods: { methods: {
//
show(){ show(){
this.dialogVisible = true; this.dialogVisible=true;
}, },
//
getWxUserTags() { getWxUserTags() {
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/wxUserTags/list'), // URL url: this.$http.adornUrl('/manage/wxUserTags/list'),
method: 'get', // HTTPGET method: 'get',
}).then(({ data }) => { }).then(({ data }) => {
if (data && data.code === 200) { if (data && data.code === 200) {
// Vuex
this.$store.commit('wxUserTags/updateTags', data.list) this.$store.commit('wxUserTags/updateTags', data.list)
} else { } else {
//
this.$message.error(data.msg) this.$message.error(data.msg)
} }
}) })
}, },
//
deleteTag(tagid) { deleteTag(tagid) {
if(this.submitting){ if(this.submitting){
return // return
} }
this.$confirm(`确定删除标签?`, '提示', { this.$confirm(`确定删除标签?`, '提示', {
confirmButtonText: '确定', // confirmButtonText: '确定',
cancelButtonText: '取消', // cancelButtonText: '取消',
type: 'warning' // type: 'warning'
}).then(() => { }).then(() => {
this.submitting = true // true this.submitting=true
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/wxUserTags/delete/' + tagid), // URL url: this.$http.adornUrl('/manage/wxUserTags/delete/'+tagid),
method: 'post', // HTTPPOST method: 'post',
}).then(({ data }) => { }).then(({ data }) => {
if (data && data.code === 200) { if (data && data.code === 200) {
this.getWxUserTags(); // this.getWxUserTags();
this.$emit('change'); // change this.$emit('change');
} else { } else {
this.$message.error(data.msg) // this.$message.error(data.msg)
} }
this.submitting = false; // this.submitting=false;
}) })
}) })
}, },
//
showInput() { showInput() {
this.inputVisible = true; // this.inputVisible = true;
this.$nextTick(_ => { this.$nextTick(_ => {
this.$refs.saveTagInput.$refs.input.focus(); // this.$refs.saveTagInput.$refs.input.focus();
}); });
}, },
//
addTag() { addTag() {
let newTagName = this.inputValue; // let newTagName = this.inputValue;
this.saveTag(newTagName) // this.saveTag(newTagName)
this.inputVisible = false; // this.inputVisible = false;
this.inputValue = ''; // this.inputValue = '';
}, },
// editTag(tagid,orignName=''){
editTag(tagid, orignName=''){
this.$prompt('请输入新标签名称', '提示', { this.$prompt('请输入新标签名称', '提示', {
confirmButtonText: '确定', // confirmButtonText: '确定',
cancelButtonText: '取消', // cancelButtonText: '取消',
inputValue: orignName, // inputValue:orignName,
inputPattern: /^.{1,30}$/, // 1-30 inputPattern: /^.{1,30}$/,
inputErrorMessage: '名称1-30字符' // inputErrorMessage: '名称1-30字符'
}).then(({ value }) => { }).then(({ value }) => {
this.saveTag(value, tagid) // console.log(value)
this.saveTag(value,tagid)
}) })
}, },
// saveTag(name,tagid){
saveTag(name, tagid){
if(this.submitting){ if(this.submitting){
return // return
} }
this.submitting = true // true this.submitting=true
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/wxUserTags/save'), // URL url: this.$http.adornUrl('/manage/wxUserTags/save'),
method: 'post', // HTTPPOST method: 'post',
data: this.$http.adornData({ data:this.$http.adornData({
id: tagid ? tagid : undefined, // tagid id : tagid?tagid:undefined,
name: name // name : name
}) })
}).then(({ data }) => { }).then(({ data }) => {
if (data && data.code === 200) { if (data && data.code === 200) {
this.getWxUserTags(); // this.getWxUserTags();
this.$emit('change'); // change this.$emit('change');
} else { } else {
this.$message.error(data.msg) // this.$message.error(data.msg)
} }
this.submitting = false; // this.submitting=false;
}) })
} }
} }
} }
</script>
<style scoped> <style scoped>
/* 面板样式 */
.panel { .panel {
flex: 1; /* 弹性布局,占满剩余空间 */ flex: 1;
} }
/* 标签和按钮样式 */ .el-tag,.button-new-tag {
.el-tag, .button-new-tag { margin: 5px;
margin: 5px; /* 外边距 */
} }
/* 输入框样式 */
.input-new-tag { .input-new-tag {
width: inherit; /* 宽度继承父元素 */ width: inherit;
} }
</style> </style>

@ -1,3 +1 @@
// 导出一个函数,该函数接收一个文件名作为参数
// 返回一个动态导入模块的函数
module.exports = file => () => import('@/views/' + file + '.vue') module.exports = file => () => import('@/views/' + file + '.vue')

@ -10,76 +10,75 @@ import http from '@/utils/httpRequest'
import { isURL } from '@/utils/validate' import { isURL } from '@/utils/validate'
import { clearLoginInfo } from '@/utils' import { clearLoginInfo } from '@/utils'
// 使用VueRouter插件
Vue.use(VueRouter) Vue.use(VueRouter)
// 动态导入视图组件的函数
const _import = require('./import-views') const _import = require('./import-views')
// 全局路由(无需嵌套上左右整体布局) // 全局路由(无需嵌套上左右整体布局)
const globalRoutes = [ const globalRoutes = [
{ path: '/404', component: () => import('@/views/common/404'), name: '404', meta: { title: '404未找到' } }, // 404页面路由 { path: '/404', component: () => import('@/views/common/404'), name: '404', meta: { title: '404未找到' } },
{ path: '/login', component: () => import('@/views/common/login'), name: 'login', meta: { title: '登录' } } // 登录页面路由 { path: '/login', component: () => import('@/views/common/login'), name: 'login', meta: { title: '登录' } }
] ]
// 主入口路由(需嵌套上左右整体布局) // 主入口路由(需嵌套上左右整体布局)
const mainRoutes = { const mainRoutes = {
path: '/', path: '/',
component: () => import('@/views/main'), component: () => import('@/views/main'),
name: 'main', name: 'main',
redirect: { name: 'home' }, redirect: { name: 'home' },
meta: { title: '主入口整体布局' }, meta: { title: '主入口整体布局' },
children: [ children: [
// 通过meta对象设置路由展示方式 // 通过meta对象设置路由展示方式
// 1. isTab: 是否通过tab展示内容, true: 是, false: 否 // 1. isTab: 是否通过tab展示内容, true: 是, false: 否
// 2. iframeUrl: 是否通过iframe嵌套展示内容, '以http[s]://开头': 是, '': 否 // 2. iframeUrl: 是否通过iframe嵌套展示内容, '以http[s]://开头': 是, '': 否
{ path: '/home', component: () => import('@/views/common/home'), name: 'home', meta: { title: '首页' } }, // 首页路由 // 提示: 如需要通过iframe嵌套展示内容, 但不通过tab打开, 请自行创建组件使用iframe处理!
{ path: '/theme', component: () => import('@/views/common/theme'), name: 'theme', meta: { title: '主题' } } // 主题页面路由 { path: '/home', component: () => import('@/views/common/home'), name: 'home', meta: { title: '首页' } },
], { path: '/theme', component: () => import('@/views/common/theme'), name: 'theme', meta: { title: '主题' } },
beforeEnter(to, from, next) { ],
// 检查用户是否已登录,如果未登录则重定向到登录页 beforeEnter(to, from, next) {
let token = Vue.cookie.get('token') let token = Vue.cookie.get('token')
if (!token || !/\S/.test(token)) { if (!token || !/\S/.test(token)) {
clearLoginInfo() clearLoginInfo()
next({ name: 'login' }) next({ name: 'login' })
}
next()
} }
next()
}
} }
// 创建VueRouter实例
const router = new VueRouter({ const router = new VueRouter({
mode: 'hash', // 使用hash模式 mode: 'hash',
scrollBehavior: () => ({ y: 0 }), // 滚动行为:切换路由时滚动到顶部 scrollBehavior: () => ({ y: 0 }),
isAddDynamicMenuRoutes: false, // 是否已经添加动态(菜单)路由 isAddDynamicMenuRoutes: false, // 是否已经添加动态(菜单)路由
routes: globalRoutes.concat(mainRoutes) // 合并全局路由和主入口路由 routes: globalRoutes.concat(mainRoutes)
}) })
// 全局前置守卫,用于处理动态菜单路由的添加
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
// 如果已经添加了动态(菜单)路由或当前路由是全局路由,直接放行 // 添加动态(菜单)路由
if (router.options.isAddDynamicMenuRoutes || fnCurrentRouteType(to, globalRoutes) === 'global') { // 1. 已经添加 or 全局路由, 直接访问
// 2. 获取菜单列表, 添加并保存本地存储
if (router.options.isAddDynamicMenuRoutes || fnCurrentRouteType(to, globalRoutes) === 'global') {
next()
} else {
http({
url: http.adornUrl('/sys/menu/nav'),
method: 'get',
params: http.adornParams()
}).then(({ data }) => {
if (data && data.code === 200) {
fnAddDynamicMenuRoutes(data.menuList)
router.options.isAddDynamicMenuRoutes = true
sessionStorage.setItem('menuList', JSON.stringify(data.menuList || '[]'))
sessionStorage.setItem('permissions', JSON.stringify(data.permissions || '[]'))
next({ ...to, replace: true })
} else {
sessionStorage.setItem('menuList', '[]')
sessionStorage.setItem('permissions', '[]')
next() next()
} else { }
// 否则,请求菜单列表并添加动态(菜单)路由 }).catch((e) => {
http({ console.log(`%c${e} 请求菜单列表和权限失败,跳转至登录页!!`, 'color:blue')
url: http.adornUrl('/sys/menu/nav'), router.push({ name: 'login' })
method: 'get', })
params: http.adornParams() }
}).then(({ data }) => {
if (data && data.code === 200) {
fnAddDynamicMenuRoutes(data.menuList) // 添加动态(菜单)路由
router.options.isAddDynamicMenuRoutes = true // 标记为已添加动态(菜单)路由
next({ ...to, replace: true }) // 重新导航到当前路由,确保添加完动态路由后能正确显示页面
} else {
console.log(`%c${e} 请求菜单列表和权限失败,跳转至登录页!!`, 'color:blue') // 打印错误信息
router.push({ name: 'login' }) // 跳转到登录页
}
}).catch((e) => {
console.log(`%c${e} 请求菜单列表和权限失败,跳转至登录页!!`, 'color:blue') // 打印错误信息
router.push({ name: 'login' }) // 跳转到登录页
})
}
}) })
/** /**
@ -87,15 +86,15 @@ router.beforeEach((to, from, next) => {
* @param {*} route 当前路由 * @param {*} route 当前路由
*/ */
function fnCurrentRouteType(route, globalRoutes = []) { function fnCurrentRouteType(route, globalRoutes = []) {
var temp = [] var temp = []
for (var i = 0; i < globalRoutes.length; i++) { for (var i = 0; i < globalRoutes.length; i++) {
if (route.path === globalRoutes[i].path) { if (route.path === globalRoutes[i].path) {
return 'global' // 如果是全局路由,返回'global' return 'global'
} else if (globalRoutes[i].children && globalRoutes[i].children.length >= 1) { } else if (globalRoutes[i].children && globalRoutes[i].children.length >= 1) {
temp = temp.concat(globalRoutes[i].children) // 如果有子路由,添加到临时数组中 temp = temp.concat(globalRoutes[i].children)
}
} }
return temp.length >= 1 ? fnCurrentRouteType(route, temp) : 'main' // 递归判断,直到找到匹配的路由类型 }
return temp.length >= 1 ? fnCurrentRouteType(route, temp) : 'main'
} }
/** /**
@ -104,49 +103,52 @@ function fnCurrentRouteType(route, globalRoutes = []) {
* @param {*} routes 递归创建的动态(菜单)路由 * @param {*} routes 递归创建的动态(菜单)路由
*/ */
function fnAddDynamicMenuRoutes(menuList = [], routes = []) { function fnAddDynamicMenuRoutes(menuList = [], routes = []) {
var temp = [] var temp = []
for (var i = 0; i < menuList.length; i++) { for (var i = 0; i < menuList.length; i++) {
if (menuList[i].list && menuList[i].list.length >= 1) { if (menuList[i].list && menuList[i].list.length >= 1) {
temp = temp.concat(menuList[i].list) // 如果菜单项有子菜单,添加到临时数组中 temp = temp.concat(menuList[i].list)
} else if (menuList[i].url && /\S/.test(menuList[i].url)) { } else if (menuList[i].url && /\S/.test(menuList[i].url)) {
menuList[i].url = menuList[i].url.replace(/^\//, '') // 去掉URL前的斜杠 menuList[i].url = menuList[i].url.replace(/^\//, '')
var route = { var route = {
path: menuList[i].url.replace('/', '-'), // 将URL中的斜杠替换为短横线作为路径的一部分 path: menuList[i].url.replace('/', '-'),
component: null, // 初始化组件为空 component: null,
name: menuList[i].url.replace('/', '-'), // 将URL中的斜杠替换为短横线作为路由名称的一部分 name: menuList[i].url.replace('/', '-'),
meta: { meta: {
menuId: menuList[i].menuId, // 菜单ID menuId: menuList[i].menuId,
title: menuList[i].name, // 菜单名称 title: menuList[i].name,
isDynamic: true, // 标记为动态路由 isDynamic: true,
isTab: true, // 标记为标签页 isTab: true,
iframeUrl: '' // 初始化iframeUrl为空 iframeUrl: ''
}
}
// 如果URL以http[s]://开头通过iframe展示
if (isURL(menuList[i].url)) {
route['path'] = `i-${menuList[i].menuId}` // 修改路径为i-加上菜单ID
route['name'] = `i-${menuList[i].menuId}` // 修改名称为i-加上菜单ID
route['meta']['iframeUrl'] = menuList[i].url // 设置iframeUrl为菜单URL
} else {
try {
route['component'] = _import(`modules/${menuList[i].url}`) || null // 动态导入组件
} catch (e) { } // 如果导入失败,捕获异常但不做处理
}
routes.push(route) // 将路由添加到routes数组中
} }
}
// url以http[s]://开头, 通过iframe展示
if (isURL(menuList[i].url)) {
route['path'] = `i-${menuList[i].menuId}`
route['name'] = `i-${menuList[i].menuId}`
route['meta']['iframeUrl'] = menuList[i].url
} else {
try {
route['component'] = _import(`modules/${menuList[i].url}`) || null
// route['component'] = ()=>import(`@/views/modules/${menuList[i].url}.vue`) || null
} catch (e) { }
}
routes.push(route)
} }
if (temp.length >= 1) { }
fnAddDynamicMenuRoutes(temp, routes) // 如果临时数组中有数据,递归调用自身继续处理 if (temp.length >= 1) {
} else { fnAddDynamicMenuRoutes(temp, routes)
mainRoutes.name = 'main-dynamic' // 修改主入口路由的名称为main-dynamic } else {
mainRoutes.children = routes // 将动态生成的路由设置为主入口路由的子路由 mainRoutes.name = 'main-dynamic'
router.addRoutes([mainRoutes, { path: '*', redirect: { name: '404' } }]) // 添加动态路由和404重定向规则到路由器中 mainRoutes.children = routes
sessionStorage.setItem('dynamicMenuRoutes', JSON.stringify(mainRoutes.children || '')) // 将动态路由存储到sessionStorage中以便后续使用 router.addRoutes([
console.log('\n') mainRoutes,
console.log('%c!<-------------------- 动态(菜单)路由 s -------------------->', 'color:blue') // 打印动态路由开始标志 { path: '*', redirect: { name: '404' } }
console.log(mainRoutes.children) // 打印动态生成的路由信息 ])
console.log('%c!<-------------------- 动态(菜单)路由 e -------------------->', 'color:blue') // 打印动态路由结束标志 sessionStorage.setItem('dynamicMenuRoutes', JSON.stringify(mainRoutes.children || '[]'))
console.log('\n') console.log('\n')
} console.log('%c!<-------------------- 动态(菜单)路由 s -------------------->', 'color:blue')
console.log(mainRoutes.children)
console.log('%c!<-------------------- 动态(菜单)路由 e -------------------->', 'color:blue')
}
} }
export default router // 导出路由器实例 export default router

@ -1,7 +1,5 @@
import Vue from 'vue' import Vue from 'vue'
import Vuex from 'vuex' import Vuex from 'vuex'
// 引入各个模块的 Vuex 状态管理文件
import common from './modules/common' import common from './modules/common'
import user from './modules/user' import user from './modules/user'
import article from './modules/article' import article from './modules/article'
@ -9,26 +7,18 @@ import message from './modules/message'
import wxUserTags from './modules/wxUserTags' import wxUserTags from './modules/wxUserTags'
import wxAccount from './modules/wxAccount' import wxAccount from './modules/wxAccount'
// 注册 Vuex 插件,使其可用于 Vue 中
Vue.use(Vuex) Vue.use(Vuex)
export default new Vuex.Store({ export default new Vuex.Store({
// 使用 modules 来组织不同的子模块 modules: {
modules: { common,
// 引入并注册各个模块 user,
common, // 公共模块,可能用于存储一些通用的状态 article,
user, // 用户模块,存储用户信息相关的状态 message,
article, // 文章模块,存储与文章相关的状态 wxUserTags,
message, // 消息模块,存储消息相关的状态 wxAccount
wxUserTags, // 微信用户标签模块,管理微信用户标签的状态 },
wxAccount // 微信账号模块,管理微信账号相关的状态 mutations: {
}, },
strict: true
// mutations 用于同步修改状态,这里没有定义任何 mutations可根据需求进行扩展
mutations: {
// 这里可以添加全局的 mutation但目前没有定义
},
// 启用严格模式,开发环境下会对状态的修改进行检查,确保只能通过 mutation 修改状态
strict: true
}) })

@ -1,14 +1,12 @@
export default { export default {
// 启用命名空间使得该模块的状态和getters、actions、mutations是注册在全局命名空间下的子模块 namespaced: true,
namespaced: true, state: {
state: { ARTICLE_TYPES: {
// 定义文章类型常量对象 1: '普通文章',
ARTICLE_TYPES: { 5: '帮助中心',
1: '普通文章', // 普通文章类型
5: '帮助中心', // 帮助中心类型
}
},
mutations: {
// 目前没有定义任何mutation
} }
},
mutations: {
}
} }

@ -1,7 +1,6 @@
import router from '@/router' import router from '@/router'
export default { export default {
// 启用命名空间使得该模块的状态和getters、actions、mutations是注册在全局命名空间下的子模块
namespaced: true, namespaced: true,
state: { state: {
// 页面文档可视高度(随窗口改变大小) // 页面文档可视高度(随窗口改变大小)
@ -12,60 +11,47 @@ export default {
sidebarLayoutSkin: 'dark', sidebarLayoutSkin: 'dark',
// 侧边栏, 折叠状态 // 侧边栏, 折叠状态
sidebarFold: false, sidebarFold: false,
// 侧边栏, 菜单列表 // 侧边栏, 菜单
menuList: [], menuList: [],
// 当前激活的菜单项名称
menuActiveName: '', menuActiveName: '',
// 内容区域是否需要刷新 // 内容, 是否需要刷新
contentIsNeedRefresh: false, contentIsNeedRefresh: false,
// 主入口标签页数组 // 主入口标签页
mainTabs: [], mainTabs: [],
// 当前激活的标签页名称
mainTabsActiveName: '' mainTabsActiveName: ''
}, },
mutations: { mutations: {
// 更新文档可视高度
updateDocumentClientHeight(state, height) { updateDocumentClientHeight(state, height) {
state.documentClientHeight = height state.documentClientHeight = height
}, },
// 更新导航条布局类型
updateNavbarLayoutType(state, type) { updateNavbarLayoutType(state, type) {
state.navbarLayoutType = type state.navbarLayoutType = type
}, },
// 更新侧边栏布局皮肤
updateSidebarLayoutSkin(state, skin) { updateSidebarLayoutSkin(state, skin) {
state.sidebarLayoutSkin = skin state.sidebarLayoutSkin = skin
}, },
// 更新侧边栏折叠状态
updateSidebarFold(state, fold) { updateSidebarFold(state, fold) {
state.sidebarFold = fold state.sidebarFold = fold
}, },
// 更新菜单列表
updateMenuList(state, list) { updateMenuList(state, list) {
state.menuList = list state.menuList = list
}, },
// 更新当前激活的菜单项名称
updateMenuActiveName(state, name) { updateMenuActiveName(state, name) {
state.menuActiveName = name state.menuActiveName = name
}, },
// 更新内容区域是否需要刷新
updateContentIsNeedRefresh(state, status) { updateContentIsNeedRefresh(state, status) {
state.contentIsNeedRefresh = status state.contentIsNeedRefresh = status
}, },
// 更新主入口标签页数组
updateMainTabs(state, tabs) { updateMainTabs(state, tabs) {
state.mainTabs = tabs state.mainTabs = tabs
}, },
// 更新当前激活的标签页名称
updateMainTabsActiveName(state, name) { updateMainTabsActiveName(state, name) {
state.mainTabsActiveName = name state.mainTabsActiveName = name
}, },
// 移除指定的标签页
removeTab(state, tabName) { removeTab(state, tabName) {
// 过滤掉要删除的标签页
state.mainTabs = state.mainTabs.filter(item => item.name !== tabName) state.mainTabs = state.mainTabs.filter(item => item.name !== tabName)
if (state.mainTabs.length >= 1) { if (state.mainTabs.length >= 1) {
// 如果当前选中的标签页被删除,则跳转到最后一个标签页 // 当前选中tab被删除
if (tabName === state.mainTabsActiveName) { if (tabName === state.mainTabsActiveName) {
var tab = state.mainTabs[state.mainTabs.length - 1] var tab = state.mainTabs[state.mainTabs.length - 1]
router.push({ name: tab.name, query: tab.query, params: tab.params }, () => { router.push({ name: tab.name, query: tab.query, params: tab.params }, () => {
@ -73,14 +59,12 @@ export default {
}) })
} }
} else { } else {
// 如果没有剩余的标签页,重置菜单并跳转到首页
state.menuActiveName = '' state.menuActiveName = ''
router.push({ name: 'home' }) router.push({ name: 'home' })
} }
}, },
// 关闭当前激活的标签页
closeCurrentTab(state) { closeCurrentTab(state) {
this.commit('common/removeTab', state.mainTabsActiveName) this.commit('common/removeTab', state.mainTabsActiveName)
} }
} }
} }

@ -1,39 +1,32 @@
// 导出一个默认的模块对象
export default { export default {
// 启用命名空间使得该模块的状态和getters、actions、mutations是注册在全局命名空间下的子模块
namespaced: true, namespaced: true,
// 定义模块的初始状态
state: { state: {
// 定义微信消息类型的映射关系 XmlMsgType:{
XmlMsgType: { "text":"文字",
"text": "文字", // 文本消息类型 "image":"图片",
"image": "图片", // 图片消息类型 "voice":"语音",
"voice": "语音", // 语音消息类型 "shortvideo":"短视频",
"shortvideo": "短视频", // 短视频消息类型 "video":"视频",
"video": "视频", // 视频消息类型 "news":"图文",
"news": "图文", // 图文消息类型 "music":"音乐",
"music": "音乐", // 音乐消息类型 "location":"位置",
"location": "位置", // 位置消息类型 "link":"链接",
"link": "链接", // 链接消息类型 "event":"事件",
"event": "事件", // 事件消息类型 "transfer_customer_service":"转客服"
"transfer_customer_service": "转客服" // 转接客服消息类型
}, },
// 定义客服消息类型的映射关系
KefuMsgType: { KefuMsgType: {
"text": "文本消息", // 文本消息类型 "text": "文本消息",
"image": "图片消息", // 图片消息类型 "image": "图片消息",
"voice": "语音消息", // 语音消息类型 "voice": "语音消息",
"video": "视频消息", // 视频消息类型 "video": "视频消息",
"music": "音乐消息", // 音乐消息类型 "music": "音乐消息",
"news": "文章链接", // 文章链接消息类型 "news": "文章链接",
"mpnews": "公众号图文消息", // 公众号图文消息类型 "mpnews": "公众号图文消息",
"wxcard": "卡券消息", // 卡券消息类型 "wxcard": "卡券消息",
"miniprogrampage": "小程序消息", // 小程序消息类型 "miniprogrampage": "小程序消息",
"msgmenu": "菜单消息" // 菜单消息类型 "msgmenu": "菜单消息"
} }
}, },
// 定义用于修改状态的 mutations目前为空
mutations: { mutations: {
} }

@ -1,23 +1,15 @@
// 导出一个默认的模块对象
export default { export default {
// 启用命名空间使得该模块的状态和getters、actions、mutations是注册在全局命名空间下的子模块 namespaced: true,
namespaced: true, state: {
id: 0,
// 定义模块的初始状态 name: ''
state: { },
id: 0, // 初始化 id 为 0 mutations: {
name: '' // 初始化 name 为空字符串 updateId(state, id) {
state.id = id
}, },
updateName(state, name) {
// 定义用于修改状态的 mutations state.name = name
mutations: {
// 更新 id 的 mutation
updateId(state, id) {
state.id = id; // 将传入的 id 赋值给 state 中的 id
},
// 更新 name 的 mutation
updateName(state, name) {
state.name = name; // 将传入的 name 赋值给 state 中的 name
}
} }
}
} }

@ -1,59 +1,32 @@
import Vue from 'vue'; import Vue from 'vue'
export default { export default {
// Vuex模块启用命名空间 namespaced: true,
namespaced: true, state: {
ACCOUNT_TYPES:{
// Vuex的state用来存储应用状态 1:'订阅号',
state: { 2:'服务号'
// 账户类型映射数字ID到账户类型名称 },
ACCOUNT_TYPES: { accountList:[],
1: '订阅号', selectedAppid:''
2: '服务号' },
}, mutations: {
updateAccountList (state, list) {
// 存储账户列表 state.accountList = list
accountList: [], if(!list.length)return
if(!state.selectedAppid){
// 当前选中的Appid用来标识选择的账号 let appidCookie = Vue.cookie.get('appid')
selectedAppid: '' let selectedAppid = appidCookie?appidCookie:list[0].appid
}, this.commit('wxAccount/selectAccount',selectedAppid)
}
// Vuex的mutations用来修改state },
mutations: { selectAccount (state, appid) {
// 更新账户列表 Vue.cookie.set('appid',appid)
updateAccountList (state, list) { let oldAppid = state.selectedAppid
// 更新state中的accountList state.selectedAppid = appid
state.accountList = list; if(oldAppid){//切换账号时刷新网页
location.reload();
// 如果列表为空,直接返回 }
if (!list.length) return; },
}
// 如果当前没有选中的Appid则从cookie或列表中选择一个默认Appid }
if (!state.selectedAppid) {
let appidCookie = Vue.cookie.get('appid');
// 获取cookie中的appid如果有则使用cookie中的appid否则使用列表中的第一个Appid
let selectedAppid = appidCookie ? appidCookie : list[0].appid;
// 通过commit调用mutation更新选中的账号
this.commit('wxAccount/selectAccount', selectedAppid);
}
},
// 选择某个账号切换Appid
selectAccount (state, appid) {
// 更新cookie中的appid保存选中的Appid
Vue.cookie.set('appid', appid);
// 记录上一个选中的Appid
let oldAppid = state.selectedAppid;
// 更新当前选中的Appid
state.selectedAppid = appid;
// 如果选中的Appid发生变化则刷新页面
if (oldAppid) {
location.reload();
}
}
}
};

@ -1,20 +1,12 @@
export default { export default {
// 启用 Vuex 模块的命名空间,避免命名冲突 namespaced: true,
namespaced: true, state: {
tags:[]
// state 存储模块的状态 },
state: { mutations: {
// tags 用来存储标签数据的数组 updateTags (state, tags) {
tags: [] state.tags = tags
}, }
}
// mutations 用来修改 state 中的状态
mutations: {
// 更新 tags 数组的内容
updateTags (state, tags) {
// 将传入的 tags 更新到 state 中
state.tags = tags;
} }
}
};

@ -4,81 +4,74 @@ import router from '@/router'
import qs from 'qs' import qs from 'qs'
import merge from 'lodash/merge' import merge from 'lodash/merge'
import { clearLoginInfo } from '@/utils' import { clearLoginInfo } from '@/utils'
const baseUrl = '/wx'
const baseUrl = '/wx' // 设置请求的基础路径
// 创建axios实例
const http = axios.create({ const http = axios.create({
timeout: 1000 * 30, // 设置请求超时为30秒 timeout: 1000 * 30,
withCredentials: true, // 允许携带跨域请求的cookie withCredentials: true,
headers: { headers: {
'Content-Type': 'application/json; charset=utf-8' // 默认请求头为json格式 'Content-Type': 'application/json; charset=utf-8'
} }
}) })
/** /**
* 请求拦截器 * 请求拦截
* 在每个请求发送之前加入token从cookie中获取 */
*/
http.interceptors.request.use(config => { http.interceptors.request.use(config => {
config.headers['token'] = Vue.cookie.get('token') // 在请求头中加入token config.headers['token'] = Vue.cookie.get('token') // 请求头带上token
return config // 返回请求配置 return config
}, error => { }, error => {
return Promise.reject(error) // 请求出错时返回Promise拒绝 return Promise.reject(error)
}) })
/** /**
* 响应拦截器 * 响应拦截
* 对响应数据进行拦截处理 */
* 如果返回的状态码为401未授权则清除登录信息并跳转到登录页
*/
http.interceptors.response.use(response => { http.interceptors.response.use(response => {
if (response.data && response.data.code === 401) { // 判断返回的code是否为401代表token失效 if (response.data && response.data.code === 401) { // 401, token失效
clearLoginInfo() // 清除登录信息 clearLoginInfo()
router.push({ name: 'login' }) // 跳转到登录页面 router.push({ name: 'login' })
} }
return response // 返回响应数据 return response
}, error => { }, error => {
return Promise.reject(error) // 响应出错时返回Promise拒绝 return Promise.reject(error)
}) })
/** /**
* 请求地址处理函数 * 请求地址处理
* @param {*} actionName 接口的名称拼接成完整的URL * @param {*} actionName action方法名称
* @returns {string} 拼接后的完整URL */
*/
http.adornUrl = (actionName) => { http.adornUrl = (actionName) => {
// 在开发环境下,如果开启了代理,则请求路径会带上代理前缀 // 非生产环境 && 开启代理, 接口前缀统一使用[/proxyApi/]前缀做代理拦截!
return baseUrl + actionName // 返回完整的请求URL return baseUrl + actionName
} }
/** /**
* get请求的参数处理 * get请求参数处理
* @param {*} params 请求的参数对象 * @param {*} params 参数对象
* @param {*} openDefultParams 是否开启默认参数 * @param {*} openDefultParams 是否开启默认参数?
* @returns {object} 处理后的参数对象 */
*/
http.adornParams = (params = {}, openDefultParams = true) => { http.adornParams = (params = {}, openDefultParams = true) => {
const defaults = { var defaults = {
't': new Date().getTime() // 添加时间戳参数,防止缓存 't': new Date().getTime()
} }
return openDefultParams ? merge(defaults, params) : params // 合并默认参数和传入的参数 return openDefultParams ? merge(defaults, params) : params
} }
/** /**
* post请求的数据处理 * post请求数据处理
* @param {*} data 请求的数据对象 * @param {*} data 数据对象
* @param {*} openDefultdata 是否开启默认数据 * @param {*} openDefultdata 是否开启默认数据?
* @param {*} contentType 数据格式类型'json''form' * @param {*} contentType 数据格式
* @returns {string} 处理后的数据 * json: 'application/json; charset=utf-8'
*/ * form: 'application/x-www-form-urlencoded; charset=utf-8'
*/
http.adornData = (data = {}, openDefultdata = true, contentType = 'json') => { http.adornData = (data = {}, openDefultdata = true, contentType = 'json') => {
const defaults = { var defaults = {
't': new Date().getTime() // 添加时间戳参数,防止缓存 't': new Date().getTime()
} }
data = openDefultdata ? merge(defaults, data) : data // 合并默认数据和传入的数据 data = openDefultdata ? merge(defaults, data) : data
// 根据不同的contentType处理数据格式 return contentType === 'json' ? JSON.stringify(data) : qs.stringify(data)
return contentType === 'json' ? JSON.stringify(data) : qs.stringify(data)
} }
export default http // 导出axios实例供其他模块使用 export default http

@ -3,76 +3,56 @@ import router from '@/router'
import store from '@/store' import store from '@/store'
/** /**
* 获取UUID * 获取uuid
* 生成一个标准的UUID例如xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx */
* 使用随机数和指定格式的规则生成UUID
*/
export function getUUID() { export function getUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
return (c === 'x' ? (Math.random() * 16 | 0) : ('r&0x3' | '0x8')).toString(16) return (c === 'x' ? (Math.random() * 16 | 0) : ('r&0x3' | '0x8')).toString(16)
}) })
} }
/** /**
* 检查是否有某个权限 * 是否有权限
* @param {*} key 权限的标识符 * @param {*} key
* @returns {boolean} 如果权限列表中包含该权限返回true否则返回false */
*/
export function isAuth(key) { export function isAuth(key) {
// 从 sessionStorage 中获取权限列表,并转换为数组,如果没有权限列表,默认返回空数组 return JSON.parse(sessionStorage.getItem('permissions') || '[]').indexOf(key) !== -1 || false
return JSON.parse(sessionStorage.getItem('permissions') || '[]').indexOf(key) !== -1 || false
} }
/** /**
* 将平面数据转换为树形数据 * 树形数据转换
* @param {*} data 原始平面数据 * @param {*} data
* @param {*} id 唯一标识符字段默认为'id' * @param {*} id
* @param {*} pid 父级标识符字段默认为'parentId' * @param {*} pid
* @returns {Array} 转换后的树形数据 */
*/
export function treeDataTranslate(data, id = 'id', pid = 'parentId') { export function treeDataTranslate(data, id = 'id', pid = 'parentId') {
var res = [] // 存储最终的树形结构 var res = []
var temp = {} // 临时存储每个节点,以便快速查找父节点 var temp = {}
for (var i = 0; i < data.length; i++) {
// 将数据转换为临时对象key为节点的id值为节点本身 temp[data[i][id]] = data[i]
for (var i = 0; i < data.length; i++) { }
temp[data[i][id]] = data[i] for (var k = 0; k < data.length; k++) {
} if (temp[data[k][pid]] && data[k][id] !== data[k][pid]) {
if (!temp[data[k][pid]]['children']) {
// 遍历数据根据pid将节点组织成树形结构 temp[data[k][pid]]['children'] = []
for (var k = 0; k < data.length; k++) { }
// 如果节点的父节点存在并且当前节点的id不等于父节点的id if (!temp[data[k][pid]]['_level']) {
if (temp[data[k][pid]] && data[k][id] !== data[k][pid]) { temp[data[k][pid]]['_level'] = 1
// 如果父节点没有'children'属性,则初始化为数组 }
if (!temp[data[k][pid]]['children']) { data[k]['_level'] = temp[data[k][pid]]._level + 1
temp[data[k][pid]]['children'] = [] temp[data[k][pid]]['children'].push(data[k])
} } else {
// 如果父节点没有'_level'属性则设置为1 res.push(data[k])
if (!temp[data[k][pid]]['_level']) { }
temp[data[k][pid]]['_level'] = 1 }
} return res
// 当前节点的级别为父节点的级别+1
data[k]['_level'] = temp[data[k][pid]]._level + 1
// 将当前节点推送到父节点的children数组中
temp[data[k][pid]]['children'].push(data[k])
} else {
// 如果当前节点是根节点,直接推送到结果数组中
res.push(data[k])
}
}
return res // 返回转换后的树形数据
} }
/** /**
* 清除登录信息 * 清除登录信息
* 用于用户退出时清理本地存储的登录信息 */
*/
export function clearLoginInfo() { export function clearLoginInfo() {
// 删除cookie中的'token' Vue.cookie.delete('token')
Vue.cookie.delete('token') //store.commit('resetStore')
// 目前注释掉了重置store的操作若需要可以解除注释 router.options.isAddDynamicMenuRoutes = false
// store.commit('resetStore')
// 重置动态菜单路由标志
router.options.isAddDynamicMenuRoutes = false
} }

@ -1,36 +1,31 @@
/** /**
* 验证邮箱格式 * 邮箱
* @param {*} s - 需要验证的邮箱地址 * @param {*} s
* @returns {boolean} - 返回是否是有效的邮箱地址 */
*/
export function isEmail(s) { export function isEmail(s) {
return /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/.test(s) return /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((.[a-zA-Z0-9_-]{2,3}){1,2})$/.test(s)
} }
/** /**
* 验证手机号码格式中国手机号 * 手机号码
* @param {*} s - 需要验证的手机号 * @param {*} s
* @returns {boolean} - 返回是否是有效的手机号码 */
*/ export function isMobile(s) {
export function isMobile(s) {
return /^1[0-9]{10}$/.test(s) return /^1[0-9]{10}$/.test(s)
} }
/** /**
* 验证固定电话号码格式 * 电话号码
* @param {*} s - 需要验证的电话号码 * @param {*} s
* @returns {boolean} - 返回是否是有效的电话号码包括区号和本地号码 */
*/ export function isPhone(s) {
export function isPhone(s) {
return /^([0-9]{3,4}-)?[0-9]{7,8}$/.test(s) return /^([0-9]{3,4}-)?[0-9]{7,8}$/.test(s)
} }
/** /**
* 验证URL地址格式 * URL地址
* @param {*} s - 需要验证的URL地址 * @param {*} s
* @returns {boolean} - 返回是否是有效的URL地址包括http或https协议 */
*/ export function isURL(s) {
export function isURL(s) {
return /^http[s]?:\/\/.*/.test(s) return /^http[s]?:\/\/.*/.test(s)
} }

@ -1,81 +1,61 @@
<template> <template>
<!-- 404 错误页面的模板 -->
<div class="site-wrapper site-page--not-found"> <div class="site-wrapper site-page--not-found">
<div class="site-content__wrapper"> <div class="site-content__wrapper">
<div class="site-content"> <div class="site-content">
<!-- 错误代码 --> <h2 class="not-found-title">400</h2>
<h2 class="not-found-title">400</h2> <p class="not-found-desc">抱歉您访问的页面<em>失联</em> ...</p>
<!-- 错误描述 --> <el-button @click="$router.go(-1)"></el-button>
<p class="not-found-desc">抱歉您访问的页面<em>失联</em> ...</p > <el-button type="primary" class="not-found-btn-gohome" @click="$router.push({ name: 'home' })">进入首页</el-button>
<!-- 返回上一页按钮 --> </div>
<el-button @click="$router.go(-1)"></el-button> </div>
<!-- 返回首页按钮 -->
<el-button type="primary" class="not-found-btn-gohome" @click="$router.push({ name: 'home' })">进入首页</el-button>
</div> </div>
</div> </template>
</div>
</template> <script>
export default {
<script> }
export default { </script>
// JavaScript
} <style lang="scss">
</script> .site-wrapper.site-page--not-found {
position: absolute;
<style lang="scss">
/* 整个404页面的外部容器 */
.site-wrapper.site-page--not-found {
position: absolute; /* 定位到页面的绝对位置 */
top: 0; top: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
overflow: hidden; /* 防止内容溢出 */ overflow: hidden;
/* 内容包裹层 */
.site-content__wrapper { .site-content__wrapper {
padding: 0; padding: 0;
margin: 0; margin: 0;
background-color: #fff; /* 设置背景色为白色 */ background-color: #fff;
} }
/* 页面内容容器 */
.site-content { .site-content {
position: fixed; /* 固定位置 */ position: fixed;
top: 15%; /* 距离顶部15% */ top: 15%;
left: 50%; /* 距离左边50% */ left: 50%;
z-index: 2; /* 确保内容在其他元素之上 */ z-index: 2;
padding: 30px; padding: 30px;
text-align: center; /* 内容居中 */ text-align: center;
transform: translate(-50%, 0); /* 将内容水平居中 */ transform: translate(-50%, 0);
} }
/* 错误标题 */
.not-found-title { .not-found-title {
margin: 20px 0 15px; margin: 20px 0 15px;
font-size: 10em; /* 大字体 */ font-size: 10em;
font-weight: 400; font-weight: 400;
color: rgb(55, 71, 79); /* 文字颜色 */ color: rgb(55, 71, 79);
} }
/* 错误描述 */
.not-found-desc { .not-found-desc {
margin: 0 0 30px; margin: 0 0 30px;
font-size: 26px; font-size: 26px;
text-transform: uppercase; /* 将文本转换为大写 */ text-transform: uppercase;
color: rgb(118, 131, 143); /* 文字颜色 */ color: rgb(118, 131, 143);
> em {
/* 强调标签 <em> 样式 */ font-style: normal;
> em { color: #ee8145;
font-style: normal; }
color: #ee8145; /* 设置颜色 */
} }
}
/* 返回首页按钮的左边距 */
.not-found-btn-gohome { .not-found-btn-gohome {
margin-left: 30px; margin-left: 30px;
}
} }
</style> }
</style>

@ -1,18 +1,12 @@
<template> <template>
<!-- 模块容器 div -->
<div class="mod-home"> <div class="mod-home">
<!-- 欢迎标题 --> <h3>欢迎使用微信管理系统</h3>
<h3>欢迎使用微信管理系统</h3>
</div> </div>
</template> </template>
<style>
<style> .mod-home {
/* 样式部分 */
.mod-home {
/* 设置行高,增加文本的可读性 */
line-height: 2.5; line-height: 2.5;
/* 使文本水平居中对齐 */
text-align: center; text-align: center;
} }
</style> </style>

@ -1,220 +1,184 @@
<template> <template>
<div class="site-wrapper site-page--login"> <div class="site-wrapper site-page--login">
<!-- 页面容器背景层 --> <div class="site-content__wrapper">
<div class="site-content__wrapper"> <div class="site-content">
<!-- 页面内容容器 --> <div class="brand-info">
<div class="site-content"> <h2 class="brand-info__text">微信后台管理系统</h2>
<!-- 品牌信息部分 --> <p class="brand-info__intro">微信公众号后台管理系统</p>
<div class="brand-info"> </div>
<h2 class="brand-info__text">微信后台管理系统</h2> <div class="login-main">
<p class="brand-info__intro">微信公众号后台管理系统</p > <h3 class="login-title">管理员登录</h3>
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" status-icon>
<el-form-item prop="userName">
<el-input v-model="dataForm.userName" placeholder="帐号"></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="dataForm.password" type="password" placeholder="密码"></el-input>
</el-form-item>
<el-form-item prop="captcha">
<el-row :gutter="20">
<el-col :span="14">
<el-input v-model="dataForm.captcha" placeholder="验证码">
</el-input>
</el-col>
<el-col :span="10" class="login-captcha">
<img :src="captchaPath" @click="getCaptcha()" alt="">
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-button class="login-btn-submit" type="primary" @click="dataFormSubmit()"></el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
</div> </div>
</template>
<!-- 登录表单 -->
<div class="login-main">
<h3 class="login-title">管理员登录</h3>
<!-- 表单绑定了 model validation 规则 -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" status-icon>
<!-- 用户名输入框 -->
<el-form-item prop="userName">
<el-input v-model="dataForm.userName" placeholder="帐号"></el-input>
</el-form-item>
<!-- 密码输入框 -->
<el-form-item prop="password">
<el-input v-model="dataForm.password" type="password" placeholder="密码"></el-input>
</el-form-item>
<!-- 验证码输入框 -->
<el-form-item prop="captcha">
<el-row :gutter="20">
<el-col :span="14">
<el-input v-model="dataForm.captcha" placeholder="验证码"></el-input>
</el-col>
<el-col :span="10" class="login-captcha">
<!-- 验证码图片点击刷新验证码 -->
< img :src="captchaPath" @click="getCaptcha()" alt="">
</el-col>
</el-row>
</el-form-item>
<!-- 登录按钮 -->
<el-form-item>
<el-button class="login-btn-submit" type="primary" @click="dataFormSubmit()"></el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
</div>
</template>
<script> <script>
import { getUUID } from '@/utils' // UUID import { getUUID } from '@/utils'
export default { export default {
data() { data() {
return { return {
// dataForm: {
dataForm: { userName: '',
userName: '', // password: '',
password: '', // uuid: '',
uuid: '', // UUID captcha: ''
captcha: '' // },
}, dataRule: {
// userName: [
dataRule: { { required: true, message: '帐号不能为空', trigger: 'blur' }
userName: [ ],
{ required: true, message: '帐号不能为空', trigger: 'blur' } password: [
], { required: true, message: '密码不能为空', trigger: 'blur' }
password: [ ],
{ required: true, message: '密码不能为空', trigger: 'blur' } captcha: [
], { required: true, message: '验证码不能为空', trigger: 'blur' }
captcha: [ ]
{ required: true, message: '验证码不能为空', trigger: 'blur' } },
] captchaPath: ''
}, }
captchaPath: '' // },
} created() {
}, this.getCaptcha()
created() { },
// methods: {
this.getCaptcha() //
}, dataFormSubmit() {
methods: { this.$refs['dataForm'].validate((valid) => {
// if (valid) {
dataFormSubmit() { this.$http({
// url: this.$http.adornUrl('/sys/login'),
this.$refs['dataForm'].validate((valid) => { method: 'post',
if (valid) { data: this.$http.adornData({
// 'username': this.dataForm.userName,
this.$http({ 'password': this.dataForm.password,
url: this.$http.adornUrl('/sys/login'), // 'uuid': this.dataForm.uuid,
method: 'post', 'captcha': this.dataForm.captcha
data: this.$http.adornData({ })
'username': this.dataForm.userName, }).then(({ data }) => {
'password': this.dataForm.password, if (data && data.code === 200) {
'uuid': this.dataForm.uuid, this.$cookie.set('token', data.token)
'captcha': this.dataForm.captcha this.$router.replace({ name: 'home' })
}) } else {
}).then(({ data }) => { this.getCaptcha()
if (data && data.code === 200) { this.$message.error(data.msg)
// token }
this.$cookie.set('token', data.token) })
this.$router.replace({ name: 'home' }) }
} else { })
// },
this.getCaptcha() //
this.$message.error(data.msg) getCaptcha() {
} this.dataForm.uuid = getUUID()
}) this.captchaPath = this.$http.adornUrl(`/captcha?uuid=${this.dataForm.uuid}`)
} }
}) }
},
// UUID
getCaptcha() {
this.dataForm.uuid = getUUID() // UUID
//
this.captchaPath = this.$http.adornUrl(`/captcha?uuid=${this.dataForm.uuid}`)
}
}
} }
</script> </script>
<style lang="scss"> <style lang="scss">
.site-wrapper.site-page--login { .site-wrapper.site-page--login {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
background-color: rgba(38, 50, 56, 0.5); /* 半透明背景 */ background-color: rgba(38, 50, 56, 0.5);
overflow: hidden; overflow: hidden;
&:before {
/* 页面背景渐变效果 */ position: fixed;
&:before { top: 0;
position: fixed; left: 0;
top: 0; z-index: -1;
left: 0; width: 100%;
z-index: -1; /* 确保背景层在最底层 */ height: 100%;
width: 100%; content: "";
height: 100%; background-color: #fa8bff;
content: ""; background-image: linear-gradient(
background-color: #fa8bff; 45deg,
background-image: linear-gradient( #fa8bff 0%,
45deg, #2bd2ff 52%,
#fa8bff 0%, #2bff88 90%
#2bd2ff 52%, );
#2bff88 90% background-size: cover;
); }
background-size: cover; .site-content__wrapper {
} position: absolute;
top: 0;
.site-content__wrapper { right: 0;
position: absolute; bottom: 0;
top: 0; left: 0;
right: 0; padding: 0;
bottom: 0; margin: 0;
left: 0; overflow-x: hidden;
padding: 0; overflow-y: auto;
margin: 0; background-color: transparent;
overflow-x: hidden; }
overflow-y: auto; .site-content {
background-color: transparent; min-height: 100%;
} padding: 30px 500px 30px 30px;
}
.site-content { .brand-info {
min-height: 100%; margin: 220px 100px 0 90px;
padding: 30px 500px 30px 30px; color: #fff;
} }
.brand-info__text {
/* 品牌信息样式 */ margin: 0 0 22px 0;
.brand-info { font-size: 48px;
margin: 220px 100px 0 90px; font-weight: 400;
color: #fff; text-transform: uppercase;
} }
.brand-info__intro {
.brand-info__text { margin: 10px 0;
margin: 0 0 22px 0; font-size: 16px;
font-size: 48px; line-height: 1.58;
font-weight: 400; opacity: 0.6;
text-transform: uppercase; }
} .login-main {
position: absolute;
.brand-info__intro { top: 0;
margin: 10px 0; right: 0;
font-size: 16px; padding: 150px 60px 180px;
line-height: 1.58; width: 470px;
opacity: 0.6; min-height: 100%;
} background-color: #fff;
}
/* 登录表单样式 */ .login-title {
.login-main { font-size: 16px;
position: absolute; }
top: 0; .login-captcha {
right: 0; overflow: hidden;
padding: 150px 60px 180px; > img {
width: 470px; width: 100%;
min-height: 100%; cursor: pointer;
background-color: #fff; }
} }
.login-btn-submit {
.login-title { width: 100%;
font-size: 16px; margin-top: 38px;
} }
/* 验证码图片样式 */
.login-captcha {
overflow: hidden;
> img {
width: 100%;
cursor: pointer; /* 鼠标指针变成手形,表示可以点击 */
}
}
/* 登录按钮样式 */
.login-btn-submit {
width: 100%;
margin-top: 38px;
}
} }
</style> </style>

@ -1,53 +1,33 @@
<template> <template>
<el-form> <el-form>
<!-- 布局设置表单标题 --> <h2>布局设置</h2>
<h2>布局设置</h2> <el-form-item label="导航条类型">
<el-radio-group v-model="navbarLayoutType">
<!-- 导航条类型设置项 --> <el-radio label="default" border>default</el-radio>
<el-form-item label="导航条类型"> <el-radio label="inverse" border>inverse</el-radio>
<!-- 导航条类型选择框使用 radio 按钮选择 'default' 'inverse' --> </el-radio-group>
<el-radio-group v-model="navbarLayoutType"> </el-form-item>
<el-radio label="default" border>default</el-radio> <el-form-item label="侧边栏皮肤">
<el-radio label="inverse" border>inverse</el-radio> <el-radio-group v-model="sidebarLayoutSkin">
</el-radio-group> <el-radio label="light" border>light</el-radio>
</el-form-item> <el-radio label="dark" border>dark</el-radio>
</el-radio-group>
<!-- 侧边栏皮肤设置项 --> </el-form-item>
<el-form-item label="侧边栏皮肤">
<!-- 侧边栏皮肤选择框使用 radio 按钮选择 'light' 'dark' -->
<el-radio-group v-model="sidebarLayoutSkin">
<el-radio label="light" border>light</el-radio>
<el-radio label="dark" border>dark</el-radio>
</el-radio-group>
</el-form-item>
</el-form> </el-form>
</template> </template>
<script> <script>
export default { export default {
computed: { computed: {
// navbarLayoutType: {
navbarLayoutType: { get() { return this.$store.state.common.navbarLayoutType },
// getter Vuex navbarLayoutType set(val) { this.$store.commit('common/updateNavbarLayoutType', val) }
get() { },
return this.$store.state.common.navbarLayoutType sidebarLayoutSkin: {
}, get() { return this.$store.state.common.sidebarLayoutSkin },
// setter Vuex navbarLayoutType set(val) { this.$store.commit('common/updateSidebarLayoutSkin', val) }
set(val) { }
this.$store.commit('common/updateNavbarLayoutType', val) }
}
},
//
sidebarLayoutSkin: {
// getter Vuex sidebarLayoutSkin
get() {
return this.$store.state.common.sidebarLayoutSkin
},
// setter Vuex sidebarLayoutSkin
set(val) {
this.$store.commit('common/updateSidebarLayoutSkin', val)
}
}
}
} }
</script> </script>

@ -1,150 +1,127 @@
<template> <template>
<!-- Dialog弹框用于配置云存储 -->
<el-dialog title="云存储配置" :close-on-click-modal="false" :visible.sync="visible"> <el-dialog title="云存储配置" :close-on-click-modal="false" :visible.sync="visible">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="120px">
<!-- 表单组件model 绑定 dataFormrules 绑定 dataRule --> <el-form-item size="mini" label="存储类型">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="120px"> <el-radio-group v-model="dataForm.type">
<el-radio :label="1">七牛</el-radio>
<!-- 存储类型选择项 --> <el-radio :label="2">阿里云</el-radio>
<el-form-item size="mini" label="存储类型"> <el-radio :label="3">腾讯云</el-radio>
<el-radio-group v-model="dataForm.type"> </el-radio-group>
<el-radio :label="1">七牛</el-radio> </el-form-item>
<el-radio :label="2">阿里云</el-radio> <template v-if="dataForm.type === 1">
<el-radio :label="3">腾讯云</el-radio> <el-form-item label="域名">
</el-radio-group> <el-input v-model="dataForm.qiniuDomain" placeholder="七牛绑定的域名"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="路径前缀">
<!-- 当选择七牛云时显示七牛相关配置项 --> <el-input v-model="dataForm.qiniuPrefix" placeholder="不设置默认为空"></el-input>
<template v-if="dataForm.type === 1"> </el-form-item>
<el-form-item label="域名"> <el-form-item label="AccessKey">
<el-input v-model="dataForm.qiniuDomain" placeholder="七牛绑定的域名"></el-input> <el-input v-model="dataForm.qiniuAccessKey" placeholder="七牛AccessKey"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="路径前缀"> <el-form-item label="SecretKey">
<el-input v-model="dataForm.qiniuPrefix" placeholder="不设置默认为空"></el-input> <el-input v-model="dataForm.qiniuSecretKey" placeholder="七牛SecretKey"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="AccessKey"> <el-form-item label="空间名">
<el-input v-model="dataForm.qiniuAccessKey" placeholder="七牛AccessKey"></el-input> <el-input v-model="dataForm.qiniuBucketName" placeholder="七牛存储空间名"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="SecretKey"> </template>
<el-input v-model="dataForm.qiniuSecretKey" placeholder="七牛SecretKey"></el-input> <template v-else-if="dataForm.type === 2">
</el-form-item> <el-form-item label="域名">
<el-form-item label="空间名"> <el-input v-model="dataForm.aliyunDomain" placeholder="阿里云绑定的域名"></el-input>
<el-input v-model="dataForm.qiniuBucketName" placeholder="七牛存储空间名"></el-input> </el-form-item>
</el-form-item> <el-form-item label="路径前缀">
</template> <el-input v-model="dataForm.aliyunPrefix" placeholder="不设置默认为空"></el-input>
</el-form-item>
<!-- 当选择阿里云时显示阿里云相关配置项 --> <el-form-item label="EndPoint">
<template v-else-if="dataForm.type === 2"> <el-input v-model="dataForm.aliyunEndPoint" placeholder="阿里云EndPoint"></el-input>
<el-form-item label="域名"> </el-form-item>
<el-input v-model="dataForm.aliyunDomain" placeholder="阿里云绑定的域名"></el-input> <el-form-item label="AccessKeyId">
</el-form-item> <el-input v-model="dataForm.aliyunAccessKeyId" placeholder="阿里云AccessKeyId"></el-input>
<el-form-item label="路径前缀"> </el-form-item>
<el-input v-model="dataForm.aliyunPrefix" placeholder="不设置默认为空"></el-input> <el-form-item label="AccessKeySecret">
</el-form-item> <el-input v-model="dataForm.aliyunAccessKeySecret" placeholder="阿里云AccessKeySecret"></el-input>
<el-form-item label="EndPoint"> </el-form-item>
<el-input v-model="dataForm.aliyunEndPoint" placeholder="阿里云EndPoint"></el-input> <el-form-item label="BucketName">
</el-form-item> <el-input v-model="dataForm.aliyunBucketName" placeholder="阿里云BucketName"></el-input>
<el-form-item label="AccessKeyId"> </el-form-item>
<el-input v-model="dataForm.aliyunAccessKeyId" placeholder="阿里云AccessKeyId"></el-input> </template>
</el-form-item> <template v-else-if="dataForm.type === 3">
<el-form-item label="AccessKeySecret"> <el-form-item label="域名">
<el-input v-model="dataForm.aliyunAccessKeySecret" placeholder="阿里云AccessKeySecret"></el-input> <el-input v-model="dataForm.qcloudDomain" placeholder="腾讯云绑定的域名"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="BucketName"> <el-form-item label="路径前缀">
<el-input v-model="dataForm.aliyunBucketName" placeholder="阿里云BucketName"></el-input> <el-input v-model="dataForm.qcloudPrefix" placeholder="不设置默认为空"></el-input>
</el-form-item> </el-form-item>
</template> <el-form-item label="AppId">
<el-input v-model="dataForm.qcloudAppId" placeholder="腾讯云AppId"></el-input>
<!-- 当选择腾讯云时显示腾讯云相关配置项 --> </el-form-item>
<template v-else-if="dataForm.type === 3"> <el-form-item label="SecretId">
<el-form-item label="域名"> <el-input v-model="dataForm.qcloudSecretId" placeholder="腾讯云SecretId"></el-input>
<el-input v-model="dataForm.qcloudDomain" placeholder="腾讯云绑定的域名"></el-input> </el-form-item>
</el-form-item> <el-form-item label="SecretKey">
<el-form-item label="路径前缀"> <el-input v-model="dataForm.qcloudSecretKey" placeholder="腾讯云SecretKey"></el-input>
<el-input v-model="dataForm.qcloudPrefix" placeholder="不设置默认为空"></el-input> </el-form-item>
</el-form-item> <el-form-item label="BucketName">
<el-form-item label="AppId"> <el-input v-model="dataForm.qcloudBucketName" placeholder="腾讯云BucketName"></el-input>
<el-input v-model="dataForm.qcloudAppId" placeholder="腾讯云AppId"></el-input> </el-form-item>
</el-form-item> <el-form-item label="Bucket所属地区">
<el-form-item label="SecretId"> <el-input v-model="dataForm.qcloudRegion" placeholder="如sh可选值 华南gz 华北tj 华东sh"></el-input>
<el-input v-model="dataForm.qcloudSecretId" placeholder="腾讯云SecretId"></el-input> </el-form-item>
</el-form-item> </template>
<el-form-item label="SecretKey"> </el-form>
<el-input v-model="dataForm.qcloudSecretKey" placeholder="腾讯云SecretKey"></el-input> <span slot="footer" class="dialog-footer">
</el-form-item> <el-button @click="visible = false">取消</el-button>
<el-form-item label="BucketName"> <el-button type="primary" @click="dataFormSubmit()"></el-button>
<el-input v-model="dataForm.qcloudBucketName" placeholder="腾讯云BucketName"></el-input> </span>
</el-form-item>
<el-form-item label="Bucket所属地区">
<el-input v-model="dataForm.qcloudRegion" placeholder="如sh可选值 华南gz 华北tj 华东sh"></el-input>
</el-form-item>
</template>
</el-form>
<!-- 弹窗底部按钮 -->
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()"></el-button>
</span>
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
export default { export default {
// data() {
data() { return {
return { visible: false,
// / dataForm: {},
visible: false, dataRule: {}
// }
dataForm: {}, },
// methods: {
dataRule: {} init(id) {
} this.visible = true
}, this.$http({
methods: { url: this.$http.adornUrl('/sys/oss/config'),
// id method: 'get',
init(id) { params: this.$http.adornParams()
this.visible = true // }).then(({ data }) => {
// this.dataForm = data && data.code === 200 ? data.config : []
this.$http({ })
url: this.$http.adornUrl('/sys/oss/config'), // },
method: 'get', // GET //
params: this.$http.adornParams() // dataFormSubmit() {
}).then(({ data }) => { this.$refs['dataForm'].validate((valid) => {
// if (valid) {
this.dataForm = data && data.code === 200 ? data.config : [] this.$http({
}) url: this.$http.adornUrl('/sys/oss/saveConfig'),
}, method: 'post',
// data: this.$http.adornData(this.dataForm)
dataFormSubmit() { }).then(({ data }) => {
// if (data && data.code === 200) {
this.$refs['dataForm'].validate((valid) => { this.$message({
if (valid) { message: '操作成功',
// type: 'success',
this.$http({ duration: 1500,
url: this.$http.adornUrl('/sys/oss/saveConfig'), // onClose: () => {
method: 'post', // POST this.visible = false
data: this.$http.adornData(this.dataForm) // }
}).then(({ data }) => { })
if (data && data.code === 200) { } else {
// this.$message.error(data.msg)
this.$message({ }
message: '操作成功', })
type: 'success', }
duration: 1500, })
onClose: () => { }
this.visible = false // }
}
})
} else {
//
this.$message.error(data.msg)
}
})
}
})
}
}
} }
</script> </script>

@ -1,105 +1,87 @@
<template> <template>
<!-- 触发文件选择的区域点击时触发 selectFile 方法 --> <div @click="selectFile">
<div @click="selectFile"> <input type="file" ref="fileInput" v-show="false" @change="onFileChange" />
<!-- 隐藏的文件输入框通过 click 触发文件选择 --> <div>{{uploading?infoText:'上传文件'}}</div>
<input type="file" ref="fileInput" v-show="false" @change="onFileChange" /> </div>
<!-- 显示上传状态文本如果正在上传则显示 infoText否则显示 "上传文件" --> </template>
<div>{{uploading ? infoText : '上传文件'}}</div>
</div> <script>
</template> // 使
// 使 <script src="https://unpkg.com/cos-js-sdk-v5@0.5.23/dist/cos-js-sdk-v5.min.js" async></script>
<script>
// 使
// 使 SDKhttps://unpkg.com/cos-js-sdk-v5@0.5.23/dist/cos-js-sdk-v5.min.js
var cos; var cos;
export default { export default {
name: "oss-uploader", // name: "oss-uploader",
data() { data() {
return { return {
uploading: false, // uploading: false,
infoText: "上传中...", // infoText:"上传中...",
cosConfig: [] // COS cosConfig:[]
}
},
mounted(){
this.$http({
url: this.$http.adornUrl('/sys/oss/config'),
method: 'get',
params: this.$http.adornParams()
}).then(({data}) => {
if(data && data.code === 200){
this.cosConfig = data.config
cos=new COS({
SecretId: data.config.qcloudSecretId,
SecretKey: data.config.qcloudSecretKey,
});
}else{
this.$message.error('请先配置云存储相关信息!')
}
})
},
methods: {
selectFile() {//
if (!this.uploading) {
this.$refs.fileInput.click();
}
},
onFileChange() {
let file = this.$refs.fileInput.files[0];
this.uploading = true;
let now = new Date();
let path=now.toISOString().slice(0,10)+'/'+now.getTime()+file.name.substr(file.name.lastIndexOf('.'))
cos.putObject({
Bucket: this.cosConfig.qcloudBucketName, /* 必须 */
Region: this.cosConfig.qcloudRegion, /* 必须 */
Key: path, /* 必须 */
Body: file, //
onProgress: (progressData)=> {
this.infoText='上传中:'+progressData.percent*100+'%'
}
}, (err, data)=> {
console.log(err || data);
this.uploading = false;
if(data){
this.infoText='上传文件'
let fileUrl='https://'+this.cosConfig.qcloudBucketName+'.cos.'+this.cosConfig.qcloudRegion+'.myqcloud.com/'+path;
this.saveUploadResult(fileUrl)
}else {
this.$message.error('文件上传失败',err)
}
});
},
saveUploadResult(url){
this.$http({
url: this.$http.adornUrl('/sys/oss/upload'),
method: 'post',
data:{
url:url
}
}).then(({data})=>{
this.$emit('uploaded', url)
})
}
}
} }
}, </script>
mounted() {
// COS <style scoped>
this.$http({ </style>
url: this.$http.adornUrl('/sys/oss/config'),
method: 'get',
params: this.$http.adornParams() //
}).then(({ data }) => {
if (data && data.code === 200) {
//
this.cosConfig = data.config;
// COS
cos = new COS({
SecretId: data.config.qcloudSecretId,
SecretKey: data.config.qcloudSecretKey,
});
} else {
//
this.$message.error('请先配置云存储相关信息!');
}
})
},
methods: {
//
selectFile() {
//
if (!this.uploading) {
this.$refs.fileInput.click();
}
},
//
onFileChange() {
let file = this.$refs.fileInput.files[0]; //
this.uploading = true; // true
let now = new Date();
//
let path = now.toISOString().slice(0, 10) + '/' + now.getTime() + file.name.substr(file.name.lastIndexOf('.'));
// COS
cos.putObject({
Bucket: this.cosConfig.qcloudBucketName, //
Region: this.cosConfig.qcloudRegion, //
Key: path, //
Body: file, //
onProgress: (progressData) => { //
//
this.infoText = '上传中:' + (progressData.percent * 100).toFixed(2) + '%';
}
}, (err, data) => {
//
console.log(err || data);
this.uploading = false; // false
if (data) {
//
this.infoText = '上传文件';
// 访 URL
let fileUrl = 'https://' + this.cosConfig.qcloudBucketName + '.cos.' + this.cosConfig.qcloudRegion + '.myqcloud.com/' + path;
this.saveUploadResult(fileUrl); //
} else {
//
this.$message.error('文件上传失败', err);
}
});
},
// 访 URL
saveUploadResult(url) {
this.$http({
url: this.$http.adornUrl('/sys/oss/upload'),
method: 'post',
data: { url: url } // URL
}).then(({ data }) => {
// `uploaded` URL
this.$emit('uploaded', url);
})
}
}
}
</script>
<style scoped>
/* 样式部分为空 */
</style>

@ -1,73 +1,59 @@
<template> <template>
<!-- 点击外部div触发文件选择操作 -->
<div @click="selectFile"> <div @click="selectFile">
<!-- 隐藏的文件上传input选择文件后触发onFileChange方法 --> <input type="file" ref="fileInput" v-show="false" @change="onFileChange" />
<input type="file" ref="fileInput" v-show="false" @change="onFileChange" /> <div>{{uploading?infoText:'上传文件'}}</div>
<!-- 显示上传中的状态或默认的上传文件文本 -->
<div>{{uploading ? infoText : '上传文件'}}</div>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
name: "oss-uploader", // "oss-uploader" name: "oss-uploader",
data() { data() {
return { return {
uploading: false, // false uploading: false,
infoText: "上传中...", // infoText: "上传中...",
cosConfig: [] // cosConfig: []
} }
}, },
mounted() { mounted() {
// this.$http({
this.$http({ url: this.$http.adornUrl('/sys/oss/config'),
url: this.$http.adornUrl('/sys/oss/config'), // API method: 'get',
method: 'get', // 使GET params: this.$http.adornParams()
params: this.$http.adornParams() // }).then(({ data }) => {
}).then(({ data }) => { if (data && data.code === 200 && data.config.type) {
// this.cosConfig = data.config
if (data && data.code === 200 && data.config.type) { } else {
this.cosConfig = data.config // this.$message.error('请先配置云存储相关信息!')
} else { }
//
this.$message.error('请先配置云存储相关信息!') })
}
})
}, },
methods: { methods: {
// selectFile() {//
selectFile() { if (!this.uploading) {
if (!this.uploading) { this.$refs.fileInput.click();
// input }
this.$refs.fileInput.click(); },
} onFileChange() {
}, let file = this.$refs.fileInput.files[0];
// this.uploading = true;
onFileChange() { let formData = new FormData();
let file = this.$refs.fileInput.files[0]; // formData.append("file", file)
this.uploading = true; // true this.$http({
let formData = new FormData(); url: this.$http.adornUrl('/sys/oss/upload'),
formData.append("file", file); // formData method: 'post',
data: formData
// POST }).then(({ data }) => {
this.$http({ console.log(data)
url: this.$http.adornUrl('/sys/oss/upload'), // API if (data && data.code === 200) {
method: 'post', // 使POST this.$emit('uploaded', data.url)
data: formData // } else {
}).then(({ data }) => { this.$message.error("文件上传失败:" + data.msg)
// }
console.log(data); // 便 this.uploading = false;
if (data && data.code === 200) { })
// 'uploaded'url }
this.$emit('uploaded', data.url)
} else {
//
this.$message.error("文件上传失败:" + data.msg)
}
this.uploading = false; // false
})
}
}
} }
</script> }
</script>

@ -39,79 +39,108 @@
<script> <script>
export default { export default {
data() { data() {
return { return {
// dataForm: {},
dataForm: {}, dataList: [],
// pageIndex: 1,
dataList: [], pageSize: 10,
// totalCount: 0,
pageIndex: 1, dataListLoading: false,
// dataListSelections: [],
pageSize: 10, configVisible: false,
// uploadVisible: false
totalCount: 0, }
// },
dataListLoading: false, components: {
// Config: () => import('./oss-config'),
dataListSelections: [], OssUploader: () => import('./oss-uploader')
// },
configVisible: false, activated() {
// this.getDataList()
uploadVisible: false },
} methods: {
}, //
components: { getDataList() {
// this.dataListLoading = true
Config: () => import('./oss-config'), this.$http({
// url: this.$http.adornUrl('/sys/oss/list'),
OssUploader: () => import('./oss-uploader') method: 'get',
}, params: this.$http.adornParams({
// 'page': this.pageIndex,
activated() { 'limit': this.pageSize,
this.getDataList(); 'sidx': 'id',
}, 'order': 'desc'
methods: { })
// }).then(({ data }) => {
getDataList() { if (data && data.code === 200) {
this.dataListLoading = true; this.dataList = data.page.list
this.$http({ this.totalCount = data.page.totalCount
url: this.$http.adornUrl('/sys/oss/list'), } else {
method: 'get', this.dataList = []
params: this.$http.adornParams({ this.totalCount = 0
'page': this.pageIndex, // }
'limit': this.pageSize, // this.dataListLoading = false
'sidx': 'id', // })
'order': 'desc' // },
}) //
}).then(({ data }) => { sizeChangeHandle(val) {
// dataListtotalCount this.pageSize = val
if (data && data.code === 200) { this.pageIndex = 1
this.dataList = data.page.list; this.getDataList()
this.totalCount = data.page.totalCount; },
} else { //
// currentChangeHandle(val) {
this.dataList = []; this.pageIndex = val
this.totalCount = 0; this.getDataList()
} },
this.dataListLoading = false; // //
}); selectionChangeHandle(val) {
}, this.dataListSelections = val
// },
sizeChangeHandle(val) { //
this.pageSize = val; configHandle() {
this.pageIndex = 1; // this.configVisible = true
this.getDataList(); // this.$nextTick(() => {
}, this.$refs.config.init()
// })
currentChangeHandle(val) { },
this.pageIndex = val; //
this.getDataList(); // uploadHandle() {
}, this.uploadVisible = true
// this.$nextTick(() => {
selectionChangeHandle(val) { this.$refs.upload.init()
this.dataListSelections = val; })
}, },
} //
deleteHandle(id) {
var ids = id ? [id] : this.dataListSelections.map(item => item.id)
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/sys/oss/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({ data }) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => this.getDataList()
})
} else {
this.$message.error(data.msg)
}
})
}).catch(() => { })
},
isImageUrl(url) {
return url && /.*\.(gif|jpg|jpeg|png|GIF|JPEG|JPG|PNG)/.test(url)
}
}
} }
</script> </script>

@ -1,117 +1,96 @@
<template> <template>
<el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible"> <el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible">
<!-- 弹窗组件显示标题是新增修改取决于dataForm.id --> <el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px"> <el-form-item label="参数名" prop="paramKey">
<!-- 表单组件绑定数据模型为dataForm表单校验规则为dataRule按回车键提交表单 --> <el-input v-model="dataForm.paramKey" placeholder="参数名"></el-input>
</el-form-item>
<el-form-item label="参数名" prop="paramKey"> <el-form-item label="参数值" prop="paramValue">
<!-- 参数名输入框验证参数名 --> <el-input v-model="dataForm.paramValue" placeholder="参数值"></el-input>
<el-input v-model="dataForm.paramKey" placeholder="参数名"></el-input> </el-form-item>
</el-form-item> <el-form-item label="备注" prop="remark">
<el-input v-model="dataForm.remark" placeholder="备注"></el-input>
<el-form-item label="参数值" prop="paramValue"> </el-form-item>
<!-- 参数值输入框验证参数值 --> </el-form>
<el-input v-model="dataForm.paramValue" placeholder="参数值"></el-input> <span slot="footer" class="dialog-footer">
</el-form-item> <el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()"></el-button>
<el-form-item label="备注" prop="remark"> </span>
<!-- 备注输入框 --> </el-dialog>
<el-input v-model="dataForm.remark" placeholder="备注"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<!-- 弹窗底部按钮 -->
<el-button @click="visible = false">取消</el-button>
<!-- 取消按钮点击时关闭弹窗 -->
<el-button type="primary" @click="dataFormSubmit()"></el-button>
<!-- 确定按钮点击时提交表单 -->
</span>
</el-dialog>
</template> </template>
<script> <script>
export default { export default {
data() { data() {
return { return {
visible: false, // visible: false,
dataForm: { dataForm: {
id: 0, // ID0ID id: 0,
paramKey: '', // paramKey: '',
paramValue: '',// paramValue: '',
remark: '' // remark: ''
}, },
dataRule: { dataRule: {
paramKey: [ paramKey: [
{ required: true, message: '参数名不能为空', trigger: 'blur' } { required: true, message: '参数名不能为空', trigger: 'blur' }
], ],
paramValue: [ paramValue: [
{ required: true, message: '参数值不能为空', trigger: 'blur' } { required: true, message: '参数值不能为空', trigger: 'blur' }
] ]
} }
} }
}, },
methods: { methods: {
// ID init(id) {
init(id) { this.dataForm.id = id || 0
this.dataForm.id = id || 0 this.visible = true
this.visible = true this.$nextTick(() => {
this.$nextTick(() => { this.$refs['dataForm'].resetFields()
this.$refs['dataForm'].resetFields() // if (this.dataForm.id) {
if (this.dataForm.id) { this.$http({
// url: this.$http.adornUrl(`/sys/config/info/${this.dataForm.id}`),
this.$http({ method: 'get',
url: this.$http.adornUrl(`/sys/config/info/${this.dataForm.id}`), params: this.$http.adornParams()
method: 'get', }).then(({ data }) => {
params: this.$http.adornParams() if (data && data.code === 200) {
}).then(({ data }) => { this.dataForm.paramKey = data.config.paramKey
if (data && data.code === 200) { this.dataForm.paramValue = data.config.paramValue
this.dataForm.paramKey = data.config.paramKey this.dataForm.remark = data.config.remark
this.dataForm.paramValue = data.config.paramValue }
this.dataForm.remark = data.config.remark })
}
})
},
//
dataFormSubmit() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.$http({
url: this.$http.adornUrl(`/sys/config/${!this.dataForm.id ? 'save' : 'update'}`),
method: 'post',
data: this.$http.adornData({
'id': this.dataForm.id || undefined,
'paramKey': this.dataForm.paramKey,
'paramValue': this.dataForm.paramValue,
'remark': this.dataForm.remark
})
}).then(({ data }) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false
this.$emit('refreshDataList')
}
})
} else {
this.$message.error(data.msg)
}
})
}
})
}
}
} }
})
}
})
},
//
dataFormSubmit() {
//
this.$refs['dataForm'].validate((valid) => {
if (valid) {
//
this.$http({
url: this.$http.adornUrl(`/sys/config/${!this.dataForm.id ? 'save' : 'update'}`),
method: 'post',
data: this.$http.adornData({
'id': this.dataForm.id || undefined, // IDID
'paramKey': this.dataForm.paramKey,
'paramValue': this.dataForm.paramValue,
'remark': this.dataForm.remark
})
}).then(({ data }) => {
if (data && data.code === 200) {
//
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false
this.$emit('refreshDataList') //
}
})
} else {
//
this.$message.error(data.msg)
}
})
}
})
}
}
}
</script> </script>

@ -1,172 +1,134 @@
<template> <template>
<div class="mod-config"> <div class="mod-config">
<!-- 表单部分包含参数名的查询框和操作按钮 --> <el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()"> <el-form-item>
<!-- 表单项使用 inline 布局按回车触发查询 --> <el-input v-model="dataForm.paramKey" placeholder="参数名" clearable></el-input>
<el-form-item> </el-form-item>
<el-input v-model="dataForm.paramKey" placeholder="参数名" clearable></el-input> <el-form-item>
<!-- 输入框绑定 model dataForm.paramKey允许清除内容 --> <el-button @click="getDataList()"></el-button>
</el-form-item> <el-button type="primary" @click="addOrUpdateHandle()"></el-button>
<el-form-item> <el-button type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量删除</el-button>
<!-- 查询按钮点击时触发 getDataList 方法 --> </el-form-item>
<el-button @click="getDataList()"></el-button> </el-form>
<!-- 新增按钮点击时触发 addOrUpdateHandle 方法 --> <el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;">
<el-button type="primary" @click="addOrUpdateHandle()"></el-button> <el-table-column type="selection" header-align="center" align="center" width="50">
<!-- 批量删除按钮点击时触发 deleteHandle 方法只有当有选中的数据时可用 --> </el-table-column>
<el-button type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量删除</el-button> <el-table-column prop="id" header-align="center" align="center" width="80" label="ID">
</el-form-item> </el-table-column>
</el-form> <el-table-column prop="paramKey" header-align="center" align="center" label="参数名">
</el-table-column>
<!-- 表格部分用于显示数据列表 --> <el-table-column prop="paramValue" header-align="center" align="center" label="参数值">
<el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;"> </el-table-column>
<!-- 多选列 --> <el-table-column prop="remark" header-align="center" align="center" label="备注">
<el-table-column type="selection" header-align="center" align="center" width="50"></el-table-column> </el-table-column>
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<!-- ID列 --> <template slot-scope="scope">
<el-table-column prop="id" header-align="center" align="center" width="80" label="ID"></el-table-column> <el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.id)"></el-button>
<el-button type="text" size="small" @click="deleteHandle(scope.row.id)"></el-button>
<!-- 参数名列 --> </template>
<el-table-column prop="paramKey" header-align="center" align="center" label="参数名"></el-table-column> </el-table-column>
</el-table>
<!-- 参数值列 --> <el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
<el-table-column prop="paramValue" header-align="center" align="center" label="参数值"></el-table-column> </el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<!-- 备注列 --> <add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
<el-table-column prop="remark" header-align="center" align="center" label="备注"></el-table-column> </div>
<!-- 操作列包含修改和删除按钮 -->
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope">
<!-- 修改按钮点击时触发 addOrUpdateHandle 方法传入当前行的 id -->
<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.id)"></el-button>
<!-- 删除按钮点击时触发 deleteHandle 方法传入当前行的 id -->
<el-button type="text" size="small" @click="deleteHandle(scope.row.id)"></el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗组件用于新增或修改数据 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div>
</template> </template>
<script> <script>
import AddOrUpdate from './config-add-or-update' import AddOrUpdate from './config-add-or-update'
export default { export default {
data() { data() {
return { return {
// dataForm: {
dataForm: { paramKey: ''
paramKey: '' },
}, dataList: [],
// pageIndex: 1,
dataList: [], pageSize: 10,
// totalCount: 0,
pageIndex: 1, dataListLoading: false,
// dataListSelections: [],
pageSize: 10, addOrUpdateVisible: false
// }
totalCount: 0, },
// components: {
dataListLoading: false, AddOrUpdate
// },
dataListSelections: [], activated() {
// / this.getDataList()
addOrUpdateVisible: false },
methods: {
//
getDataList() {
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/sys/config/list'),
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex,
'limit': this.pageSize,
'paramKey': this.dataForm.paramKey
})
}).then(({ data }) => {
if (data && data.code === 200) {
this.dataList = data.page.list
this.totalCount = data.page.totalCount
} else {
this.dataList = []
this.totalCount = 0
}
this.dataListLoading = false
})
},
//
sizeChangeHandle(val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
//
currentChangeHandle(val) {
this.pageIndex = val
this.getDataList()
},
//
selectionChangeHandle(val) {
this.dataListSelections = val
},
// /
addOrUpdateHandle(id) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id)
})
},
//
deleteHandle(id) {
var ids = id ? [id] : this.dataListSelections.map(item => item.id)
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/sys/config/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({ data }) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => this.getDataList()
})
} else {
this.$message.error(data.msg)
}
})
}).catch(() => { })
}
}
} }
},
components: {
AddOrUpdate
},
activated() {
//
this.getDataList()
},
methods: {
//
getDataList() {
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/sys/config/list'),
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex,
'limit': this.pageSize,
'paramKey': this.dataForm.paramKey
})
}).then(({ data }) => {
if (data && data.code === 200) {
this.dataList = data.page.list
this.totalCount = data.page.totalCount
} else {
this.dataList = []
this.totalCount = 0
}
this.dataListLoading = false
})
},
//
sizeChangeHandle(val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
//
currentChangeHandle(val) {
this.pageIndex = val
this.getDataList()
},
//
selectionChangeHandle(val) {
this.dataListSelections = val
},
// /
addOrUpdateHandle(id) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
// /
this.$refs.addOrUpdate.init(id)
})
},
//
deleteHandle(id) {
var ids = id ? [id] : this.dataListSelections.map(item => item.id)
//
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/sys/config/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({ data }) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => this.getDataList()
})
} else {
this.$message.error(data.msg)
}
})
}).catch(() => { })
}
}
}
</script> </script>

@ -1,107 +1,90 @@
<template> <template>
<div class="mod-log"> <div class="mod-log">
<!-- 搜索表单部分 --> <el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()"> <el-form-item>
<!-- 输入框绑定到 dataForm.key用于搜索用户操作或者用户名 --> <el-input v-model="dataForm.key" placeholder="用户名/用户操作" clearable></el-input>
<el-form-item> </el-form-item>
<el-input v-model="dataForm.key" placeholder="用户名/用户操作" clearable></el-input> <el-form-item>
</el-form-item> <el-button @click="getDataList()"></el-button>
<el-form-item> </el-form-item>
<!-- 查询按钮点击时触发 getDataList 方法 --> </el-form>
<el-button @click="getDataList()"></el-button> <el-table :data="dataList" border v-loading="dataListLoading" style="width: 100%">
</el-form-item> <el-table-column prop="id" header-align="center" align="center" width="80" label="ID">
</el-form> </el-table-column>
<el-table-column prop="username" header-align="center" align="center" label="用户名">
<!-- 表格显示数据 --> </el-table-column>
<el-table :data="dataList" border v-loading="dataListLoading" style="width: 100%"> <el-table-column prop="operation" header-align="center" align="center" label="用户操作">
<!-- 表格的列定义 --> </el-table-column>
<el-table-column prop="id" header-align="center" align="center" width="80" label="ID"></el-table-column> <el-table-column prop="method" header-align="center" align="center" width="150" :show-overflow-tooltip="true" label="请求方法">
<el-table-column prop="username" header-align="center" align="center" label="用户名"></el-table-column> </el-table-column>
<el-table-column prop="operation" header-align="center" align="center" label="用户操作"></el-table-column> <el-table-column prop="params" header-align="center" align="center" width="150" :show-overflow-tooltip="true" label="请求参数">
<el-table-column prop="method" header-align="center" align="center" width="150" :show-overflow-tooltip="true" label="请求方法"></el-table-column> </el-table-column>
<el-table-column prop="params" header-align="center" align="center" width="150" :show-overflow-tooltip="true" label="请求参数"></el-table-column> <el-table-column prop="time" header-align="center" align="center" label="执行时长(毫秒)">
<el-table-column prop="time" header-align="center" align="center" label="执行时长(毫秒)"></el-table-column> </el-table-column>
<el-table-column prop="ip" header-align="center" align="center" width="150" label="IP地址"></el-table-column> <el-table-column prop="ip" header-align="center" align="center" width="150" label="IP地址">
<el-table-column prop="createDate" header-align="center" align="center" width="180" label="创建时间"></el-table-column> </el-table-column>
</el-table> <el-table-column prop="createDate" header-align="center" align="center" width="180" label="创建时间">
</el-table-column>
<!-- 分页组件 --> </el-table>
<el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper"> <el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
</el-pagination> </el-pagination>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
data() { data() {
return { return {
// 'key' dataForm: {
dataForm: { key: ''
key: '' },
}, dataList: [],
// pageIndex: 1,
dataList: [], pageSize: 10,
// totalCount: 0,
pageIndex: 1, dataListLoading: false,
// selectionDataList: []
pageSize: 10, }
// },
totalCount: 0, created() {
// this.getDataList()
dataListLoading: false, },
// 使 methods: {
selectionDataList: [] //
} getDataList() {
}, this.dataListLoading = true
created() { this.$http({
// url: this.$http.adornUrl('/sys/log/list'),
this.getDataList() method: 'get',
}, params: this.$http.adornParams({
methods: { 'page': this.pageIndex,
// 'limit': this.pageSize,
getDataList() { 'key': this.dataForm.key,
// 'sidx': 'id',
this.dataListLoading = true 'order': 'desc'
this.$http({ })
url: this.$http.adornUrl('/sys/log/list'), // }).then(({ data }) => {
method: 'get', if (data && data.code === 200) {
params: this.$http.adornParams({ this.dataList = data.page.list
// this.totalCount = data.page.totalCount
'page': this.pageIndex, } else {
'limit': this.pageSize, this.dataList = []
'key': this.dataForm.key, // this.totalCount = 0
'sidx': 'id', // id }
'order': 'desc' // this.dataListLoading = false
}) })
}).then(({ data }) => { },
// //
if (data && data.code === 200) { sizeChangeHandle(val) {
this.dataList = data.page.list // this.pageSize = val
this.totalCount = data.page.totalCount // this.pageIndex = 1
} else { this.getDataList()
// },
this.dataList = [] //
this.totalCount = 0 currentChangeHandle(val) {
} this.pageIndex = val
// this.getDataList()
this.dataListLoading = false }
}) }
},
//
sizeChangeHandle(val) {
this.pageSize = val //
this.pageIndex = 1 //
this.getDataList() //
},
//
currentChangeHandle(val) {
this.pageIndex = val //
this.getDataList() //
}
} }
}
</script> </script>

@ -1,253 +1,218 @@
<template> <template>
<!-- 弹窗组件标题根据dataForm.id判断是新增还是修改 -->
<el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible"> <el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible">
<!-- 表单组件绑定数据模型和验证规则 --> <el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px"> <el-form-item label="类型" prop="type">
<el-radio-group v-model="dataForm.type">
<!-- 菜单类型选择 --> <el-radio v-for="(type, index) in dataForm.typeList" :label="index" :key="index">{{ type }}</el-radio>
<el-form-item label="类型" prop="type"> </el-radio-group>
<el-radio-group v-model="dataForm.type"> </el-form-item>
<!-- 循环渲染可选择的菜单类型 --> <el-form-item :label="dataForm.typeList[dataForm.type] + '名称'" prop="name">
<el-radio v-for="(type, index) in dataForm.typeList" :label="index" :key="index">{{ type }}</el-radio> <el-input v-model="dataForm.name" :placeholder="dataForm.typeList[dataForm.type] + '名称'"></el-input>
</el-radio-group> </el-form-item>
</el-form-item> <el-form-item label="上级菜单" prop="parentName">
<el-popover ref="menuListPopover" placement="bottom-start" trigger="click">
<!-- 菜单名称输入框 --> <el-tree :data="menuList" :props="menuListTreeProps" node-key="menuId" ref="menuListTree" @current-change="menuListTreeCurrentChangeHandle" :default-expand-all="true" :highlight-current="true" :expand-on-click-node="false">
<el-form-item :label="dataForm.typeList[dataForm.type] + '名称'" prop="name"> </el-tree>
<el-input v-model="dataForm.name" :placeholder="dataForm.typeList[dataForm.type] + '名称'"></el-input> </el-popover>
</el-form-item> <el-input v-model="dataForm.parentName" v-popover:menuListPopover :readonly="true" placeholder="点击选择上级菜单" class="menu-list__input"></el-input>
</el-form-item>
<!-- 上级菜单选择 --> <el-form-item v-if="dataForm.type === 1" label="菜单路由" prop="url">
<el-form-item label="上级菜单" prop="parentName"> <el-input v-model="dataForm.url" placeholder="菜单路由"></el-input>
<!-- 弹出菜单树选择 --> </el-form-item>
<el-popover ref="menuListPopover" placement="bottom-start" trigger="click"> <el-form-item v-if="dataForm.type !== 0" label="授权标识" prop="perms">
<el-tree :data="menuList" :props="menuListTreeProps" node-key="menuId" ref="menuListTree" <el-input v-model="dataForm.perms" placeholder="多个用逗号分隔, 如: user:list,user:create"></el-input>
@current-change="menuListTreeCurrentChangeHandle" :default-expand-all="true" </el-form-item>
:highlight-current="true" :expand-on-click-node="false">
</el-tree> <el-form-item v-if="dataForm.type !== 2" label="菜单图标" prop="icon">
</el-popover> <el-row>
<!-- 显示上级菜单选择 --> <el-col :span="12">
<el-input v-model="dataForm.parentName" v-popover:menuListPopover :readonly="true" <el-input v-model="dataForm.icon" placeholder="菜单图标名称" class="icon-list__input"></el-input>
placeholder="点击选择上级菜单" class="menu-list__input"></el-input> </el-col>
</el-form-item> <el-col :span="12" class="icon-list__tips">
<el-form-item v-if="dataForm.type !== 2" label="排序号" prop="orderNum">
<!-- 菜单路由仅在选择菜单类型时显示 --> <el-input-number v-model="dataForm.orderNum" controls-position="right" :min="0" label="排序号"></el-input-number>
<el-form-item v-if="dataForm.type === 1" label="菜单路由" prop="url"> </el-form-item>
<el-input v-model="dataForm.url" placeholder="菜单路由"></el-input> </el-col>
</el-form-item> </el-row>
</el-form-item>
<!-- 授权标识仅在类型不为目录时显示 --> <div>参考ElementUI图标库, <a href="https://element.eleme.cn/#/zh-CN/component/icon" target="_blank">找图标</a></div>
<el-form-item v-if="dataForm.type !== 0" label="授权标识" prop="perms"> </el-form>
<el-input v-model="dataForm.perms" placeholder="多个用逗号分隔, 如: user:list,user:create"></el-input> <span slot="footer" class="dialog-footer">
</el-form-item> <el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()"></el-button>
<!-- 菜单图标仅在类型不为按钮时显示 --> </span>
<el-form-item v-if="dataForm.type !== 2" label="菜单图标" prop="icon">
<el-row>
<el-col :span="12">
<el-input v-model="dataForm.icon" placeholder="菜单图标名称" class="icon-list__input"></el-input>
</el-col>
<el-col :span="12" class="icon-list__tips">
<!-- 排序号输入框仅在类型不为按钮时显示 -->
<el-form-item v-if="dataForm.type !== 2" label="排序号" prop="orderNum">
<el-input-number v-model="dataForm.orderNum" controls-position="right" :min="0" label="排序号"></el-input-number>
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<!-- 提示用户参考图标库 -->
<div>参考ElementUI图标库, 找图标</div>
</el-form>
<!-- 弹窗底部按钮 -->
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()"></el-button>
</span>
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
// import { treeDataTranslate } from '@/utils'
import { treeDataTranslate } from '@/utils' export default {
export default {
data() { data() {
// URL var validateUrl = (rule, value, callback) => {
var validateUrl = (rule, value, callback) => { if (this.dataForm.type === 1 && !/\S/.test(value)) {
// URL callback(new Error('菜单URL不能为空'))
if (this.dataForm.type === 1 && !/\S/.test(value)) { } else {
callback(new Error('菜单URL不能为空')) callback()
} else { }
callback() // }
} return {
} visible: false,
return { dataForm: {
visible: false, // id: 0,
dataForm: { type: 1,
id: 0, // ID0 typeList: ['目录', '菜单', '按钮'],
type: 1, // 0-1-2- name: '',
typeList: ['目录', '菜单', '按钮'], // parentId: 0,
name: '', // parentName: '',
parentId: 0, // ID url: '',
parentName: '', // perms: '',
url: '', // orderNum: 0,
perms: '', // icon: '',
orderNum: 0, // },
icon: '', // dataRule: {
}, name: [
// { required: true, message: '菜单名称不能为空', trigger: 'blur' }
dataRule: { ],
name: [ parentName: [
{ required: true, message: '菜单名称不能为空', trigger: 'blur' } // { required: true, message: '上级菜单不能为空', trigger: 'change' }
], ],
parentName: [ url: [
{ required: true, message: '上级菜单不能为空', trigger: 'change' } // { validator: validateUrl, trigger: 'blur' }
], ]
url: [ },
{ validator: validateUrl, trigger: 'blur' } // URL menuList: [],
] menuListTreeProps: {
}, label: 'name',
menuList: [], // 使 children: 'children'
menuListTreeProps: { }
label: 'name', // }
children: 'children' //
}
}
}, },
methods: { methods: {
// init(id) {
init(id) { this.dataForm.id = id || 0
this.dataForm.id = id || 0 // ID this.$http({
this.$http({ url: this.$http.adornUrl('/sys/menu/select'),
url: this.$http.adornUrl('/sys/menu/select'), method: 'get',
method: 'get', params: this.$http.adornParams()
params: this.$http.adornParams() }).then(({ data }) => {
}).then(({ data }) => { this.menuList = treeDataTranslate(data.menuList, 'menuId')
// }).then(() => {
this.menuList = treeDataTranslate(data.menuList, 'menuId') this.visible = true
}).then(() => { this.$nextTick(() => {
this.visible = true // this.$refs['dataForm'].resetFields()
this.$nextTick(() => { })
this.$refs['dataForm'].resetFields() // }).then(() => {
}) if (!this.dataForm.id) {
}).then(() => { //
if (!this.dataForm.id) { this.menuListTreeSetCurrentNode()
// } else {
this.menuListTreeSetCurrentNode() //
} else { this.$http({
// url: this.$http.adornUrl(`/sys/menu/info/${this.dataForm.id}`),
this.$http({ method: 'get',
url: this.$http.adornUrl(`/sys/menu/info/${this.dataForm.id}`), params: this.$http.adornParams()
method: 'get', }).then(({ data }) => {
params: this.$http.adornParams() this.dataForm.id = data.menu.menuId
}).then(({ data }) => { this.dataForm.type = data.menu.type
// this.dataForm.name = data.menu.name
this.dataForm.id = data.menu.menuId this.dataForm.parentId = data.menu.parentId
this.dataForm.type = data.menu.type this.dataForm.url = data.menu.url
this.dataForm.name = data.menu.name this.dataForm.perms = data.menu.perms
this.dataForm.parentId = data.menu.parentId this.dataForm.orderNum = data.menu.orderNum
this.dataForm.url = data.menu.url this.dataForm.icon = data.menu.icon
this.dataForm.perms = data.menu.perms this.menuListTreeSetCurrentNode()
this.dataForm.orderNum = data.menu.orderNum })
this.dataForm.icon = data.menu.icon }
this.menuListTreeSetCurrentNode() // })
}) },
} //
}) menuListTreeCurrentChangeHandle(data, node) {
}, this.dataForm.parentId = data.menuId
// this.dataForm.parentName = data.name
menuListTreeCurrentChangeHandle(data, node) { },
this.dataForm.parentId = data.menuId // ID //
this.dataForm.parentName = data.name // menuListTreeSetCurrentNode() {
}, this.$refs.menuListTree.setCurrentKey(this.dataForm.parentId)
// this.dataForm.parentName = (this.$refs.menuListTree.getCurrentNode() || {})['name']
menuListTreeSetCurrentNode() { },
this.$refs.menuListTree.setCurrentKey(this.dataForm.parentId) // //
this.dataForm.parentName = (this.$refs.menuListTree.getCurrentNode() || {})['name'] // dataFormSubmit() {
}, this.$refs['dataForm'].validate((valid) => {
// if (valid) {
dataFormSubmit() { this.$http({
this.$refs['dataForm'].validate((valid) => { url: this.$http.adornUrl(`/sys/menu/${!this.dataForm.id ? 'save' : 'update'}`),
if (valid) { method: 'post',
// data: this.$http.adornData({
this.$http({ 'menuId': this.dataForm.id || undefined,
url: this.$http.adornUrl(`/sys/menu/${!this.dataForm.id ? 'save' : 'update'}`), 'type': this.dataForm.type,
method: 'post', 'name': this.dataForm.name,
data: this.$http.adornData({ 'parentId': this.dataForm.parentId,
'menuId': this.dataForm.id || undefined, 'url': this.dataForm.url,
'type': this.dataForm.type, 'perms': this.dataForm.perms,
'name': this.dataForm.name, 'orderNum': this.dataForm.orderNum,
'parentId': this.dataForm.parentId, 'icon': this.dataForm.icon
'url': this.dataForm.url, })
'perms': this.dataForm.perms, }).then(({ data }) => {
'orderNum': this.dataForm.orderNum, if (data && data.code === 200) {
'icon': this.dataForm.icon this.$message({
}) message: '操作成功',
}).then(({ data }) => { type: 'success',
if (data && data.code === 200) { duration: 1500,
// onClose: () => {
this.$message({ this.visible = false
message: '操作成功', this.$emit('refreshDataList')
type: 'success', }
duration: 1500, })
onClose: () => { } else {
this.visible = false // this.$message.error(data.msg)
this.$emit('refreshDataList') // }
} })
}) }
} else { })
// }
this.$message.error(data.msg) }
} }
}) </script>
}
}) <style lang="scss">
} .mod-menu {
}
}
</script>
<style lang="scss">
.mod-menu {
.menu-list__input, .menu-list__input,
.icon-list__input { .icon-list__input {
> .el-input__inner { > .el-input__inner {
cursor: pointer; // cursor: pointer;
} }
} }
&__icon-popover { &__icon-popover {
width: 458px; // width: 458px;
overflow: hidden; // overflow: hidden;
} }
&__icon-inner { &__icon-inner {
width: 478px; // width: 478px;
max-height: 258px; // max-height: 258px;
overflow-x: hidden; // overflow-x: hidden;
overflow-y: auto; // overflow-y: auto;
} }
&__icon-list { &__icon-list {
width: 458px; // width: 458px;
padding: 0; // padding: 0;
margin: -8px 0 0 -8px; // margin: -8px 0 0 -8px;
> .el-button { > .el-button {
padding: 8px; // padding: 8px;
margin: 8px 0 0 8px; // margin: 8px 0 0 8px;
> span { > span {
display: inline-block; // 使 display: inline-block;
vertical-align: middle; // vertical-align: middle;
width: 18px; // width: 18px;
height: 18px; // height: 18px;
font-size: 18px; // font-size: 18px;
} }
} }
} }
.icon-list__tips { .icon-list__tips {
font-size: 18px; // font-size: 18px;
text-align: center; // text-align: center;
color: #e6a23c; // color: #e6a23c;
cursor: pointer; // cursor: pointer;
}
} }
</style> }
</style>

@ -1,148 +1,109 @@
<template> <template>
<!-- 菜单管理模块 -->
<div class="mod-menu"> <div class="mod-menu">
<!-- 表单组件内联布局 --> <el-form :inline="true" :model="dataForm">
<el-form :inline="true" :model="dataForm"> <el-form-item>
<el-form-item> <el-button v-if="isAuth('sys:menu:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
<!-- 如果有新增权限显示新增按钮 --> </el-form-item>
<el-button v-if="isAuth('sys:menu:save')" type="primary" @click="addOrUpdateHandle()"></el-button> </el-form>
</el-form-item>
</el-form> <el-table :data="dataList" row-key="menuId" border style="width: 100%; ">
<el-table-column prop="name" header-align="center" min-width="150" label="名称">
<!-- 菜单数据表格 --> </el-table-column>
<el-table :data="dataList" row-key="menuId" border style="width: 100%;"> <el-table-column prop="parentName" header-align="center" align="center" width="120" label="上级菜单">
<!-- 菜单名称列 --> </el-table-column>
<el-table-column prop="name" header-align="center" min-width="150" label="名称"> <el-table-column header-align="center" align="center" label="图标">
</el-table-column> <template slot-scope="scope">
<i :class="scope.row.icon"></i>
<!-- 上级菜单列 --> </template>
<el-table-column prop="parentName" header-align="center" align="center" width="120" label="上级菜单"> </el-table-column>
</el-table-column> <el-table-column prop="type" header-align="center" align="center" label="类型">
<template slot-scope="scope">
<!-- 菜单图标列 --> <el-tag v-if="scope.row.type === 0" size="small"></el-tag>
<el-table-column header-align="center" align="center" label="图标"> <el-tag v-else-if="scope.row.type === 1" size="small" type="success">菜单</el-tag>
<template slot-scope="scope"> <el-tag v-else-if="scope.row.type === 2" size="small" type="info">按钮</el-tag>
<!-- 根据图标类渲染图标 --> </template>
<i :class="scope.row.icon"></i> </el-table-column>
</template> <el-table-column prop="orderNum" header-align="center" align="center" label="排序号">
</el-table-column> </el-table-column>
<el-table-column prop="url" header-align="center" align="center" width="150" :show-overflow-tooltip="true" label="菜单URL">
<!-- 菜单类型列 --> </el-table-column>
<el-table-column prop="type" header-align="center" align="center" label="类型"> <el-table-column prop="perms" header-align="center" align="center" width="150" :show-overflow-tooltip="true" label="授权标识">
<template slot-scope="scope"> </el-table-column>
<!-- 根据菜单类型渲染不同的标签 --> <el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<el-tag v-if="scope.row.type === 0" size="small"></el-tag> <template slot-scope="scope">
<el-tag v-else-if="scope.row.type === 1" size="small" type="success">菜单</el-tag> <el-button v-if="isAuth('sys:menu:update')" type="text" size="small" @click="addOrUpdateHandle(scope.row.menuId)"></el-button>
<el-tag v-else-if="scope.row.type === 2" size="small" type="info">按钮</el-tag> <el-button v-if="isAuth('sys:menu:delete')" type="text" size="small" @click="deleteHandle(scope.row.menuId)"></el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table>
<!-- 排序号列 --> <!-- 弹窗, 新增 / 修改 -->
<el-table-column prop="orderNum" header-align="center" align="center" label="排序号"> <add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</el-table-column>
<!-- 菜单URL列支持溢出提示 -->
<el-table-column prop="url" header-align="center" align="center" width="150" :show-overflow-tooltip="true" label="菜单URL">
</el-table-column>
<!-- 授权标识列支持溢出提示 -->
<el-table-column prop="perms" header-align="center" align="center" width="150" :show-overflow-tooltip="true" label="授权标识">
</el-table-column>
<!-- 操作列显示修改和删除按钮 -->
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope">
<!-- 如果有修改权限显示修改按钮 -->
<el-button v-if="isAuth('sys:menu:update')" type="text" size="small" @click="addOrUpdateHandle(scope.row.menuId)"></el-button>
<!-- 如果有删除权限显示删除按钮 -->
<el-button v-if="isAuth('sys:menu:delete')" type="text" size="small" @click="deleteHandle(scope.row.menuId)"></el-button>
</template>
</el-table-column>
</el-table>
<!-- 弹窗组件用于新增或修改菜单 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div> </div>
</template> </template>
<script> <script>
// / import AddOrUpdate from './menu-add-or-update'
import AddOrUpdate from './menu-add-or-update' import { treeDataTranslate } from '@/utils'
import { treeDataTranslate } from '@/utils' export default {
export default {
data() { data() {
return { return {
dataForm: {}, // dataForm: {},
dataList: [], // dataList: [],
dataListLoading: false, // dataListLoading: false,
addOrUpdateVisible: false // addOrUpdateVisible: false
} }
}, },
components: { components: {
AddOrUpdate // / AddOrUpdate
}, },
activated() { activated() {
// this.getDataList()
this.getDataList()
}, },
methods: { methods: {
// //
getDataList() { getDataList() {
this.dataListLoading = true // this.dataListLoading = true
// HTTP this.$http({
this.$http({ url: this.$http.adornUrl('/sys/menu/list'),
url: this.$http.adornUrl('/sys/menu/list'), method: 'get',
method: 'get', params: this.$http.adornParams()
params: this.$http.adornParams() }).then(({ data }) => {
}).then(({ data }) => { this.dataList = treeDataTranslate(data, 'menuId')
// 使 this.dataListLoading = false
this.dataList = treeDataTranslate(data, 'menuId') })
this.dataListLoading = false // },
}) // /
}, addOrUpdateHandle(id) {
this.addOrUpdateVisible = true
// this.$nextTick(() => {
addOrUpdateHandle(id) { this.$refs.addOrUpdate.init(id)
this.addOrUpdateVisible = true // / })
this.$nextTick(() => { },
// //
this.$refs.addOrUpdate.init(id) deleteHandle(id) {
}) this.$confirm(`确定对[id=${id}]进行[删除]操作?`, '提示', {
}, confirmButtonText: '确定',
cancelButtonText: '取消',
// type: 'warning'
deleteHandle(id) { }).then(() => {
// this.$http({
this.$confirm(`确定对[id=${id}]进行[删除]操作?`, '提示', { url: this.$http.adornUrl(`/sys/menu/delete/${id}`),
confirmButtonText: '确定', method: 'post',
cancelButtonText: '取消', data: this.$http.adornData()
type: 'warning' // }).then(({ data }) => {
}).then(() => { if (data && data.code === 200) {
// this.$message({
this.$http({ message: '操作成功',
url: this.$http.adornUrl(`/sys/menu/delete/${id}`), type: 'success',
method: 'post', duration: 1500,
data: this.$http.adornData() onClose: () => this.getDataList()
}).then(({ data }) => { })
if (data && data.code === 200) { } else {
// this.$message.error(data.msg)
this.$message({ }
message: '操作成功', })
type: 'success', }).catch(() => { })
duration: 1500, }
onClose: () => this.getDataList() //
})
} else {
//
this.$message.error(data.msg)
}
})
}).catch(() => {
//
})
}
}
} }
</script> }
</script>

@ -1,148 +1,111 @@
<template> <template>
<!-- 弹窗组件显示角色的新增或修改表单 -->
<el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible"> <el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible">
<!-- 表单组件绑定数据和验证规则 --> <el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px"> <el-form-item label="角色名称" prop="roleName">
<el-input v-model="dataForm.roleName" placeholder="角色名称"></el-input>
<!-- 角色名称输入框 --> </el-form-item>
<el-form-item label="角色名称" prop="roleName"> <el-form-item label="备注" prop="remark">
<el-input v-model="dataForm.roleName" placeholder="角色名称"></el-input> <el-input v-model="dataForm.remark" placeholder="备注"></el-input>
</el-form-item> </el-form-item>
<el-form-item size="mini" label="授权">
<!-- 备注输入框 --> <el-tree :data="menuList" :props="menuListTreeProps" node-key="menuId" ref="menuListTree" :default-expand-all="true" show-checkbox>
<el-form-item label="备注" prop="remark"> </el-tree>
<el-input v-model="dataForm.remark" placeholder="备注"></el-input> </el-form-item>
</el-form-item> </el-form>
<span slot="footer" class="dialog-footer">
<!-- 授权菜单树 --> <el-button @click="visible = false">取消</el-button>
<el-form-item size="mini" label="授权"> <el-button type="primary" @click="dataFormSubmit()"></el-button>
<el-tree :data="menuList" :props="menuListTreeProps" node-key="menuId" ref="menuListTree" :default-expand-all="true" show-checkbox> </span>
</el-tree>
</el-form-item>
</el-form>
<!-- 弹窗底部按钮取消和确认按钮 -->
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()"></el-button>
</span>
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
// import { treeDataTranslate } from '@/utils'
import { treeDataTranslate } from '@/utils' export default {
export default {
data() { data() {
return { return {
// visible: false,
visible: false, menuList: [],
// menuListTreeProps: {
menuList: [], label: 'name',
// el-tree children: 'children'
menuListTreeProps: { },
label: 'name', // dataForm: {
children: 'children' // id: 0,
}, roleName: '',
// remark: ''
dataForm: { },
id: 0, // ID0 dataRule: {
roleName: '', // roleName: [
remark: '' // { required: true, message: '角色名称不能为空', trigger: 'blur' }
}, ]
// }
dataRule: { }
roleName: [
{ required: true, message: '角色名称不能为空', trigger: 'blur' } //
]
}
}
}, },
methods: { methods: {
// ID init(id) {
init(id) { this.dataForm.id = id || 0
this.dataForm.id = id || 0 // ID0 this.$http({
url: this.$http.adornUrl('/sys/menu/list'),
// method: 'get',
this.$http({ params: this.$http.adornParams()
url: this.$http.adornUrl('/sys/menu/list'), }).then(({ data }) => {
method: 'get', this.menuList = treeDataTranslate(data, 'menuId')
params: this.$http.adornParams() }).then(() => {
}).then(({ data }) => { this.visible = true
this.menuList = treeDataTranslate(data, 'menuId') // this.$nextTick(() => {
}).then(() => { this.$refs['dataForm'].resetFields()
// this.$refs.menuListTree.setCheckedKeys([])
this.visible = true })
}).then(() => {
// DOM if (this.dataForm.id) {
this.$nextTick(() => { this.$http({
// url: this.$http.adornUrl(`/sys/role/info/${this.dataForm.id}`),
this.$refs['dataForm'].resetFields() method: 'get',
// params: this.$http.adornParams()
this.$refs.menuListTree.setCheckedKeys([]) }).then(({ data }) => {
}) if (data && data.code === 200) {
}).then(() => { this.dataForm.roleName = data.role.roleName
// ID this.dataForm.remark = data.role.remark
if (this.dataForm.id) { data.role.menuIdList.forEach(item => {
this.$http({ this.$refs.menuListTree.setChecked(item, true);
url: this.$http.adornUrl(`/sys/role/info/${this.dataForm.id}`), });
method: 'get', }
params: this.$http.adornParams() })
}).then(({ data }) => { }
if (data && data.code === 200) { })
// },
this.dataForm.roleName = data.role.roleName //
this.dataForm.remark = data.role.remark dataFormSubmit() {
// this.$refs['dataForm'].validate((valid) => {
data.role.menuIdList.forEach(item => { if (valid) {
this.$refs.menuListTree.setChecked(item, true) this.$http({
}) url: this.$http.adornUrl(`/sys/role/${!this.dataForm.id ? 'save' : 'update'}`),
} method: 'post',
}) data: this.$http.adornData({
} 'roleId': this.dataForm.id || undefined,
}) 'roleName': this.dataForm.roleName,
}, 'remark': this.dataForm.remark,
'menuIdList': [].concat(this.$refs.menuListTree.getCheckedKeys(), this.$refs.menuListTree.getHalfCheckedKeys())
// })
dataFormSubmit() { }).then(({ data }) => {
// if (data && data.code === 200) {
this.$refs['dataForm'].validate((valid) => { this.$message({
if (valid) { message: '操作成功',
// type: 'success',
this.$http({ duration: 1500,
url: this.$http.adornUrl(`/sys/role/${!this.dataForm.id ? 'save' : 'update'}`), // ID onClose: () => {
method: 'post', this.visible = false
data: this.$http.adornData({ this.$emit('refreshDataList')
'roleId': this.dataForm.id || undefined, // ID }
'roleName': this.dataForm.roleName, // })
'remark': this.dataForm.remark, // } else {
// ID this.$message.error(data.msg)
'menuIdList': [].concat(this.$refs.menuListTree.getCheckedKeys(), this.$refs.menuListTree.getHalfCheckedKeys()) }
}) })
}).then(({ data }) => { }
if (data && data.code === 200) { })
// }
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
//
this.visible = false
this.$emit('refreshDataList')
}
})
} else {
//
this.$message.error(data.msg)
}
})
}
})
}
}
} }
</script> }
</script>

@ -1,168 +1,132 @@
<template> <template>
<div class="mod-role"> <div class="mod-role">
<!-- 搜索和管理角色的表单 --> <el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()"> <el-form-item>
<el-form-item> <el-input v-model="dataForm.roleName" placeholder="角色名称" clearable></el-input>
<!-- 角色名称输入框 --> </el-form-item>
<el-input v-model="dataForm.roleName" placeholder="角色名称" clearable></el-input> <el-form-item>
</el-form-item> <el-button @click="getDataList()"></el-button>
<el-form-item> <el-button v-if="isAuth('sys:role:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
<!-- 查询按钮 --> <el-button v-if="isAuth('sys:role:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button>
<el-button @click="getDataList()"></el-button> </el-form-item>
<!-- 新增按钮只有在有权限时才显示 --> </el-form>
<el-button v-if="isAuth('sys:role:save')" type="primary" @click="addOrUpdateHandle()"></el-button> <el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;">
<!-- 批量删除按钮只有在有权限时才显示且未选择则禁用 --> <el-table-column type="selection" header-align="center" align="center" width="50">
<el-button v-if="isAuth('sys:role:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button> </el-table-column>
</el-form-item> <el-table-column prop="roleId" header-align="center" align="center" width="80" label="ID">
</el-form> </el-table-column>
<el-table-column prop="roleName" header-align="center" align="center" label="角色名称">
<!-- 显示角色的表格 --> </el-table-column>
<el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;"> <el-table-column prop="remark" header-align="center" align="center" label="备注">
<!-- 选择列用于多选 --> </el-table-column>
<el-table-column type="selection" header-align="center" align="center" width="50"></el-table-column> <el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<!-- ID列 --> <template slot-scope="scope">
<el-table-column prop="roleId" header-align="center" align="center" width="80" label="ID"></el-table-column> <el-button v-if="isAuth('sys:role:update')" type="text" size="small" @click="addOrUpdateHandle(scope.row.roleId)"></el-button>
<!-- 角色名称列 --> <el-button v-if="isAuth('sys:role:delete')" type="text" size="small" @click="deleteHandle(scope.row.roleId)"></el-button>
<el-table-column prop="roleName" header-align="center" align="center" label="角色名称"></el-table-column> </template>
<!-- 备注列 --> </el-table-column>
<el-table-column prop="remark" header-align="center" align="center" label="备注"></el-table-column> </el-table>
<!-- 操作列 --> <el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作"> </el-pagination>
<template slot-scope="scope"> <!-- 弹窗, 新增 / 修改 -->
<!-- 修改按钮只有在有权限时才显示 --> <add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
<el-button v-if="isAuth('sys:role:update')" type="text" size="small" @click="addOrUpdateHandle(scope.row.roleId)"></el-button> </div>
<!-- 删除按钮只有在有权限时才显示 --> </template>
<el-button v-if="isAuth('sys:role:delete')" type="text" size="small" @click="deleteHandle(scope.row.roleId)"></el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalCount"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div>
</template>
<script> <script>
import AddOrUpdate from './role-add-or-update' // / import AddOrUpdate from './role-add-or-update'
export default {
export default { data() {
data() { return {
return { dataForm: {
// roleName: ''
dataForm: { },
roleName: '' dataList: [],
}, pageIndex: 1,
// pageSize: 10,
dataList: [], totalCount: 0,
// dataListLoading: false,
pageIndex: 1, dataListSelections: [],
// addOrUpdateVisible: false
pageSize: 10, }
// },
totalCount: 0, components: {
// AddOrUpdate
dataListLoading: false, },
// activated() {
dataListSelections: [], this.getDataList()
// },
addOrUpdateVisible: false methods: {
} //
}, getDataList() {
components: { this.dataListLoading = true
AddOrUpdate // / this.$http({
}, url: this.$http.adornUrl('/sys/role/list'),
activated() { method: 'get',
// params: this.$http.adornParams({
this.getDataList() 'page': this.pageIndex,
}, 'limit': this.pageSize,
methods: { 'roleName': this.dataForm.roleName
// })
getDataList() { }).then(({ data }) => {
this.dataListLoading = true // if (data && data.code === 200) {
this.$http({ this.dataList = data.page.list
url: this.$http.adornUrl('/sys/role/list'), // API this.totalCount = data.page.totalCount
method: 'get', // HTTP } else {
params: this.$http.adornParams({ this.dataList = []
'page': this.pageIndex, // this.totalCount = 0
'limit': this.pageSize, // }
'roleName': this.dataForm.roleName // this.dataListLoading = false
}) })
}).then(({ data }) => { },
if (data && data.code === 200) { //
// sizeChangeHandle(val) {
this.dataList = data.page.list // this.pageSize = val
this.totalCount = data.page.totalCount // this.pageIndex = 1
} else { this.getDataList()
// },
this.dataList = [] //
this.totalCount = 0 currentChangeHandle(val) {
} this.pageIndex = val
this.dataListLoading = false // this.getDataList()
}) },
}, //
// selectionChangeHandle(val) {
sizeChangeHandle(val) { this.dataListSelections = val
this.pageSize = val // },
this.pageIndex = 1 // // /
this.getDataList() // addOrUpdateHandle(id) {
}, this.addOrUpdateVisible = true
// this.$nextTick(() => {
currentChangeHandle(val) { this.$refs.addOrUpdate.init(id)
this.pageIndex = val // })
this.getDataList() // },
}, //
// deleteHandle(id) {
selectionChangeHandle(val) { var ids = id ? [id] : this.dataListSelections.map(item => item.roleId)
this.dataListSelections = val // this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
}, confirmButtonText: '确定',
// / cancelButtonText: '取消',
addOrUpdateHandle(id) { type: 'warning'
this.addOrUpdateVisible = true // }).then(() => {
this.$nextTick(() => { this.$http({
this.$refs.addOrUpdate.init(id) // ID url: this.$http.adornUrl('/sys/role/delete'),
}) method: 'post',
}, data: this.$http.adornData(ids, false)
// }).then(({ data }) => {
deleteHandle(id) { if (data && data.code === 200) {
// ID this.$message({
var ids = id ? [id] : this.dataListSelections.map(item => item.roleId) message: '操作成功',
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', { type: 'success',
confirmButtonText: '确定', duration: 1500,
cancelButtonText: '取消', onClose: () => this.getDataList()
type: 'warning' })
}).then(() => { } else {
// this.$message.error(data.msg)
this.$http({ }
url: this.$http.adornUrl('/sys/role/delete'), // API })
method: 'post', // HTTP }).catch(() => { })
data: this.$http.adornData(ids, false) // ID }
}).then(({ data }) => { }
if (data && data.code === 200) { }
// </script>
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => this.getDataList() //
})
} else {
//
this.$message.error(data.msg) //
}
})
}).catch(() => { }) //
}
}
}
</script>

@ -1,202 +1,177 @@
<template> <template>
<el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible"> <el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible">
<!-- 表单使用dataForm作为模型进行用户信息的新增或修改 --> <el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px"> <el-form-item label="用户名" prop="userName">
<!-- 用户名输入框 --> <el-input v-model="dataForm.userName" placeholder="登录帐号"></el-input>
<el-form-item label="用户名" prop="userName"> </el-form-item>
<el-input v-model="dataForm.userName" placeholder="登录帐号"></el-input> <el-form-item label="密码" prop="password" :class="{ 'is-required': !dataForm.id }">
</el-form-item> <el-input v-model="dataForm.password" type="password" placeholder="密码"></el-input>
<!-- 密码输入框若为新增则必填 --> </el-form-item>
<el-form-item label="密码" prop="password" :class="{ 'is-required': !dataForm.id }"> <el-form-item label="确认密码" prop="comfirmPassword" :class="{ 'is-required': !dataForm.id }">
<el-input v-model="dataForm.password" type="password" placeholder="密码"></el-input> <el-input v-model="dataForm.comfirmPassword" type="password" placeholder="确认密码"></el-input>
</el-form-item> </el-form-item>
<!-- 确认密码输入框若为新增则必填 --> <el-form-item label="邮箱" prop="email">
<el-form-item label="确认密码" prop="comfirmPassword" :class="{ 'is-required': !dataForm.id }"> <el-input v-model="dataForm.email" placeholder="邮箱"></el-input>
<el-input v-model="dataForm.comfirmPassword" type="password" placeholder="确认密码"></el-input> </el-form-item>
</el-form-item> <el-form-item label="手机号" prop="mobile">
<!-- 邮箱输入框 --> <el-input v-model="dataForm.mobile" placeholder="手机号"></el-input>
<el-form-item label="邮箱" prop="email"> </el-form-item>
<el-input v-model="dataForm.email" placeholder="邮箱"></el-input> <el-form-item label="角色" size="mini" prop="roleIdList">
</el-form-item> <el-checkbox-group v-model="dataForm.roleIdList">
<!-- 手机号输入框 --> <el-checkbox v-for="role in roleList" :key="role.roleId" :label="role.roleId">{{ role.roleName }}</el-checkbox>
<el-form-item label="手机号" prop="mobile"> </el-checkbox-group>
<el-input v-model="dataForm.mobile" placeholder="手机号"></el-input> </el-form-item>
</el-form-item> <el-form-item label="状态" size="mini" prop="status">
<!-- 角色选择 --> <el-radio-group v-model="dataForm.status">
<el-form-item label="角色" size="mini" prop="roleIdList"> <el-radio :label="0">禁用</el-radio>
<el-checkbox-group v-model="dataForm.roleIdList"> <el-radio :label="1">正常</el-radio>
<el-checkbox v-for="role in roleList" :key="role.roleId" :label="role.roleId">{{ role.roleName }}</el-checkbox> </el-radio-group>
</el-checkbox-group> </el-form-item>
</el-form-item> </el-form>
<!-- 状态选择 --> <span slot="footer" class="dialog-footer">
<el-form-item label="状态" size="mini" prop="status"> <el-button @click="visible = false">取消</el-button>
<el-radio-group v-model="dataForm.status"> <el-button type="primary" @click="dataFormSubmit()"></el-button>
<el-radio :label="0">禁用</el-radio> </span>
<el-radio :label="1">正常</el-radio> </el-dialog>
</el-radio-group> </template>
</el-form-item>
</el-form>
<!-- 对话框底部按钮 -->
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()"></el-button>
</span>
</el-dialog>
</template>
<script> <script>
import { isEmail, isMobile } from '@/utils/validate' // import { isEmail, isMobile } from '@/utils/validate'
export default {
export default { data() {
data() { var validatePassword = (rule, value, callback) => {
// if (!this.dataForm.id && !/\S/.test(value)) {
var validatePassword = (rule, value, callback) => { callback(new Error('密码不能为空'))
if (!this.dataForm.id && !/\S/.test(value)) { // } else {
callback(new Error('密码不能为空')) callback()
} else { }
callback() // }
} var validateComfirmPassword = (rule, value, callback) => {
} if (!this.dataForm.id && !/\S/.test(value)) {
var validateComfirmPassword = (rule, value, callback) => { callback(new Error('确认密码不能为空'))
if (!this.dataForm.id && !/\S/.test(value)) { // } else if (this.dataForm.password !== value) {
callback(new Error('确认密码不能为空')) callback(new Error('确认密码与密码输入不一致'))
} else if (this.dataForm.password !== value) { // } else {
callback(new Error('确认密码与密码输入不一致')) callback()
} else { }
callback() // }
} var validateEmail = (rule, value, callback) => {
} if (!isEmail(value)) {
// callback(new Error('邮箱格式错误'))
var validateEmail = (rule, value, callback) => { } else {
if (!isEmail(value)) { callback()
callback(new Error('邮箱格式错误')) }
} else { }
callback() // var validateMobile = (rule, value, callback) => {
} if (!isMobile(value)) {
} callback(new Error('手机号格式错误'))
// } else {
var validateMobile = (rule, value, callback) => { callback()
if (!isMobile(value)) { }
callback(new Error('手机号格式错误')) }
} else { return {
callback() // visible: false,
} roleList: [],
} dataForm: {
return { id: 0,
visible: false, // userName: '',
roleList: [], // password: '',
// comfirmPassword: '',
dataForm: { salt: '',
id: 0, // ID email: '',
userName: '', // mobile: '',
password: '', // roleIdList: [],
comfirmPassword: '', // status: 1
salt: '', // },
email: '', // dataRule: {
mobile: '', // userName: [
roleIdList: [], // ID { required: true, message: '用户名不能为空', trigger: 'blur' }
status: 1 // 1: , 0: ],
}, password: [
// { validator: validatePassword, trigger: 'blur' }
dataRule: { ],
userName: [ comfirmPassword: [
{ required: true, message: '用户名不能为空', trigger: 'blur' } { validator: validateComfirmPassword, trigger: 'blur' }
], ],
password: [ email: [
{ validator: validatePassword, trigger: 'blur' } { required: true, message: '邮箱不能为空', trigger: 'blur' },
], { validator: validateEmail, trigger: 'blur' }
comfirmPassword: [ ],
{ validator: validateComfirmPassword, trigger: 'blur' } mobile: [
], { required: true, message: '手机号不能为空', trigger: 'blur' },
email: [ { validator: validateMobile, trigger: 'blur' }
{ required: true, message: '邮箱不能为空', trigger: 'blur' }, ]
{ validator: validateEmail, trigger: 'blur' } }
], }
mobile: [ },
{ required: true, message: '手机号不能为空', trigger: 'blur' }, methods: {
{ validator: validateMobile, trigger: 'blur' } init(id) {
] this.dataForm.id = id || 0
} this.$http({
} url: this.$http.adornUrl('/sys/role/select'),
}, method: 'get',
methods: { params: this.$http.adornParams()
// }).then(({ data }) => {
init(id) { this.roleList = data && data.code === 200 ? data.list : []
this.dataForm.id = id || 0 // ID }).then(() => {
// this.visible = true
this.$http({ this.$nextTick(() => {
url: this.$http.adornUrl('/sys/role/select'), this.$refs['dataForm'].resetFields()
method: 'get', })
params: this.$http.adornParams() // }).then(() => {
}).then(({ data }) => { if (this.dataForm.id) {
this.roleList = data && data.code === 200 ? data.list : [] // this.$http({
}).then(() => { url: this.$http.adornUrl(`/sys/user/info/${this.dataForm.id}`),
this.visible = true // method: 'get',
this.$nextTick(() => { params: this.$http.adornParams()
this.$refs['dataForm'].resetFields() // }).then(({ data }) => {
}) if (data && data.code === 200) {
}).then(() => { this.dataForm.userName = data.user.username
// this.dataForm.salt = data.user.salt
if (this.dataForm.id) { this.dataForm.email = data.user.email
this.$http({ this.dataForm.mobile = data.user.mobile
url: this.$http.adornUrl(`/sys/user/info/${this.dataForm.id}`), this.dataForm.roleIdList = data.user.roleIdList
method: 'get', this.dataForm.status = data.user.status
params: this.$http.adornParams() }
}).then(({ data }) => { })
if (data && data.code === 200) { }
// })
this.dataForm.userName = data.user.username // },
this.dataForm.salt = data.user.salt // 使 //
this.dataForm.email = data.user.email // dataFormSubmit() {
this.dataForm.mobile = data.user.mobile // this.$refs['dataForm'].validate((valid) => {
this.dataForm.roleIdList = data.user.roleIdList // ID if (valid) {
this.dataForm.status = data.user.status // this.$http({
} url: this.$http.adornUrl(`/sys/user/${!this.dataForm.id ? 'save' : 'update'}`),
}) method: 'post',
} data: this.$http.adornData({
}) 'userId': this.dataForm.id || undefined,
}, 'username': this.dataForm.userName,
// 'password': this.dataForm.password,
dataFormSubmit() { 'salt': this.dataForm.salt,
// 'email': this.dataForm.email,
this.$refs['dataForm'].validate((valid) => { 'mobile': this.dataForm.mobile,
if (valid) { // 'status': this.dataForm.status,
this.$http({ 'roleIdList': this.dataForm.roleIdList
// ID })
url: this.$http.adornUrl(`/sys/user/${!this.dataForm.id ? 'save' : 'update'}`), }).then(({ data }) => {
method: 'post', if (data && data.code === 200) {
data: this.$http.adornData({ this.$message({
// message: '操作成功',
'userId': this.dataForm.id || undefined, type: 'success',
'username': this.dataForm.userName, duration: 1500,
'password': this.dataForm.password, onClose: () => {
'salt': this.dataForm.salt, this.visible = false
'email': this.dataForm.email, this.$emit('refreshDataList')
'mobile': this.dataForm.mobile, }
'status': this.dataForm.status, })
'roleIdList': this.dataForm.roleIdList } else {
}) this.$message.error(data.msg)
}).then(({ data }) => { }
// })
if (data && data.code === 200) { }
// })
this.$message({ }
message: '操作成功', }
type: 'success', }
duration: 1500, </script>
onClose: () => {
this.visible = false //
this.$emit('refreshDataList') //
}
})
} else {
//
this.$message.error(data.msg) //
}
})
}
})
}
}
}
</script>

@ -1,168 +1,140 @@
<template> <template>
<div class="mod-user"> <div class="mod-user">
<!-- 搜索表单 --> <el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()"> <el-form-item>
<el-form-item> <el-input v-model="dataForm.userName" placeholder="用户名" clearable></el-input>
<!-- 用户名输入框 --> </el-form-item>
<el-input v-model="dataForm.userName" placeholder="用户名" clearable></el-input> <el-form-item>
</el-form-item> <el-button @click="getDataList()"></el-button>
<el-form-item> <el-button v-if="isAuth('sys:user:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
<!-- 查询按钮 --> <el-button v-if="isAuth('sys:user:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button>
<el-button @click="getDataList()"></el-button> </el-form-item>
<!-- 新增按钮只有在有权限时显示 --> </el-form>
<el-button v-if="isAuth('sys:user:save')" type="primary" @click="addOrUpdateHandle()"></el-button> <el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;">
<!-- 批量删除按钮只有在有权限时显示并且在没有选择项时禁用 --> <el-table-column type="selection" header-align="center" align="center" width="50">
<el-button v-if="isAuth('sys:user:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button> </el-table-column>
</el-form-item> <el-table-column prop="userId" header-align="center" align="center" width="80" label="ID">
</el-form> </el-table-column>
<el-table-column prop="username" header-align="center" align="center" label="用户名">
<!-- 用户数据表格 --> </el-table-column>
<el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;"> <el-table-column prop="email" header-align="center" align="center" label="邮箱">
<!-- 选择框列 --> </el-table-column>
<el-table-column type="selection" header-align="center" align="center" width="50"> <el-table-column prop="mobile" header-align="center" align="center" label="手机号">
</el-table-column> </el-table-column>
<!-- ID列 --> <el-table-column prop="status" header-align="center" align="center" label="状态">
<el-table-column prop="userId" header-align="center" align="center" width="80" label="ID"> <template slot-scope="scope">
</el-table-column> <el-tag v-if="scope.row.status === 0" size="small" type="danger"></el-tag>
<!-- 用户名列 --> <el-tag v-else size="small">正常</el-tag>
<el-table-column prop="username" header-align="center" align="center" label="用户名"> </template>
</el-table-column> </el-table-column>
<!-- 邮箱列 --> <el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<el-table-column prop="email" header-align="center" align="center" label="邮箱"> <template slot-scope="scope">
</el-table-column> <el-button v-if="isAuth('sys:user:update')" type="text" size="small" @click="addOrUpdateHandle(scope.row.userId)"></el-button>
<!-- 手机号列 --> <el-button v-if="isAuth('sys:user:delete')" type="text" size="small" @click="deleteHandle(scope.row.userId)"></el-button>
<el-table-column prop="mobile" header-align="center" align="center" label="手机号"> </template>
</el-table-column> </el-table-column>
<!-- 状态列 --> </el-table>
<el-table-column prop="status" header-align="center" align="center" label="状态"> <el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
<template slot-scope="scope"> </el-pagination>
<el-tag v-if="scope.row.status === 0" size="small" type="danger"></el-tag> <!-- 弹窗, 新增 / 修改 -->
<el-tag v-else size="small">正常</el-tag> <add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</template> </div>
</el-table-column> </template>
<!-- 操作列 -->
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope">
<!-- 修改按钮只有在有权限时显示 -->
<el-button v-if="isAuth('sys:user:update')" type="text" size="small" @click="addOrUpdateHandle(scope.row.userId)"></el-button>
<!-- 删除按钮只有在有权限时显示 -->
<el-button v-if="isAuth('sys:user:delete')" type="text" size="small" @click="deleteHandle(scope.row.userId)"></el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗用于新增或修改用户 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div>
</template>
<script> <script>
import AddOrUpdate from './user-add-or-update' // / import AddOrUpdate from './user-add-or-update'
export default {
export default { data() {
data() { return {
return { dataForm: {
dataForm: { userName: ''
userName: '' // },
}, dataList: [],
dataList: [], // pageIndex: 1,
pageIndex: 1, // pageSize: 10,
pageSize: 10, // totalCount: 0,
totalCount: 0, // dataListLoading: false,
dataListLoading: false, // dataListSelections: [],
dataListSelections: [], // addOrUpdateVisible: false
addOrUpdateVisible: false // / }
} },
}, components: {
components: { AddOrUpdate
AddOrUpdate // / },
}, activated() {
activated() { this.getDataList()
this.getDataList() // },
}, methods: {
methods: { //
// getDataList() {
getDataList() { this.dataListLoading = true
this.dataListLoading = true // true this.$http({
this.$http({ url: this.$http.adornUrl('/sys/user/list'),
url: this.$http.adornUrl('/sys/user/list'), // API method: 'get',
method: 'get', params: this.$http.adornParams({
params: this.$http.adornParams({ 'page': this.pageIndex,
'page': this.pageIndex, // 'limit': this.pageSize,
'limit': this.pageSize, // 'username': this.dataForm.userName
'username': this.dataForm.userName // })
}) }).then(({ data }) => {
}).then(({ data }) => { if (data && data.code === 200) {
if (data && data.code === 200) { this.dataList = data.page.list
// this.totalCount = data.page.totalCount
this.dataList = data.page.list } else {
this.totalCount = data.page.totalCount this.dataList = []
} else { this.totalCount = 0
// }
this.dataList = [] this.dataListLoading = false
this.totalCount = 0 })
} },
this.dataListLoading = false // false //
}) sizeChangeHandle(val) {
}, this.pageSize = val
// this.pageIndex = 1
sizeChangeHandle(val) { this.getDataList()
this.pageSize = val // },
this.pageIndex = 1 // //
this.getDataList() // currentChangeHandle(val) {
}, this.pageIndex = val
// this.getDataList()
currentChangeHandle(val) { },
this.pageIndex = val // //
this.getDataList() // selectionChangeHandle(val) {
}, this.dataListSelections = val
// },
selectionChangeHandle(val) { // /
this.dataListSelections = val // addOrUpdateHandle(id) {
}, this.addOrUpdateVisible = true
// this.$nextTick(() => {
addOrUpdateHandle(id) { this.$refs.addOrUpdate.init(id)
this.addOrUpdateVisible = true // / })
this.$nextTick(() => { },
this.$refs.addOrUpdate.init(id) // ID //
}) deleteHandle(id) {
}, var userIds = id ? [id] : this.dataListSelections.map(item => item.userId)
// this.$confirm(`确定对[id=${userIds.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
deleteHandle(id) { confirmButtonText: '确定',
// ID cancelButtonText: '取消',
var userIds = id ? [id] : this.dataListSelections.map(item => item.userId) type: 'warning'
this.$confirm(`确定对[id=${userIds.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', { }).then(() => {
confirmButtonText: '确定', this.$http({
cancelButtonText: '取消', url: this.$http.adornUrl('/sys/user/delete'),
type: 'warning' method: 'post',
}).then(() => { data: this.$http.adornData(userIds, false)
// }).then(({ data }) => {
this.$http({ if (data && data.code === 200) {
url: this.$http.adornUrl('/sys/user/delete'), // API this.$message({
method: 'post', message: '操作成功',
data: this.$http.adornData(userIds, false) // ID type: 'success',
}).then(({ data }) => { duration: 1500,
if (data && data.code === 200) { onClose: () => this.getDataList()
// })
this.$message({ } else {
message: '操作成功', this.$message.error(data.msg)
type: 'success', }
duration: 1500, })
onClose: () => this.getDataList() // }).catch(() => { })
}) }
} else { }
// }
this.$message.error(data.msg) </script>
}
})
}).catch(() => {
//
})
}
}
}
</script>

@ -1,63 +1,54 @@
<template> <template>
<!-- 开发接入信息对话框 --> <el-dialog title="开发接入信息" :close-on-click-modal="false" :visible.sync="visible">
<el-dialog title="开发接入信息" :close-on-click-modal="false" :visible.sync="visible"> <div>
<div> <div class="list-item"><span class="label">公众号:</span>{{account.name}}</div>
<!-- 公众号信息 --> <div class="list-item"><span class="label">token:</span>{{account.token}}</div>
<div class="list-item"><span class="label">公众号:</span>{{account.name}}</div> <div class="list-item"><span class="label">aesKey:</span>{{account.aesKey}}</div>
<!-- token 信息 --> <div class="list-item">
<div class="list-item"><span class="label">token:</span>{{account.token}}</div> <span class="label">接入链接:</span>
<!-- aesKey 信息 --> <span v-html="accessUrl"></span>
<div class="list-item"><span class="label">aesKey:</span>{{account.aesKey}}</div> </div>
<!-- 接入链接 -->
<div class="list-item"> </div>
<span class="label">接入链接:</span> </el-dialog>
<span v-html="accessUrl"></span> <!-- 使 v-html --> </template>
</div>
</div>
</el-dialog>
</template>
<script> <script>
export default { export default {
data() { data() {
return { return {
visible: false, // visible: false,
account: {}, // account: {},
} }
}, },
computed: { computed: {
// accessUrl() {
accessUrl() { let host = location.host;
let host = location.host; // if(/^(\d(.\d){3})|localhost/.test(host)){
// IP localhost host='<span class="text-red">正式域名</span>'
if(/^(\d(.\d){3})|localhost/.test(host)){ }
host='<span class="text-red">正式域名</span>' // IP localhost return location.protocol + '//' + host + '/wx/wx/msg/' + this.account.appid
} }
// },
return location.protocol + '//' + host + '/wx/wx/msg/' + this.account.appid methods: {
} init(item) {
}, this.visible = true
methods: { if (item && item.appid) {
// this.account = item
init(item) { }
this.visible = true // },
if (item && item.appid) { }
this.account = item // appid account }
} </script>
}, <style scoped>
} .list-item{
} line-height: 30px;
</script> }
.label{
<style scoped> width: 100px;
.list-item{ display: inline-block;
line-height: 30px; /* 每一项的行高 */ margin-right: 10px;
} font-weight: bold;
.label{ text-align: right;
width: 100px; /* 标签的宽度 */ }
display: inline-block; /* 使标签在每行中内联显示 */ </style>
margin-right: 10px; /* 标签与内容之间的右边距 */
font-weight: bold; /* 标签加粗 */
text-align: right; /* 标签文本右对齐 */
}
</style>

@ -1,145 +1,118 @@
<template> <template>
<!-- 新增/修改对话框 --> <el-dialog
<el-dialog title="新增/修改"
title="新增/修改" :close-on-click-modal="false"
:close-on-click-modal="false" :visible.sync="visible">
:visible.sync="visible"> <el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="100px">
<el-form-item label="公众号名称" prop="name">
<el-input v-model="dataForm.name" placeholder="公众号名称"></el-input>
</el-form-item>
<div class="padding text-gray">测试号可选择服务号不同类型账号是否认证可使用功能权限不同<a href="https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Explanation_of_interface_privileges.html">参考文档</a></div>
<el-row>
<el-col :span="12">
<el-form-item label="公众号类型" prop="type">
<el-select v-model="dataForm.type" placeholder="公众号类型">
<el-option v-for="(name,key) in ACCOUNT_TYPES" :key="name" :label="name" :value="key"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否认证" prop="verified">
<el-switch v-model="dataForm.verified" placeholder="是否认证"></el-switch>
</el-form-item>
</el-col>
</el-row>
<!-- 表单区域 --> <el-form-item label="appid" prop="appid">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="100px"> <el-input v-model="dataForm.appid" placeholder="appid"></el-input>
</el-form-item>
<!-- 公众号名称输入 --> <el-form-item label="appsecret" prop="secret">
<el-form-item label="公众号名称" prop="name"> <el-input v-model="dataForm.secret" placeholder="appsecret"></el-input>
<el-input v-model="dataForm.name" placeholder="公众号名称"></el-input> </el-form-item>
</el-form-item> <el-form-item label="token" prop="token">
<el-input v-model="dataForm.token" placeholder="token"></el-input>
<!-- 提示信息 --> </el-form-item>
<div class="padding text-gray">测试号可选择服务号不同类型账号是否认证可使用功能权限不同<a href="https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Explanation_of_interface_privileges.html">参考文档</a></div> <el-form-item label="aesKey" prop="aesKey">
<el-input v-model="dataForm.aesKey" placeholder="aesKey可为空"></el-input>
<el-row> </el-form-item>
<el-col :span="12"> </el-form>
<!-- 公众号类型选择 --> <span slot="footer" class="dialog-footer">
<el-form-item label="公众号类型" prop="type"> <el-button @click="visible = false">取消</el-button>
<el-select v-model="dataForm.type" placeholder="公众号类型"> <el-button type="primary" @click="dataFormSubmit()"></el-button>
<el-option v-for="(name,key) in ACCOUNT_TYPES" :key="name" :label="name" :value="key"></el-option> </span>
</el-select> </el-dialog>
</el-form-item> </template>
</el-col>
<el-col :span="12">
<!-- 是否认证开关 -->
<el-form-item label="是否认证" prop="verified">
<el-switch v-model="dataForm.verified" placeholder="是否认证"></el-switch>
</el-form-item>
</el-col>
</el-row>
<!-- appid 输入 -->
<el-form-item label="appid" prop="appid">
<el-input v-model="dataForm.appid" placeholder="appid"></el-input>
</el-form-item>
<!-- appsecret 输入 -->
<el-form-item label="appsecret" prop="secret">
<el-input v-model="dataForm.secret" placeholder="appsecret"></el-input>
</el-form-item>
<!-- token 输入 -->
<el-form-item label="token" prop="token">
<el-input v-model="dataForm.token" placeholder="token"></el-input>
</el-form-item>
<!-- aesKey 输入 -->
<el-form-item label="aesKey" prop="aesKey">
<el-input v-model="dataForm.aesKey" placeholder="aesKey可为空"></el-input>
</el-form-item>
</el-form>
<!-- 对话框底部按钮 -->
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()"></el-button>
</span>
</el-dialog>
</template>
<script> <script>
import { mapState } from 'vuex' // Vuex mapState import { mapState } from 'vuex'
export default {
export default { data () {
data () { return {
return { visible: false,
visible: false, // dataForm: {
dataForm: { // appid: '',
appid: '', name: '',
name: '', type:'2',
type:'2', // verified:true,
verified: true, // true secret: '',
secret: '', token: 'my_weixin_token_',
token: 'my_weixin_token_', // token aesKey: ''
aesKey: '' // aesKey },
}, dataRule: {
dataRule: { // name: [
name: [ { required: true, message: '公众号名称不能为空', trigger: 'blur' }
{ required: true, message: '公众号名称不能为空', trigger: 'blur' } // ],
], appid: [
appid: [ { required: true, message: 'appid不能为空', trigger: 'blur' }
{ required: true, message: 'appid不能为空', trigger: 'blur' } // appid ],
], secret: [
secret: [ { required: true, message: 'appsecret不能为空', trigger: 'blur' }
{ required: true, message: 'appsecret不能为空', trigger: 'blur' } // appsecret ]
] }
} }
} },
}, computed: mapState({
computed: mapState({ ACCOUNT_TYPES: state=>state.wxAccount.ACCOUNT_TYPES
ACCOUNT_TYPES: state => state.wxAccount.ACCOUNT_TYPES // store }),
}), methods: {
methods: { init (item) {
// this.visible = true
init (item) { if(item && item.appid){
this.visible = true // this.dataForm = item
if(item && item.appid) { this.dataForm.type = item.type+''
this.dataForm = item // }else{
this.dataForm.type = item.type + '' // this.$nextTick(() => {
} else { this.$refs['dataForm'].resetFields()
// })
this.$nextTick(() => { }
this.$refs['dataForm'].resetFields()
}) },
} //
}, dataFormSubmit () {
// this.$refs['dataForm'].validate((valid) => {
dataFormSubmit () { if (valid) {
// this.$http({
this.$refs['dataForm'].validate((valid) => { url: this.$http.adornUrl(`/manage/wxAccount/save`),
if (valid) { method: 'post',
// POST data: this.$http.adornData(this.dataForm)
this.$http({ }).then(({data}) => {
url: this.$http.adornUrl(`/manage/wxAccount/save`), // URL if (data && data.code === 200) {
method: 'post', this.$message({
data: this.$http.adornData(this.dataForm) // 使 adornData message: '操作成功',
}).then(({data}) => { type: 'success',
if (data && data.code === 200) { duration: 1500,
// onClose: () => {
this.$message({ this.visible = false
message: '操作成功', this.$emit('refreshDataList')
type: 'success', }
duration: 1500, })
onClose: () => { } else {
this.visible = false // this.$message.error(data.msg)
this.$emit('refreshDataList') // }
} })
}) }
} else { })
// }
this.$message.error(data.msg) }
} }
}) </script>
}
})
}
}
}
</script>

@ -1,101 +1,68 @@
<template> <template>
<!-- 根据visible属性的值控制整个组件内容的显示与隐藏当visible为true时显示 -->
<div v-show="visible"> <div v-show="visible">
<!-- 定义一个表单绑定了dataForm数据模型使用了dataRule验证规则设置了表单尺寸为迷你型标签宽度为80px -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" size="mini" label-width="80px"> <el-form :model="dataForm" :rules="dataRule" ref="dataForm" size="mini" label-width="80px">
<!-- 第一行布局使用el-row包裹 -->
<el-row> <el-row>
<!-- 占12列用于放置文章标题相关的表单元素 -->
<el-col :span="12"> <el-col :span="12">
<!-- 文章标题的表单项设置了必填验证对应dataForm中的title属性 -->
<el-form-item label="文章标题" prop="title" required> <el-form-item label="文章标题" prop="title" required>
<!-- 使用el-input组件实现输入框双向绑定dataForm.title设置了最大长度为1024有占位提示文字 -->
<el-input v-model="dataForm.title" :maxlength="1024" placeholder="标题"></el-input> <el-input v-model="dataForm.title" :maxlength="1024" placeholder="标题"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 同样占12列用于放置文章类型相关的表单元素 -->
<el-col :span="12"> <el-col :span="12">
<!-- 文章类型的表单项设置了必填验证对应dataForm中的type属性 -->
<el-form-item label="文章类型" prop="type" required> <el-form-item label="文章类型" prop="type" required>
<!-- 使用el-select组件实现下拉选择框双向绑定dataForm.type有占位提示文字 -->
<el-select v-model="dataForm.type" placeholder="选择文章类型"> <el-select v-model="dataForm.type" placeholder="选择文章类型">
<!-- 循环遍历ARTICLE_TYPES对象生成下拉选项选项的标签显示为name值为key允许用户创建新的选项 -->
<el-option v-for="(name,key) in ARTICLE_TYPES" :key="name" :label="name" :value="key" allow-create></el-option> <el-option v-for="(name,key) in ARTICLE_TYPES" :key="name" :label="name" :value="key" allow-create></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<!-- 第二行布局 -->
<el-row> <el-row>
<!-- 占12列用于放置一级目录相关的表单元素 -->
<el-col :span="12"> <el-col :span="12">
<!-- 一级目录的表单项对应dataForm中的category属性 -->
<el-form-item label="一级目录" prop="category"> <el-form-item label="一级目录" prop="category">
<!-- 使用el-input组件实现输入框双向绑定dataForm.category设置了最大长度为50有占位提示文字 -->
<el-input :maxlength="50" v-model="dataForm.category" placeholder="一级目录"></el-input> <el-input :maxlength="50" v-model="dataForm.category" placeholder="一级目录"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 占12列用于放置二级分类相关的表单元素 -->
<el-col :span="12"> <el-col :span="12">
<!-- 二级分类的表单项对应dataForm中的subCategory属性 -->
<el-form-item label="二级分类" prop="subCategory"> <el-form-item label="二级分类" prop="subCategory">
<!-- 使用el-input组件实现输入框双向绑定dataForm.subCategory设置了最大长度为50有占位提示文字 -->
<el-input :maxlength="50" v-model="dataForm.subCategory" placeholder="二级目录"></el-input> <el-input :maxlength="50" v-model="dataForm.subCategory" placeholder="二级目录"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<!-- 指向外链的表单项对应dataForm中的targetLink属性 -->
<el-form-item label="指向外链" prop="targetLink"> <el-form-item label="指向外链" prop="targetLink">
<!-- 使用el-input组件实现输入框双向绑定dataForm.targetLink有占位提示文字 -->
<el-input v-model="dataForm.targetLink" placeholder="指向外链"></el-input> <el-input v-model="dataForm.targetLink" placeholder="指向外链"></el-input>
</el-form-item> </el-form-item>
<!-- 摘要的表单项对应dataForm中的summary属性 -->
<el-form-item label="摘要" prop="summary"> <el-form-item label="摘要" prop="summary">
<!-- 使用el-input组件实现文本域输入框双向绑定dataForm.summary设置了行数为3最大长度为512显示字数限制提示 -->
<el-input v-model="dataForm.summary" placeholder="摘要" type="textarea" rows="3" maxlength="512" show-word-limit></el-input> <el-input v-model="dataForm.summary" placeholder="摘要" type="textarea" rows="3" maxlength="512" show-word-limit></el-input>
</el-form-item> </el-form-item>
<!-- 标签的表单项使用了自定义的tags-editor组件双向绑定dataForm.tags -->
<el-form-item label="标签" prop="tags"> <el-form-item label="标签" prop="tags">
<tags-editor v-model="dataForm.tags"></tags-editor> <tags-editor v-model="dataForm.tags"></tags-editor>
</el-form-item> </el-form-item>
<!-- 封面图的表单项对应dataForm中的image属性 -->
<el-form-item label="封面图" prop="image"> <el-form-item label="封面图" prop="image">
<!-- 使用el-input组件实现输入框双向绑定dataForm.image有占位提示文字 -->
<el-input v-model="dataForm.image" placeholder="图片链接"> <el-input v-model="dataForm.image" placeholder="图片链接">
<!-- 插入一个自定义的OssUploader组件用于上传图片上传成功后将返回值赋给dataForm.image -->
<OssUploader slot="append" @uploaded="dataForm.image=$event"></OssUploader> <OssUploader slot="append" @uploaded="dataForm.image=$event"></OssUploader>
</el-input> </el-input>
</el-form-item> </el-form-item>
<!-- 引入一个富文本编辑器组件双向绑定dataForm.content -->
<tinymce-editor ref="editor" v-model="dataForm.content"></tinymce-editor> <tinymce-editor ref="editor" v-model="dataForm.content"></tinymce-editor>
</el-form> </el-form>
<!-- 距离顶部有一定间距靠右对齐的按钮区域 -->
<div class="margin-top text-right"> <div class="margin-top text-right">
<!-- 取消按钮点击时通过$emit触发父组件的hide事件 -->
<el-button @click="$emit('hide')"></el-button> <el-button @click="$emit('hide')"></el-button>
<!-- 确定按钮类型为主要按钮点击时调用dataFormSubmit方法进行表单提交 -->
<el-button type="primary" @click="dataFormSubmit()"></el-button> <el-button type="primary" @click="dataFormSubmit()"></el-button>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
// VuexmapStateVuex
import { mapState } from 'vuex' import { mapState } from 'vuex'
export default { export default {
name: 'article-add-or-update', name:'article-add-or-update',
components: { components: {
// TinymceEditor
TinymceEditor: () => import("@/components/tinymce-editor"), TinymceEditor: () => import("@/components/tinymce-editor"),
// tags-editor
tagsEditor: () => import("@/components/tags-editor"), tagsEditor: () => import("@/components/tags-editor"),
// OssUploaderOSS
OssUploader: () => import('../oss/oss-uploader') OssUploader: () => import('../oss/oss-uploader')
}, },
props: { props:{
visible: { visible:{
type: Boolean, type:Boolean,
default: false default:false
} }
}, },
data() { data() {
@ -127,45 +94,37 @@ export default {
}; };
}, },
computed: mapState({ computed: mapState({
// VuexstatearticleARTICLE_TYPES ARTICLE_TYPES: state=>state.article.ARTICLE_TYPES
ARTICLE_TYPES: state => state.article.ARTICLE_TYPES
}), }),
methods: { methods: {
init(id) { init(id) {
// dataFormidid使
this.dataForm.id = id || ""; this.dataForm.id = id || "";
this.$nextTick(() => { this.$nextTick(() => {
// DOM
this.$refs["dataForm"].resetFields(); this.$refs["dataForm"].resetFields();
if (id > 0) { if (id > 0) {
// id0HTTP
this.$http({ this.$http({
url: this.$http.adornUrl(`/manage/article/info/${this.dataForm.id}`), url: this.$http.adornUrl(`/manage/article/info/${this.dataForm.id}`),
method: "get", method: "get",
params: this.$http.adornParams() params: this.$http.adornParams()
}).then(({ data }) => { }).then(({ data }) => {
if (data && data.code === 200) { if (data && data.code === 200) {
// dataForm this.dataForm=data.article;
this.dataForm = data.article;
this.dataForm.type = data.article.type + ""; this.dataForm.type = data.article.type + "";
} }
}); });
} }
}); });
}, },
// //
dataFormSubmit() { dataFormSubmit() {
// valid
this.$refs["dataForm"].validate(valid => { this.$refs["dataForm"].validate(valid => {
if (valid) { if (valid) {
// HTTP
this.$http({ this.$http({
url: this.$http.adornUrl(`/manage/article/save`), url: this.$http.adornUrl(`/manage/article/save`),
method: "post", method: "post",
data: this.$http.adornData(this.dataForm) data: this.$http.adornData(this.dataForm)
}).then(({ data }) => { }).then(({ data }) => {
if (data && data.code === 200) { if (data && data.code === 200) {
// $emitrefreshDataListhide
this.$message({ this.$message({
message: "操作成功", message: "操作成功",
type: "success", type: "success",
@ -176,7 +135,6 @@ export default {
} }
}); });
} else { } else {
//
this.$message.error(data.msg); this.$message.error(data.msg);
} }
}); });
@ -186,14 +144,12 @@ export default {
imgUploadSuccess(response, file, fileList) { imgUploadSuccess(response, file, fileList) {
console.log(response); console.log(response);
if (response.code == 200) { if (response.code == 200) {
// dataForm.image
this.dataForm.image = response.data; this.dataForm.image = response.data;
console.log("this.article", this.article); console.log("this.article", this.article);
} else { } else {
//
this.$message.warning(response.msg); this.$message.warning(response.msg);
} }
} }
} }
}; };
</script> </script>

@ -1,56 +1,37 @@
<template> <template>
<!-- 外层容器div -->
<div> <div>
<!-- 根据条件控制显示内容当addOrUpdateVisible为false时显示以下部分 -->
<div v-show="!addOrUpdateVisible"> <div v-show="!addOrUpdateVisible">
<!-- 内联表单绑定了dataForm数据模型监听回车键native修饰符表示监听原生的键盘事件按下时调用getDataList方法 -->
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()"> <el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<!-- 文章类型选择的表单项 -->
<el-form-item> <el-form-item>
<!-- 使用el-select组件实现下拉选择框双向绑定dataForm.type有占位提示文字选项通过循环ARTICLE_TYPES生成 -->
<el-select v-model="dataForm.type" placeholder="选择文章类型"> <el-select v-model="dataForm.type" placeholder="选择文章类型">
<el-option v-for="(name,key) in ARTICLE_TYPES" :key="key" :label="name" :value="key" allow-create></el-option> <el-option v-for="(name,key) in ARTICLE_TYPES" :key="key" :label="name" :value="key" allow-create></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- 文章标题输入的表单项使用el-input组件双向绑定dataForm.title有占位提示文字且可清空输入内容 -->
<el-form-item> <el-form-item>
<el-input v-model="dataForm.title" placeholder="标题" clearable></el-input> <el-input v-model="dataForm.title" placeholder="标题" clearable></el-input>
</el-form-item> </el-form-item>
<!-- 操作按钮所在的表单项 -->
<el-form-item> <el-form-item>
<!-- 查询按钮点击时先将当前页码设置为1然后调用getDataList方法获取数据 -->
<el-button @click="pageIndex=1;getDataList()"></el-button> <el-button @click="pageIndex=1;getDataList()"></el-button>
<!-- 新增按钮根据权限调用isAuth方法判断决定是否显示类型为主要按钮点击时调用addOrUpdateHandle方法 -->
<el-button v-if="isAuth('wx:article:save')" type="primary" @click="addOrUpdateHandle()"></el-button> <el-button v-if="isAuth('wx:article:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
<!-- 批量删除按钮根据权限决定是否显示类型为危险按钮点击时调用deleteHandle方法当没有选中的数据项时禁用按钮 -->
<el-button v-if="isAuth('wx:article:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button> <el-button v-if="isAuth('wx:article:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- el-table组件用于展示数据列表绑定了dataList数据显示边框加载数据时显示加载提示监听选择项变化事件 -->
<el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;"> <el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;">
<!-- 选择列用于多选操作设置了表头和内容的对齐方式以及宽度 -->
<el-table-column type="selection" header-align="center" align="center" width="50"> <el-table-column type="selection" header-align="center" align="center" width="50">
</el-table-column> </el-table-column>
<!-- ID列对应dataList中数据项的id属性设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="id" header-align="center" align="center" label="ID"> <el-table-column prop="id" header-align="center" align="center" label="ID">
</el-table-column> </el-table-column>
<!-- 文章类型列对应dataList中数据项的type属性使用formatter函数格式化显示内容设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="type" header-align="center" align="center" :formatter="articleTypeFormat" label="文章类型"> <el-table-column prop="type" header-align="center" align="center" :formatter="articleTypeFormat" label="文章类型">
</el-table-column> </el-table-column>
<!-- 标题列对应dataList中数据项的title属性显示溢出提示设置了表头和内容的对齐方式以及列标题内容用a标签包裹可点击跳转 -->
<el-table-column prop="title" header-align="center" align="center" show-overflow-tooltip label="标题"> <el-table-column prop="title" header-align="center" align="center" show-overflow-tooltip label="标题">
<a :href="scope.row.targetLink" slot-scope="scope">{{scope.row.title}}</a> <a :href="scope.row.targetLink" slot-scope="scope">{{scope.row.title}}</a>
</el-table-column> </el-table-column>
<!-- 一级分类列对应dataList中数据项的category属性设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="category" header-align="center" align="center" label="一级分类"> <el-table-column prop="category" header-align="center" align="center" label="一级分类">
</el-table-column> </el-table-column>
<!-- 二级分类列对应dataList中数据项的subCategory属性设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="subCategory" header-align="center" align="center" label="二级分类"> <el-table-column prop="subCategory" header-align="center" align="center" label="二级分类">
</el-table-column> </el-table-column>
<!-- 打开次数列对应dataList中数据项的openCount属性设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="openCount" header-align="center" align="center" label="打开次数"> <el-table-column prop="openCount" header-align="center" align="center" label="打开次数">
</el-table-column> </el-table-column>
<!-- 操作列固定在右侧设置了表头和内容的对齐方式宽度以及列标题通过插槽定义了修改和删除按钮 -->
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作"> <el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.id)"></el-button> <el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.id)"></el-button>
@ -58,19 +39,16 @@
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页组件绑定了相关的分页事件和属性用于切换每页显示数量当前页码等操作 -->
<el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper"> <el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
</el-pagination> </el-pagination>
</div> </div>
<!-- 新增/修改组件通过属性和事件与父组件进行交互 --> <!-- 新增 / 修改 -->
<add-or-update :visible="addOrUpdateVisible" ref="addOr-update" @refreshDataList="getDataList" @hide="addOrUpdateVisible=false"></add-or-update> <add-or-update :visible="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList" @hide="addOrUpdateVisible=false"></add-or-update>
</div> </div>
</template> </template>
<script> <script>
// /
import AddOrUpdate from './article-add-or-update' import AddOrUpdate from './article-add-or-update'
// VuexmapStateVuex
import { mapState } from 'vuex' import { mapState } from 'vuex'
export default { export default {
components: { components: {
@ -92,19 +70,15 @@ export default {
} }
}, },
computed: mapState({ computed: mapState({
// VuexstatearticleARTICLE_TYPES ARTICLE_TYPES: state=>state.article.ARTICLE_TYPES
ARTICLE_TYPES: state => state.article.ARTICLE_TYPES
}), }),
mounted() { mounted() {
// getDataList
this.getDataList() this.getDataList()
}, },
methods: { methods: {
// //
getDataList() { getDataList() {
// true
this.dataListLoading = true this.dataListLoading = true
// HTTPURL
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/article/list'), url: this.$http.adornUrl('/manage/article/list'),
method: 'get', method: 'get',
@ -117,60 +91,52 @@ export default {
'order': 'desc' 'order': 'desc'
}) })
}).then(({ data }) => { }).then(({ data }) => {
//
if (data && data.code === 200) { if (data && data.code === 200) {
//
this.dataList = data.page.list this.dataList = data.page.list
this.totalCount = data.page.totalCount this.totalCount = data.page.totalCount
} else { } else {
//
this.dataList = [] this.dataList = []
this.totalCount = 0 this.totalCount = 0
} }
// false
this.dataListLoading = false this.dataListLoading = false
}) })
}, },
// 1 //
sizeChangeHandle(val) { sizeChangeHandle(val) {
this.pageSize = val this.pageSize = val
this.pageIndex = 1 this.pageIndex = 1
this.getDataList() this.getDataList()
}, },
// //
currentChangeHandle(val) { currentChangeHandle(val) {
this.pageIndex = val this.pageIndex = val
this.getDataList() this.getDataList()
}, },
// //
selectionChangeHandle(val) { selectionChangeHandle(val) {
this.dataListSelections = val this.dataListSelections = val
}, },
// //trueDOM/id // /
addOrUpdateHandle(id) { addOrUpdateHandle(id) {
this.addOrUpdateVisible = true this.addOrUpdateVisible = true
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.addOrUpdate.init(id) this.$refs.addOrUpdate.init(id)
}) })
}, },
// idHTTP //
deleteHandle(id) { deleteHandle(id) {
// id使id var ids = id ? [id] : this.dataListSelections.map(item => item.id)
var ids = id? [id] : this.dataListSelections.map(item => item.id) this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
// HTTP
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/article/delete'), url: this.$http.adornUrl('/manage/article/delete'),
method: 'post', method: 'post',
data: this.$http.adornData(ids, false) data: this.$http.adornData(ids, false)
}).then(({ data }) => { }).then(({ data }) => {
//
if (data && data.code === 200) { if (data && data.code === 200) {
//
this.$message({ this.$message({
message: '操作成功', message: '操作成功',
type: 'success', type: 'success',
@ -178,16 +144,14 @@ export default {
onClose: () => this.getDataList() onClose: () => this.getDataList()
}) })
} else { } else {
//
this.$message.error(data.msg) this.$message.error(data.msg)
} }
}) })
}) })
}, },
// ARTICLE_TYPES
articleTypeFormat(row, column, cellValue) { articleTypeFormat(row, column, cellValue) {
return this.ARTICLE_TYPES[cellValue]; return this.ARTICLE_TYPES[cellValue];
} }
} }
} }
</script> </script>

@ -1,44 +1,38 @@
<template> <template>
<!-- 选择素材的对话框 --> <el-dialog title="选择素材" :visible.sync="dataVisible" :modal="true" append-to-body @close="onClose">
<el-dialog title="选择素材" :visible.sync="dataVisible" :modal="true" append-to-body @close="onClose"> <material-news v-if="selectType=='news'" @selected="onSelect" selectMode></material-news>
<!-- 根据选择的素材类型加载不同的子组件 --> <material-file v-else :fileType="selectType" @selected="onSelect" selectMode></material-file>
<material-news v-if="selectType === 'news'" @selected="onSelect" selectMode></material-news> </el-dialog>
<material-file v-else :fileType="selectType" @selected="onSelect" selectMode></material-file> </template>
</el-dialog> <script>
</template> export default {
name:"assets-selector",
<script> data:function (){
export default { return {
name: "assets-selector", // dataVisible : this.visible
data: function () { }
return { },
dataVisible: this.visible // dataVisible visible components:{
} MaterialFile:()=>import('./material-file'),
}, MaterialNews:()=>import('./material-news')
components: { },
// props:{
MaterialFile: () => import('./material-file'), // MaterialFile selectType:{// imagevoicevideonews
MaterialNews: () => import('./material-news') // MaterialNews type:String,
}, default:'image'
props: { },
selectType: { // imagevoicevideonews visible:{
type: String, type:Boolean,
default: 'image' // 'image' default:false
}, }
visible: { // },
type: Boolean, methods:{
default: false // onSelect(itemInfo){
} this.$emit('selected', itemInfo)
}, },
methods: { onClose(){
// this.$emit('onClose')
onSelect(itemInfo) { }
this.$emit('selected', itemInfo) // }
}, }
//
onClose() {
this.$emit('onClose') //
}
}
}
</script> </script>

@ -1,118 +1,103 @@
<template> <template>
<!-- 对话框标题根据是否有 id 决定是新增还是修改 --> <el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible">
<el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible"> <el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<!-- 表单区域 --> <el-form-item label="媒体文件">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px"> <el-button type="primary">
<!-- 媒体文件选择 --> 选择文件
<el-form-item label="媒体文件"> <input type="file" style="opacity: 0;height: 100%;position: absolute;left: 0;top: 0;" @change="onFileChange" />
<el-button type="primary"> </el-button>
选择文件 <div>{{dataForm.file.name}}</div>
<!-- 隐藏的文件输入框选择文件后触发 onFileChange 方法 --> </el-form-item>
<input type="file" style="opacity: 0; height: 100%; position: absolute; left: 0; top: 0;" @change="onFileChange" /> <el-form-item label="媒体类型" prop="mediaType">
</el-button> <el-select v-model="dataForm.mediaType" placeholder="媒体类型" style="width:100%">
<!-- 显示已选择的文件名称 --> <el-option label="图片2M以内支持PNG\JPEG\JPG\GIF" value="image"></el-option>
<div>{{dataForm.file.name}}</div> <el-option label="视频10M以内只支持MP4" value="video"></el-option>
</el-form-item> <el-option label="语音2M、60s以内支持AMR\MP3" value="voice"></el-option>
<!-- 媒体类型选择 --> <el-option label="缩略图64K以内JPG" value="thumb"></el-option>
<el-form-item label="媒体类型" prop="mediaType"> </el-select>
<el-select v-model="dataForm.mediaType" placeholder="媒体类型" style="width:100%"> </el-form-item>
<el-option label="图片2M以内支持PNG\JPEG\JPG\GIF" value="image"></el-option> <el-form-item label="素材名称" prop="fileName">
<el-option label="视频10M以内只支持MP4" value="video"></el-option> <el-input v-model="dataForm.fileName" placeholder="为便于管理建议按用途分类+素材内容命名"></el-input>
<el-option label="语音2M、60s以内支持AMR\MP3" value="voice"></el-option> </el-form-item>
<el-option label="缩略图64K以内JPG" value="thumb"></el-option> </el-form>
</el-select> <span slot="footer" class="dialog-footer">
</el-form-item> <el-button @click="visible = false">取消</el-button>
<!-- 素材名称输入 --> <el-button type="primary" @click="dataFormSubmit()" :disabled="uploading">{{uploading?'提交中...':'提交'}}</el-button>
<el-form-item label="素材名称" prop="fileName"> </span>
<el-input v-model="dataForm.fileName" placeholder="为便于管理建议按用途分类+素材内容命名"></el-input> </el-dialog>
</el-form-item> </template>
</el-form>
<!-- 对话框底部按钮 -->
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()" :disabled="uploading">{{uploading ? '提交中...' : '提交'}}</el-button>
</span>
</el-dialog>
</template>
<script> <script>
export default { export default {
data() { data() {
return { return {
visible: false, // visible: false,
uploading: false, // uploading:false,
dataForm: { // dataForm: {
mediaId: '', // ID mediaId: '',
file: '', // file: '',
fileName: '', // fileName: '',
mediaType: 'image' // 'image' mediaType: 'image'
}, },
dataRule: { // dataRule: {
fileName: [ fileName: [
{ required: true, message: '素材名称不能为空', trigger: 'blur' } // { required: true, message: '素材名称不能为空', trigger: 'blur' }
], ],
mediaType: [ mediaType: [
{ required: true, message: '素材类型不能为空', trigger: 'blur' } // { required: true, message: '素材类型不能为空', trigger: 'blur' }
] ]
} }
} }
}, },
methods: { methods: {
// init(fileType) {
init(fileType) { if(fileType)this.dataForm.mediaType=fileType
if (fileType) this.dataForm.mediaType = fileType // this.visible = true
this.visible = true // },
}, //
// dataFormSubmit() {
dataFormSubmit() { if(this.uploading)return
if (this.uploading) return // this.$refs['dataForm'].validate((valid) => {
// if (valid) {
this.$refs['dataForm'].validate((valid) => { this.uploading=true
if (valid) { console.log(this.dataForm)
this.uploading = true // let form = new FormData();
console.log(this.dataForm) // form.append('mediaId', this.dataForm.mediaId || '')
let form = new FormData(); // FormData form.append('file', this.dataForm.file)
// FormData form.append('fileName', this.dataForm.fileName)
form.append('mediaId', this.dataForm.mediaId || '') form.append('mediaType', this.dataForm.mediaType)
form.append('file', this.dataForm.file) this.$http({
form.append('fileName', this.dataForm.fileName) url: this.$http.adornUrl(`/manage/wxAssets/materialFileUpload`),
form.append('mediaType', this.dataForm.mediaType) method: 'post',
// POST data: form,
this.$http({ headers: { 'Content-Type': 'multipart/form-data' }
url: this.$http.adornUrl(`/manage/wxAssets/materialFileUpload`), // URL }).then(({ data }) => {
method: 'post', if (data && data.code === 200) {
data: form, this.$message({
headers: { 'Content-Type': 'multipart/form-data' } // multipart/form-data message: '操作成功',
}).then(({ data }) => { type: 'success',
if (data && data.code === 200) { duration: 1500,
// onClose: () => {
this.$message({ this.visible = false
message: '操作成功', this.$emit('refreshDataList')
type: 'success', }
duration: 1500, })
onClose: () => { } else {
this.visible = false // this.$message.error(data.msg)
this.$emit('refreshDataList') // }
} this.uploading=false
}) })
} else { }
// })
this.$message.error(data.msg) },
} onFileChange(e) {
this.uploading = false // let file = event.currentTarget.files[0]
}) this.dataForm.file = file;
} this.dataForm.fileName = file.name.substring(0, file.name.lastIndexOf('.'))
}) let mediaType = file.type.substring(0, file.type.lastIndexOf('/'))
}, if (mediaType == 'audio') mediaType = 'voice'
// this.dataForm.mediaType = mediaType
onFileChange(e) { }
let file = e.currentTarget.files[0] // }
this.dataForm.file = file; // }
this.dataForm.fileName = file.name.substring(0, file.name.lastIndexOf('.')) // </script>
let mediaType = file.type.substring(0, file.type.lastIndexOf('/')) //
if (mediaType === 'audio') mediaType = 'voice' // 'voice'
this.dataForm.mediaType = mediaType //
}
}
}
</script>

@ -1,207 +1,185 @@
<template> <template>
<div class="mod-menu"> <div class="mod-menu">
<!-- 表单用于新增按钮 --> <el-form :inline="true" :model="dataForm">
<el-form :inline="true" :model="dataForm"> <el-form-item v-show="!selectMode">
<el-form-item v-show="!selectMode"> <el-button size="mini" v-if="isAuth('wx:wxassets:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
<!-- 新增按钮只有在有权限时才显示 --> </el-form-item>
<el-button size="mini" v-if="isAuth('wx:wxassets:save')" type="primary" @click="addOrUpdateHandle()"></el-button> </el-form>
</el-form-item> <div v-loading="dataListLoading">
</el-form> <div class="card" v-for="item in dataList" :key="item.mediaId" @click="onSelect(item)">
<!-- 数据列表加载状态 --> <el-image v-if="fileType=='image'" class="card-image" :src="item.url" fit="contain" lazy></el-image>
<div v-loading="dataListLoading"> <div v-else class="card-preview">
<!-- 遍历数据列表生成每个素材的卡片 --> <div v-if="fileType=='voice'" class="card-preview-icon el-icon-microphone"></div>
<div class="card" v-for="item in dataList" :key="item.mediaId" @click="onSelect(item)"> <div v-if="fileType=='video'" class="card-preview-icon el-icon-video-camera-solid"></div>
<!-- 如果是图片类型显示图片 --> <div class="card-preview-text">管理后台不支持预览<br/>微信中可正常播放</div>
<el-image v-if="fileType === 'image'" class="card-image" :src="item.url" fit="contain" lazy></el-image> </div>
<div v-else class="card-preview"> <div class="card-footer">
<!-- 根据不同的媒体类型显示不同的图标和提示 --> <div class="text-cut-name">{{item.name}}</div>
<div v-if="fileType === 'voice'" class="card-preview-icon el-icon-microphone"></div> <div>{{$moment(item.updateTime).calendar()}}</div>
<div v-if="fileType === 'video'" class="card-preview-icon el-icon-video-camera-solid"></div> <div class="flex justify-between align-center" v-show="!selectMode">
<div class="card-preview-text">管理后台不支持预览<br/>微信中可正常播放</div> <el-button size="mini" type="text" icon="el-icon-copy-document" v-clipboard:copy="item.mediaId" v-clipboard:success="onCopySuccess" v-clipboard:error="onCopyError">复制media_id</el-button>
</div> <el-button size="mini" type="text" icon="el-icon-delete" @click="deleteHandle(item.mediaId)" >删除</el-button>
<div class="card-footer"> </div>
<!-- 显示素材名称和更新时间 --> </div>
<div class="text-cut-name">{{item.name}}</div> </div>
<div>{{$moment(item.updateTime).calendar()}}</div> </div>
<div class="flex justify-between align-center" v-show="!selectMode"> <el-pagination @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[20]" :page-size="20" :total="totalCount" layout="total, prev,pager, next, jumper">
<!-- 复制 media_id 的按钮 --> </el-pagination>
<el-button size="mini" type="text" icon="el-icon-copy-document" v-clipboard:copy="item.mediaId" v-clipboard:success="onCopySuccess" v-clipboard:error="onCopyError">复制media_id</el-button> <!-- 弹窗, 新增 / 修改 -->
<!-- 删除素材的按钮 --> <add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="onChange"></add-or-update>
<el-button size="mini" type="text" icon="el-icon-delete" @click="deleteHandle(item.mediaId)"></el-button> </div>
</div> </template>
</div> <script>
</div> import AddOrUpdate from './material-file-add-or-update'
</div> export default {
<!-- 分页组件 --> name:'material-file',
<el-pagination @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[20]" :page-size="20" :total="totalCount" layout="total, prev, pager, next, jumper"> props:{
</el-pagination> fileType:{// imagevoicevideo
<!-- 弹窗用于新增/修改素材 --> type:String,
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="onChange"></add-or-update> default:'image'
</div> },
</template> selectMode:{//
type:Boolean,
default:false
}
},
components: {
AddOrUpdate
},
data() {
return {
dataForm: {},
addOrUpdateVisible: false,
dataList: [],
pageIndex: 1,
pageSize: 20,
totalCount: 0,
dataListLoading: false,
}
},
mounted(){
this.init()
},
methods: {
init(){
if(!this.dataList.length){
this.getDataList()
}
},
getDataList() {
if(this.dataListLoading) return
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/manage/wxAssets/materialFileBatchGet'),
params: this.$http.adornParams({
'page': this.pageIndex,
'type': this.fileType
})
}).then(({ data }) => {
if (data && data.code == 200) {
this.dataList = data.data.items
this.totalCount = data.data.totalCount
this.pageIndex++;
} else {
this.$message.error(data.msg);
}
this.dataListLoading = false
})
},
// /
addOrUpdateHandle() {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(this.fileType)
})
},
onSelect(itemInfo){
if(!this.selectMode)return
this.$emit('selected',itemInfo)
},
//
deleteHandle(id) {
this.$confirm(`确定对[mediaId=${id}]进行删除操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/manage/wxAssets/materialDelete'),
method: 'post',
data: { mediaId: id }
}).then(({ data }) => {
if (data && data.code === 200) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => this.onChange()
})
} else {
this.$message.error(data.msg)
}
})
})
},
//
currentChangeHandle(val) {
this.pageIndex = val
this.getDataList()
},
onCopySuccess(){
this.$message.success('已复制')
},
onCopyError(err){
this.$message.error('复制失败,可能是此浏览器不支持复制')
},
onChange(){
this.pageIndex=1
this.getDataList()
this.$emit('change')
}
<script> }
import AddOrUpdate from './material-file-add-or-update' // / }
</script>
export default { <style scoped>
name: 'material-file', // .card{
props: { width: 170px;
fileType: { // imagevoicevideo display: inline-block;
type: String, background: #FFFFFF;
default: 'image' border: 1px solid #EBEEF5;
}, box-shadow: 1px 1px 20px 0 rgba(0, 0, 0, 0.1);
selectMode: { // margin: 0 10px 10px 0;
type: Boolean, vertical-align: top;
default: false border-radius: 5px;
} box-sizing: border-box;
}, }
components: { .card:hover{
AddOrUpdate // / border: 2px solid #66b1ff;
}, margin-bottom: 6px;
data() { }
return { .card-image{
dataForm: {}, // line-height: 170px;
addOrUpdateVisible: false, // / max-height: 170px;
dataList: [], // width: 100%;
pageIndex: 1, // }
pageSize: 20, // .card-preview{
totalCount: 0, // padding: 20px 0;
dataListLoading: false, // color: #d9d9d9;
} display: flex;
}, justify-content: center;
mounted() { align-items: center;
this.init() // }
}, .card-preview-icon{
methods: { font-size: 30px;
init() { margin-right: 5px;
// }
if (!this.dataList.length) { .card-preview-text{
this.getDataList() font-size: 12px;
} }
}, .card-footer{
getDataList() { color: #ccc;
// font-size: 12px;
if (this.dataListLoading) return // padding: 15px 10px;
this.dataListLoading = true // }
this.$http({
url: this.$http.adornUrl('/manage/wxAssets/materialFileBatchGet'), // URL
params: this.$http.adornParams({
'page': this.pageIndex, //
'type': this.fileType //
})
}).then(({ data }) => {
if (data && data.code == 200) {
//
this.dataList = data.data.items
this.totalCount = data.data.totalCount
this.pageIndex++; //
} else {
//
this.$message.error(data.msg);
}
this.dataListLoading = false //
})
},
// /
addOrUpdateHandle() {
this.addOrUpdateVisible = true // /
this.$nextTick(() => {
this.$refs.addOrUpdate.init(this.fileType) // /
})
},
onSelect(itemInfo) {
//
if (!this.selectMode) return //
this.$emit('selected', itemInfo) //
},
//
deleteHandle(id) {
this.$confirm(`确定对[mediaId=${id}]进行删除操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/manage/wxAssets/materialDelete'), // URL
method: 'post',
data: { mediaId: id } // ID
}).then(({ data }) => {
if (data && data.code === 200) {
//
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => this.onChange() //
})
} else {
//
this.$message.error(data.msg)
}
})
})
},
//
currentChangeHandle(val) {
this.pageIndex = val //
this.getDataList() //
},
onCopySuccess() {
//
this.$message.success('已复制')
},
onCopyError(err) {
//
this.$message.error('复制失败, 可能是此浏览器不支持复制')
},
onChange() {
//
this.pageIndex = 1 // 1
this.getDataList() //
this.$emit('change') //
}
}
}
</script>
<style scoped>
.card {
width: 170px; /* 卡片宽度 */
display: inline-block; /* 以行内块元素方式显示,允许多个卡片横向排列 */
background: #FFFFFF; /* 背景颜色 */
border: 1px solid #EBEEF5; /* 边框颜色 */
box-shadow: 1px 1px 20px 0 rgba(0, 0, 0, 0.1); /* 盒子阴影 */
margin: 0 10px 10px 0; /* 外边距 */
vertical-align: top; /* 垂直对齐 */
border-radius: 5px; /* 圆角 */
box-sizing: border-box; /* 包含内边距和边框在内的盒模型 */
}
.card:hover {
border: 2px solid #66b1ff; /* 鼠标悬停时加粗边框 */
margin-bottom: 6px; /* 下方外边距调整 */
}
.card-image {
line-height: 170px; /* 设定行高以居中内容 */
max-height: 170px; /* 最大高度 */
width: 100%; /* 宽度100% */
}
.card-preview {
padding: 20px 0; /* 上下内边距 */
color: #d9d9d9; /* 字体颜色 */
display: flex; /* 使用弹性布局 */
justify-content: center; /* 内容居中对齐 */
align-items: center; /* 垂直居中对齐 */
}
.card-preview-icon {
font-size: 30px; /* 图标字体大小 */
margin-right: 5px; /* 右侧外边距 */
}
.card-preview-text {
font-size: 12px; /* 文本大小 */
}
.card-footer {
color: #ccc; /* 字体颜色 */
font-size: 12px; /* 字体大小 */
padding: 15px 10px; /* 内边距 */
}
</style> </style>

@ -1,232 +1,221 @@
<template> <template>
<div v-show="visible"> <!-- --> <div v-show="visible">
<div class="flex"> <div class="flex">
<div class="card-list"> <div class="card-list">
<div class="text-center margin-bottom">图文列表</div> <div class="text-center margin-bottom">图文列表</div>
<!-- 遍历所有文章生成列表项 --> <div class="card-item" :class="{'selected':selectedIndex==index}" v-for="(item,index) in articles" :key="index" @click="selectedIndex=index">
<div class="card-item" :class="{'selected':selectedIndex==index}" v-for="(item,index) in articles" :key="index" @click="selectedIndex=index"> <div class="text-cut-name">{{item.title}}</div>
<div class="text-cut-name">{{item.title}}</div> </div>
</div> <div v-show="articles.length<8 && !mediaId" class="card-add el-icon-plus" @click="addArticle()"></div>
<!-- 如果文章少于8篇且没有媒体ID显示新增文章的按钮 --> </div>
<div v-show="articles.length<8 && !mediaId" class="card-add el-icon-plus" @click="addArticle()"></div> <el-form size="mini" v-if="articles.length" :model="articles[selectedIndex]" :rules="dataRule" ref="dataForm" label-width="100px">
</div> <el-form-item label="标题" prop="title">
<!-- 文章表单绑定到选中的文章 --> <el-input v-model="articles[selectedIndex].title" placeholder="标题"></el-input>
<el-form size="mini" v-if="articles.length" :model="articles[selectedIndex]" :rules="dataRule" ref="dataForm" label-width="100px"> </el-form-item>
<el-form-item label="标题" prop="title"> <el-form-item label="封面图" prop="thumbMediaId">
<el-input v-model="articles[selectedIndex].title" placeholder="标题"></el-input> <el-input v-model="articles[selectedIndex].thumbMediaId" placeholder="封面图media_id">
</el-form-item> <div slot="append" @click="assetsSelectorVisible=true"></div>
<el-form-item label="封面图" prop="thumbMediaId"> </el-input>
<el-input v-model="articles[selectedIndex].thumbMediaId" placeholder="封面图media_id"> </el-form-item>
<div slot="append" @click="assetsSelectorVisible=true"></div> <!-- --> <el-form-item label="摘要" prop="digest">
</el-input> <el-input v-model="articles[selectedIndex].digest" placeholder="摘要"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="摘要" prop="digest"> <el-form-item label="原文地址" prop="contentSourceUrl">
<el-input v-model="articles[selectedIndex].digest" placeholder="摘要"></el-input> <el-input v-model="articles[selectedIndex].contentSourceUrl" placeholder="阅读原文链接"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="原文地址" prop="contentSourceUrl"> <el-row>
<el-input v-model="articles[selectedIndex].contentSourceUrl" placeholder="阅读原文链接"></el-input> <el-col :span="9">
</el-form-item> <el-form-item label="作者" prop="author">
<el-row> <el-input v-model="articles[selectedIndex].author" placeholder="作者"></el-input>
<el-col :span="9"> </el-form-item>
<el-form-item label="作者" prop="author"> </el-col>
<el-input v-model="articles[selectedIndex].author" placeholder="作者"></el-input> <el-col :span="5">
</el-form-item> <el-form-item label="显示封面" prop="showCoverPic">
</el-col> <el-switch v-model="articles[selectedIndex].showCoverPic"></el-switch>
<el-col :span="5"> </el-form-item>
<el-form-item label="显示封面" prop="showCoverPic"> </el-col>
<el-switch v-model="articles[selectedIndex].showCoverPic"></el-switch> <el-col :span="5">
</el-form-item> <el-form-item label="允许评论" prop="needOpenComment">
</el-col> <el-switch v-model="articles[selectedIndex].needOpenComment"></el-switch>
<el-col :span="5"> </el-form-item>
<el-form-item label="允许评论" prop="needOpenComment"> </el-col>
<el-switch v-model="articles[selectedIndex].needOpenComment"></el-switch> <el-col :span="5">
</el-form-item> <el-form-item label="仅粉丝可评论" prop="onlyFansCanComment">
</el-col> <el-switch v-model="articles[selectedIndex].onlyFansCanComment"></el-switch>
<el-col :span="5"> </el-form-item>
<el-form-item label="仅粉丝可评论" prop="onlyFansCanComment"> </el-col>
<el-switch v-model="articles[selectedIndex].onlyFansCanComment"></el-switch> </el-row>
</el-form-item> <el-form-item label="内容" prop="content">
</el-col> <tinymce-editor ref="editor" v-model="articles[selectedIndex].content"> </tinymce-editor>
</el-row> </el-form-item>
<el-form-item label="内容" prop="content"> </el-form>
<tinymce-editor ref="editor" v-model="articles[selectedIndex].content"> </tinymce-editor> <!-- 富文本编辑器 --> </div>
</el-form-item> <div class="dialog-footer">
</el-form> <el-button @click="$emit('hide')"></el-button>
</div> <el-button type="primary" @click="dataFormSubmit()" :disabled="uploading">{{this.mediaId?'修改此篇':'全部提交(共'+articles.length+'篇)'}}</el-button>
<div class="dialog-footer"> </div>
<el-button @click="$emit('hide')"></el-button> <!-- 取消按钮 --> <assets-selector v-if="assetsSelectorVisible" :visible="assetsSelectorVisible" selectType="image" @selected="onAssetsSelect"></assets-selector>
<el-button type="primary" @click="dataFormSubmit()" :disabled="uploading">{{this.mediaId?'修改此篇':'全部提交(共'+articles.length+'篇)'}}</el-button> </div>
<!-- 提交按钮根据是否有mediaId来决定按钮显示内容 --> </template>
</div>
<assets-selector v-if="assetsSelectorVisible" :visible="assetsSelectorVisible" selectType="image" @selected="onAssetsSelect"></assets-selector> <!-- 选择素材框 -->
</div>
</template>
<script> <script>
const articleTemplate = { // const articleTemplate={
templateId: 0, templateId: 0,
title: '', title: '',
content: '', content: '',
author: '', author: '',
showCoverPic: true, showCoverPic: true,
contentSourceUrl: '', contentSourceUrl: '',
digest: '', digest: '',
thumbMediaId: '', thumbMediaId: '',
needOpenComment: false, needOpenComment: false,
onlyFansCanComment: false onlyFansCanComment: false
} }
export default { export default {
components: { components: {
TinymceEditor: () => import('@/components/tinymce-editor'), // TinymceEditor: () => import('@/components/tinymce-editor'),
AssetsSelector: () => import('./assets-selector') // AssetsSelector:()=>import('./assets-selector')
}, },
props: { props:{
visible: { // visible:{
type: Boolean, type:Boolean,
default: false default:false
} }
}, },
data() { data() {
return { return {
assetsSelectorVisible: false, // assetsSelectorVisible:false,
mediaId: '', // ID mediaId:'',
selectedIndex: 0, // selectedIndex:0,
articles: [], // articles:[],
uploading: false, // uploading:false,
dataRule: { // dataRule: {
title: [ title: [
{ required: true, message: '标题不能为空', trigger: 'blur' } { required: true, message: '标题不能为空', trigger: 'blur' }
], ],
content: [ content: [
{ required: true, message: '内容不能为空', trigger: 'blur' } { required: true, message: '内容不能为空', trigger: 'blur' }
], ],
thumbMediaId: [ thumbMediaId: [
{ required: true, message: '封面图media_id不能为空', trigger: 'blur' } { required: true, message: '封面图media_id不能为空', trigger: 'blur' }
], ],
contentSourceUrl: [ contentSourceUrl: [
{ required: true, message: '原文地址不得为空', trigger: 'blur' } { required: true, message: '原文地址不得为空', trigger: 'blur' }
] ]
} }
} }
}, },
methods: { methods: {
init(news) { // init(news){
if (news && news.mediaId) { if(news && news.mediaId){
this.mediaId = news.mediaId; // mediaIdmediaId this.mediaId=news.mediaId
this.articles = news.content.articles; // this.articles = news.content.articles
} else { }else{
this.mediaId = ''; // this.mediaId=''
this.articles = [{ ...articleTemplate }]; // 使 this.articles=[{...articleTemplate}]
} }
}, },
// //
dataFormSubmit() { dataFormSubmit() {
if (this.uploading) return; // if(this.uploading)return
this.$refs['dataForm'].validate((valid) => { // this.$refs['dataForm'].validate((valid) => {
if (valid) { if (valid) {
if (this.mediaId) { // mediaId if(this.mediaId){//
this.materialArticleUpdate(); this.materialArticleUpdate();
} else { // }else{ //
this.materialNewsUpload(); this.materialNewsUpload();
} }
} }
}); })
}, },
// materialNewsUpload(){
materialNewsUpload() { this.uploading=true
this.uploading = true; // true this.$http({
this.$http({ url: this.$http.adornUrl(`/manage/wxAssets/materialNewsUpload`),
url: this.$http.adornUrl(`/manage/wxAssets/materialNewsUpload`), // URL method: 'post',
method: 'post', // data: this.$http.adornData(this.articles,false)
data: this.$http.adornData(this.articles, false) // }).then(({ data }) => {
}).then(({ data }) => { if (data && data.code === 200) {
// this.$message({
if (data && data.code === 200) { message: "操作成功",
this.$message({ type: "success",
message: "操作成功", // duration: 1500,
type: "success", onClose: () => {
duration: 1500, this.$emit("refreshDataList");
onClose: () => { this.emit('hide')
this.$emit("refreshDataList"); // }
this.$emit('hide'); // });
} } else {
}); this.$message.error(data.msg)
} else { }
this.$message.error(data.msg); // this.uploading=false
} })
this.uploading = false; // },
}); materialArticleUpdate(){
}, this.uploading=true
// this.$http({
materialArticleUpdate() { url: this.$http.adornUrl(`/manage/wxAssets/materialArticleUpdate`),
this.uploading = true; // true method: 'post',
this.$http({ data: this.$http.adornData({
url: this.$http.adornUrl(`/manage/wxAssets/materialArticleUpdate`), // URL 'mediaId':this.mediaId,
method: 'post', // 'index':this.selectedIndex,
data: this.$http.adornData({ 'articles':this.articles[this.selectedIndex]
'mediaId': this.mediaId, // mediaId })
'index': this.selectedIndex, // }).then(({ data }) => {
'articles': this.articles[this.selectedIndex] // if (data && data.code === 200) {
}) this.$message.success('操作成功')
}).then(({ data }) => { } else {
// this.$message.error(data.msg)
if (data && data.code === 200) { }
this.$message.success('操作成功'); // this.uploading=false
} else { })
this.$message.error(data.msg); // },
} addArticle(){
this.uploading = false; // this.articles.push({...articleTemplate})
}); this.selectedIndex=this.articles.length-1
}, },
// onAssetsSelect(assetsInfo){
addArticle() { Vue.set(this.articles[this.selectedIndex], 'thumbMediaId', assetsInfo.mediaId)
this.articles.push({ ...articleTemplate }); // this.assetsSelectorVisible=false
this.selectedIndex = this.articles.length - 1; // }
}, }
// }
onAssetsSelect(assetsInfo) { </script>
Vue.set(this.articles[this.selectedIndex], 'thumbMediaId', assetsInfo.mediaId); // mediaId <style scoped>
this.assetsSelectorVisible = false; // .card-list{
} width: 300px;
} padding-right: 10px;
} border-right: 1px solid #eeeeee;
</script> }
.card-item{
<style scoped> margin-top: 2px;
.card-list { padding: 20px 5px;
width: 300px; /* 卡片列表的宽度 */ border: 1px solid #ddd;
padding-right: 10px; /* 右侧内边距 */ font-size: 12px;
border-right: 1px solid #eeeeee; /* 右侧边框 */ line-height: 15px;
} }
.card-item { .card-item.selected{
margin-top: 2px; /* 上外边距 */ border: 2px solid #409EFF;
padding: 20px 5px; /* 内边距 */ }
border: 1px solid #ddd; /* 边框 */ .text-cut-name{
font-size: 12px; /* 字体大小 */ display: -webkit-box;
line-height: 15px; /* 行高 */ word-wrap:break-word;
} word-break:break-all;
.card-item.selected { -webkit-box-orient: vertical;
border: 2px solid #409EFF; /* 选中时的边框颜色 */ -webkit-line-clamp: 2;
} overflow: hidden;
.text-cut-name { }
display: -webkit-box; /* 使用盒子模型 */ .card-add{
word-wrap: break-word; /* 自动换行 */ margin-top: 2px;
word-break: break-all; /* 强制断行 */ display: block;
-webkit-box-orient: vertical; /* 垂直方向的盒子模型 */ border: 1px dotted #ddd;
-webkit-line-clamp: 2; /* 限制显示的行数 */ color: #ddd;
overflow: hidden; /* 溢出隐藏 */ text-align: center;
} font-size: 30px;
.card-add { line-height: 50px;
margin-top: 2px; /* 上外边距 */ }
display: block; /* 块级元素 */ .dialog-footer {
border: 1px dotted #ddd; /* 虚线边框 */ margin-top: 20px;
color: #ddd; /* 文字颜色 */ text-align: right;
text-align: center; /* 居中对齐 */ }
font-size: 30px; /* 文字大小 */
line-height: 50px; /* 行高 */
}
.dialog-footer {
margin-top: 20px; /* 上外边距 */
text-align: right; /* 右对齐 */
}
</style> </style>

@ -1,45 +1,26 @@
<template> <template>
<!-- 整个页面的面板容器 -->
<div class="panel"> <div class="panel">
<!-- 根据条件控制显示内容当addOrUpdateVisible为false时显示以下内容 -->
<div v-show="!addOrUpdateVisible"> <div v-show="!addOrUpdateVisible">
<!-- 内联表单绑定了dataForm数据模型 -->
<el-form :inline="true" :model="dataForm"> <el-form :inline="true" :model="dataForm">
<!-- 根据selectMode的值决定是否显示该表单元素当不是选择模式时显示 -->
<el-form-item v-show="!selectMode"> <el-form-item v-show="!selectMode">
<!-- 按钮根据权限判断是否显示调用isAuth方法判断类型为主要按钮点击时调用addOrUpdateHandle方法按钮尺寸为迷你型 -->
<el-button size="mini" v-if="isAuth('wx:wxassets:save')" type="primary" @click="addOrUpdateHandle()"></el-button> <el-button size="mini" v-if="isAuth('wx:wxassets:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 加载提示容器根据dataListLoading的值显示加载状态 -->
<div class="flex justify-start" v-loading="dataListLoading"> <div class="flex justify-start" v-loading="dataListLoading">
<!-- 循环生成行n表示行号这里的rows应该是控制每行显示的元素数量相关 -->
<div v-for="n in rows" :key="n"> <div v-for="n in rows" :key="n">
<!-- 循环遍历dataList数据列表用于展示具体的数据项 -->
<template v-for="(item,i) in dataList"> <template v-for="(item,i) in dataList">
<!-- 根据条件显示卡片元素当满足i%rows==n-1时显示点击卡片会调用onSelect方法传递当前项数据 -->
<div class="card" :key="item.mediaId" v-if="i%rows==n-1" @click="onSelect(item)"> <div class="card" :key="item.mediaId" v-if="i%rows==n-1" @click="onSelect(item)">
<!-- 卡片的预览部分用于展示文章相关信息 -->
<div class="card-preview"> <div class="card-preview">
<!-- 循环遍历文章数组展示每篇文章的信息点击文章链接会在新标签页打开target="_blank" -->
<a v-for="(article,k) in item.content.articles" :key="k" :href="article.url" class="article-item" target="_blank"> <a v-for="(article,k) in item.content.articles" :key="k" :href="article.url" class="article-item" target="_blank">
<!-- 文章标题做了多行文本截断显示处理 -->
<div class="article-title">{{article.title}}</div> <div class="article-title">{{article.title}}</div>
<!-- 文章缩略图通过绑定src属性显示图片 -->
<el-image class="article-thumb" :src="article.thumbUrl"></el-image> <el-image class="article-thumb" :src="article.thumbUrl"></el-image>
</a> </a>
</div> </div>
<!-- 卡片的底部部分用于展示更新时间以及操作按钮等 -->
<div class="card-footer"> <div class="card-footer">
<!-- 显示更新时间使用了moment.js库通过$moment方法调用将时间格式化为日历格式 -->
<div>{{$moment(item.updateTime).calendar()}}</div> <div>{{$moment(item.updateTime).calendar()}}</div>
<!-- 根据是否是选择模式决定是否显示以下操作按钮 -->
<div class="flex justify-between align-center" v-show="!selectMode"> <div class="flex justify-between align-center" v-show="!selectMode">
<!-- 复制按钮点击复制media_id复制成功和失败分别调用对应的方法 -->
<el-button size="mini" type="text" icon="el-icon-copy-document" v-clipboard:copy="item.mediaId" v-clipboard:success="onCopySuccess" v-clipboard:error="onCopyError">复制media_id</el-button> <el-button size="mini" type="text" icon="el-icon-copy-document" v-clipboard:copy="item.mediaId" v-clipboard:success="onCopySuccess" v-clipboard:error="onCopyError">复制media_id</el-button>
<!-- 编辑按钮点击调用addOrUpdateHandle方法传入当前项数据按钮类型为文本样式 -->
<el-button size="mini" type="text" icon="el-icon-edit" @click="addOrUpdateHandle(item)"></el-button> <el-button size="mini" type="text" icon="el-icon-edit" @click="addOrUpdateHandle(item)"></el-button>
<!-- 删除按钮点击调用deleteHandle方法传入mediaId进行删除操作按钮类型为文本样式 -->
<el-button size="mini" type="text" icon="el-icon-delete" @click="deleteHandle(item.mediaId)"></el-button> <el-button size="mini" type="text" icon="el-icon-delete" @click="deleteHandle(item.mediaId)"></el-button>
</div> </div>
</div> </div>
@ -47,23 +28,20 @@
</template> </template>
</div> </div>
</div> </div>
<!-- 分页组件绑定了相关的分页事件和属性用于切换页面获取不同页的数据 -->
<el-pagination @current-change="currentChangeHandle" :current-page="pageIndex" :page-size="pageSize" :total="totalCount" layout="total, prev,pager, next, jumper"> <el-pagination @current-change="currentChangeHandle" :current-page="pageIndex" :page-size="pageSize" :total="totalCount" layout="total, prev,pager, next, jumper">
</el-pagination> </el-pagination>
</div> </div>
<!-- 新增/修改组件通过属性和事件与父组件进行交互 --> <!-- 新增 / 修改 -->
<add-or-update :visible="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="onChange" @hide="addOrUpdateVisible=false"></add-or-update> <add-or-update :visible="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="onChange" @hide="addOrUpdateVisible=false"></add-or-update>
</div> </div>
</template> </template>
<script> <script>
// /
import AddOrUpdate from './material-news-add-or-update' import AddOrUpdate from './material-news-add-or-update'
export default { export default {
name: 'material-news', name: 'material-news',
components: { components: {
AddOrUpdate AddOrUpdate
}, },
//
props: { props: {
selectMode: {// selectMode: {//
type: Boolean, type: Boolean,
@ -86,63 +64,50 @@ export default {
} }
}, },
mounted(){ mounted(){
// init
this.init(); this.init();
}, },
methods: { methods: {
init() { init() {
// getDataList
if (!this.dataList.length) { if (!this.dataList.length) {
this.getDataList() this.getDataList()
} }
}, },
getDataList() { getDataList() {
//
if (this.dataListLoading) return if (this.dataListLoading) return
// true
this.dataListLoading = true this.dataListLoading = true
// HTTPURL
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/wxAssets/materialNewsBatchGet'), url: this.$http.adornUrl('/manage/wxAssets/materialNewsBatchGet'),
params: this.$http.adornParams({ params: this.$http.adornParams({
'page': this.pageIndex 'page': this.pageIndex
}) })
}).then(({ data }) => { }).then(({ data }) => {
//
if (data.code == 200) { if (data.code == 200) {
//
this.dataList = data.data.items this.dataList = data.data.items
this.totalCount = data.data.totalCount this.totalCount = data.data.totalCount
} else { } else {
//
this.$message.error(data.msg); this.$message.error(data.msg);
} }
// false
this.dataListLoading = false this.dataListLoading = false
}) })
}, },
onSelect(itemInfo) { onSelect(itemInfo) {
// $emitselected
if (!this.selectMode) return if (!this.selectMode) return
this.$emit('selected', itemInfo) this.$emit('selected', itemInfo)
}, },
// //
deleteHandle(id) { deleteHandle(id) {
//
this.$confirm(`确定对[mediaId=${id}]进行删除操作?`, '提示', { this.$confirm(`确定对[mediaId=${id}]进行删除操作?`, '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
// HTTP
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/wxAssets/materialDelete'), url: this.$http.adornUrl('/manage/wxAssets/materialDelete'),
method: 'post', method: 'post',
data: { mediaId: id } data: { mediaId: id }
}).then(({ data }) => { }).then(({ data }) => {
//
if (data && data.code === 200) { if (data && data.code === 200) {
// onChange
this.$message({ this.$message({
message: '操作成功', message: '操作成功',
type: 'success', type: 'success',
@ -150,18 +115,17 @@ export default {
onClose: () => this.onChange() onClose: () => this.onChange()
}) })
} else { } else {
//
this.$message.error(data.msg) this.$message.error(data.msg)
} }
}) })
}) })
}, },
// //
currentChangeHandle(val) { currentChangeHandle(val) {
this.pageIndex = val this.pageIndex = val
this.getDataList() this.getDataList()
}, },
// //trueDOM/ // /
addOrUpdateHandle(news) { addOrUpdateHandle(news) {
this.addOrUpdateVisible = true this.addOrUpdateVisible = true
this.$nextTick(() => { this.$nextTick(() => {
@ -169,15 +133,12 @@ export default {
}) })
}, },
onCopySuccess() { onCopySuccess() {
//
this.$message.success('已复制') this.$message.success('已复制')
}, },
onCopyError(err) { onCopyError(err) {
//
this.$message.error('复制失败,可能是此浏览器不支持复制') this.$message.error('复制失败,可能是此浏览器不支持复制')
}, },
onChange() { onChange() {
// 1$emitchange
this.pageIndex=1 this.pageIndex=1
this.getDataList() this.getDataList()
this.$emit('change') this.$emit('change')

@ -1,22 +1,14 @@
<template> <template>
<!-- el-dialog组件用于弹出对话框根据dataForm中是否有id来设置标题为新增修改点击模态框背景不关闭对话框通过visible属性双向绑定控制显示隐藏 --> <el-dialog :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible" >
<el-dialog :title="!dataForm.id? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible" >
<!-- el-form组件定义表单绑定dataForm数据模型应用dataRule验证规则设置标签宽度为80px -->
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" label-width="80px"> <el-form :model="dataForm" :rules="dataRule" ref="dataForm" label-width="80px">
<!-- 规则名称的表单项对应dataForm中的ruleName属性设置了必填验证 -->
<el-form-item label="规则名称" prop="ruleName"> <el-form-item label="规则名称" prop="ruleName">
<!-- el-input组件实现输入框双向绑定dataForm.ruleName有占位提示文字 -->
<el-input v-model="dataForm.ruleName" placeholder="规则名称"></el-input> <el-input v-model="dataForm.ruleName" placeholder="规则名称"></el-input>
</el-form-item> </el-form-item>
<!-- 匹配词的表单项使用自定义的tags-editor组件双向绑定dataForm.matchValue用于输入或编辑匹配词可能是多个类似标签形式 -->
<el-form-item label="匹配词" prop="matchValue"> <el-form-item label="匹配词" prop="matchValue">
<tags-editor v-model="dataForm.matchValue"></tags-editor> <tags-editor v-model="dataForm.matchValue"></tags-editor>
</el-form-item> </el-form-item>
<!-- 第一行布局使用el-row包裹 -->
<el-row> <el-row>
<!-- 占12列用于放置作用范围相关的表单元素 -->
<el-col :span="12"> <el-col :span="12">
<!-- 作用范围的表单项对应dataForm中的appid属性通过下拉选择框选择有全部公众号和当前公众号两个选项 -->
<el-form-item label="作用范围" prop="appid"> <el-form-item label="作用范围" prop="appid">
<el-select v-model="dataForm.appid" placeholder="作用范围"> <el-select v-model="dataForm.appid" placeholder="作用范围">
<el-option label="全部公众号" value=""></el-option> <el-option label="全部公众号" value=""></el-option>
@ -24,91 +16,71 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 同样占12列用于放置精确匹配相关的表单元素使用el-switch组件实现开关切换功能双向绑定dataForm.exactMatch -->
<el-col :span="12"> <el-col :span="12">
<el-form-item label="精确匹配" prop="exactMatch"> <el-form-item label="精确匹配" prop="exactMatch">
<el-switch v-model="dataForm.exactMatch" :active-value="true" :inactive-value="false"></el-switch> <el-switch v-model="dataForm.exactMatch" :active-value="true" :inactive-value="false"></el-switch>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<!-- 第二行布局 -->
<el-row> <el-row>
<!-- 占12列用于放置回复类型相关的表单元素 -->
<el-col :span="12"> <el-col :span="12">
<!-- 回复类型的表单项对应dataForm中的replyType属性通过下拉选择框选择选项通过循环KefuMsgType生成选择改变时触发onReplyTypeChange方法 -->
<el-form-item label="回复类型" prop="replyType"> <el-form-item label="回复类型" prop="replyType">
<el-select v-model="dataForm.replyType" @change="onReplyTypeChange"> <el-select v-model="dataForm.replyType" @change="onReplyTypeChange">
<el-option v-for="(name,key) in KefuMsgType" :key="key" :value="key" :label="name"></el-option> <el-option v-for="(name,key) in KefuMsgType" :key="key" :value="key" :label="name"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 占12列用于放置是否启用相关的表单元素使用el-switch组件实现开关切换功能双向绑定dataForm.status -->
<el-col :span="12"> <el-col :span="12">
<el-form-item label="是否启用" prop="status"> <el-form-item label="是否启用" prop="status">
<el-switch v-model="dataForm.status" :active-value="true" :inactive-value="false"></el-switch> <el-switch v-model="dataForm.status" :active-value="true" :inactive-value="false"></el-switch>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<!-- 第三行布局 -->
<el-row> <el-row>
<!-- 占12列用于放置生效时间相关的表单元素使用el-time-picker组件实现时间选择功能绑定dataForm.effectTimeStart设置时间格式 -->
<el-col :span="12"> <el-col :span="12">
<el-form-item label="生效时间" prop="effectTimeStart"> <el-form-item label="生效时间" prop="effectTimeStart">
<el-time-picker v-model="dataForm.effectTimeStart" value-format="HH:mm:ss"></el-time-picker> <el-time-picker v-model="dataForm.effectTimeStart" value-format="HH:mm:ss"></el-time-picker>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 占12列用于放置失效时间相关的表单元素使用el-time-picker组件实现时间选择功能绑定dataForm.effectTimeEnd设置时间格式 -->
<el-col :span="12"> <el-col :span="12">
<el-form-item label="失效时间" prop="effectTimeEnd"> <el-form-item label="失效时间" prop="effectTimeEnd">
<el-time-picker v-model="dataForm.effectTimeEnd" value-format="HH:mm:ss"></el-time-picker> <el-time-picker v-model="dataForm.effectTimeEnd" value-format="HH:mm:ss"></el-time-picker>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<!-- 回复内容的表单项对应dataForm中的replyContent属性使用el-input组件实现文本域输入框双向绑定dataForm.replyContent有行数设置和占位提示文字 -->
<el-form-item label="回复内容" prop="replyContent"> <el-form-item label="回复内容" prop="replyContent">
<el-input v-model="dataForm.replyContent" type="textarea" :rows="5" placeholder="文本、图文ID、media_id、json配置"></el-input> <el-input v-model="dataForm.replyContent" type="textarea" :rows="5" placeholder="文本、图文ID、media_id、json配置"></el-input>
<!-- 当回复类型为文本时显示插入链接按钮点击调用addLink方法在回复内容中添加链接 -->
<el-button type="text" v-show="'text'==dataForm.replyType" @click="addLink"></el-button> <el-button type="text" v-show="'text'==dataForm.replyType" @click="addLink"></el-button>
<!-- 根据assetsType的值决定是否显示按钮点击打开素材选择器通过控制assetsSelectorVisible属性按钮文字根据回复类型有所不同 -->
<el-button type="text" v-show="assetsType" @click="assetsSelectorVisible=true"> <el-button type="text" v-show="assetsType" @click="assetsSelectorVisible=true">
从素材库中选择<span v-if="'miniprogrampage'==dataForm.replyType || 'music'==dataForm.replyType"></span> 从素材库中选择<span v-if="'miniprogrampage'==dataForm.replyType || 'music'==dataForm.replyType"></span>
</el-button> </el-button>
</el-form-item> </el-form-item>
<!-- 备注说明的表单项对应dataForm中的desc属性使用el-input组件实现输入框双向绑定dataForm.desc有占位提示文字 -->
<el-form-item label="备注说明" prop="desc"> <el-form-item label="备注说明" prop="desc">
<el-input v-model="dataForm.desc" placeholder="备注说明"></el-input> <el-input v-model="dataForm.desc" placeholder="备注说明"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 对话框底部的按钮区域通过插槽定义 -->
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
<!-- 取消按钮点击时设置visible为false关闭对话框 -->
<el-button @click="visible = false">取消</el-button> <el-button @click="visible = false">取消</el-button>
<!-- 确定按钮类型为主要按钮点击时调用dataFormSubmit方法进行表单提交 -->
<el-button type="primary" @click="dataFormSubmit()"></el-button> <el-button type="primary" @click="dataFormSubmit()"></el-button>
</span> </span>
<!-- 素材选择器组件根据条件控制显示通过属性和事件与当前组件进行交互用于选择素材相关操作 -->
<assets-selector v-if="assetsSelectorVisible && assetsType" :visible="assetsSelectorVisible" :selectType="assetsType" @selected="onAssetsSelect" @onClose="assetsSelectorVisible=false"></assets-selector> <assets-selector v-if="assetsSelectorVisible && assetsType" :visible="assetsSelectorVisible" :selectType="assetsType" @selected="onAssetsSelect" @onClose="assetsSelectorVisible=false"></assets-selector>
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
// VuexmapStateVuex
import { mapState } from 'vuex' import { mapState } from 'vuex'
export default { export default {
components: { components: {
// tags-editor
tagsEditor: () => import('@/components/tags-editor'), tagsEditor: () => import('@/components/tags-editor'),
// AssetsSelector AssetsSelector:()=>import('./assets/assets-selector')
AssetsSelector: () => import('./assets/assets-selector')
}, },
data() { data() {
return { return {
visible: false, visible: false,
assetsSelectorVisible: false, assetsSelectorVisible:false,
dataForm: { dataForm: {
// ID0
ruleId: 0, ruleId: 0,
appid: '', appid:'',
ruleName: "", ruleName: "",
exactMatch: false, exactMatch: false,
matchValue: "", matchValue: "",
@ -120,31 +92,24 @@ export default {
effectTimeEnd: "23:59:59" effectTimeEnd: "23:59:59"
}, },
dataRule: { dataRule: {
// blur
ruleName: [ ruleName: [
{ required: true, message: "规则名称不能为空", trigger: "blur" } { required: true, message: "规则名称不能为空", trigger: "blur" }
], ],
//
matchValue: [ matchValue: [
{ required: true, message: "匹配的关键词、事件等不能为空", trigger: "blur" } { required: true, message: "匹配的关键词、事件等不能为空", trigger: "blur" }
], ],
//
replyType: [ replyType: [
{ required: true, message: "回复类型1:文本2:图文3媒体不能为空", trigger: "blur" } { required: true, message: "回复类型1:文本2:图文3媒体不能为空", trigger: "blur" }
], ],
//
replyContent: [ replyContent: [
{ required: true, message: "回复内容不能为空", trigger: "blur" } { required: true, message: "回复内容不能为空", trigger: "blur" }
], ],
//
status: [ status: [
{ required: true, message: "是否有效不能为空", trigger: "blur" } { required: true, message: "是否有效不能为空", trigger: "blur" }
], ],
//
effectTimeStart: [ effectTimeStart: [
{ required: true, message: "生效起始时间不能为空", trigger: "blur" } { required: true, message: "生效起始时间不能为空", trigger: "blur" }
], ],
//
effectTimeEnd: [ effectTimeEnd: [
{ required: true, message: "生效结束时间不能为空", trigger: "blur" } { required: true, message: "生效结束时间不能为空", trigger: "blur" }
] ]
@ -152,60 +117,49 @@ export default {
}; };
}, },
computed: mapState({ computed: mapState({
// VuexstatemessageKefuMsgType KefuMsgType: state=>state.message.KefuMsgType,
KefuMsgType: state => state.message.KefuMsgType, selectedAppid:state=>state.wxAccount.selectedAppid,
// VuexstatewxAccountselectedAppid assetsType(){
selectedAppid: state => state.wxAccount.selectedAppid, const config={//
assetsType() { 'image':'image',
const config = { 'voice':'voice',
// 'video':'video',
'image': 'image', 'mpnews':'news',
'voice': 'voice', 'miniprogrampage':'image',//
'video': 'video', 'music':'image'
'mpnews': 'news', }
'miniprogrampage': 'image', // return config[this.dataForm.replyType] || ''
'music': 'image'
};
return config[this.dataForm.replyType] || '';
} }
}), }),
methods: { methods: {
init(id) { init(id) {
// dataFormruleIdid0
this.dataForm.ruleId = id || 0; this.dataForm.ruleId = id || 0;
// true
this.visible = true; this.visible = true;
this.$nextTick(() => { this.$nextTick(() => {
// DOM
this.$refs["dataForm"].resetFields(); this.$refs["dataForm"].resetFields();
if (this.dataForm.ruleId) { if (this.dataForm.ruleId) {
// ruleIdHTTP
this.$http({ this.$http({
url: this.$http.adornUrl(`/manage/msgReplyRule/info/${this.dataForm.ruleId}`), url: this.$http.adornUrl( `/manage/msgReplyRule/info/${this.dataForm.ruleId}` ),
method: "get", method: "get",
params: this.$http.adornParams() params: this.$http.adornParams()
}).then(({ data }) => { }).then(({ data }) => {
if (data && data.code === 200) { if (data && data.code === 200) {
// dataForm
this.dataForm = data.msgReplyRule; this.dataForm = data.msgReplyRule;
} }
}); });
} }
}); });
}, },
// //
dataFormSubmit() { dataFormSubmit() {
// valid
this.$refs["dataForm"].validate(valid => { this.$refs["dataForm"].validate(valid => {
if (valid) { if (valid) {
// HTTPruleId
this.$http({ this.$http({
url: this.$http.adornUrl(`/manage/msgReplyRule/${!this.dataForm.ruleId? "save" : "update"}`), url: this.$http.adornUrl(`/manage/msgReplyRule/${!this.dataForm.ruleId ? "save" : "update"}`),
method: "post", method: "post",
data: this.$http.adornData(this.dataForm) data: this.$http.adornData(this.dataForm)
}).then(({ data }) => { }).then(({ data }) => {
if (data && data.code === 200) { if (data && data.code === 200) {
// $emitrefreshDataList
this.$message({ this.$message({
message: "操作成功", message: "操作成功",
type: "success", type: "success",
@ -216,7 +170,6 @@ export default {
} }
}); });
} else { } else {
//
this.$message.error(data.msg); this.$message.error(data.msg);
} }
}); });
@ -224,11 +177,9 @@ export default {
}); });
}, },
addLink() { addLink() {
//
this.dataForm.replyContent += '<a href="链接地址">链接文字</a>' this.dataForm.replyContent += '<a href="链接地址">链接文字</a>'
}, },
onReplyTypeChange(value) { onReplyTypeChange(value) {
// JSON便使
if ("miniprogrampage" == value) { if ("miniprogrampage" == value) {
let demo = { title: "标题", appid: "小程序APPID", pagepath: "页面地址", thumb_media_id: "缩略图media_id" }; let demo = { title: "标题", appid: "小程序APPID", pagepath: "页面地址", thumb_media_id: "缩略图media_id" };
this.dataForm.replyContent = JSON.stringify(demo, null, 4) this.dataForm.replyContent = JSON.stringify(demo, null, 4)
@ -239,22 +190,21 @@ export default {
let demo = { head_content: "开头文字", list: [{ id: "菜单1ID", content: "菜单2内容" }, { id: "菜单2ID", content: "菜单2内容" }, { id: "菜单nID", content: "菜单n内容" }], tail_content: "结尾文字" } let demo = { head_content: "开头文字", list: [{ id: "菜单1ID", content: "菜单2内容" }, { id: "菜单2ID", content: "菜单2内容" }, { id: "菜单nID", content: "菜单n内容" }], tail_content: "结尾文字" }
this.dataForm.replyContent = JSON.stringify(demo, null, 4) this.dataForm.replyContent = JSON.stringify(demo, null, 4)
} else if ("news" == value) { } else if ("news" == value) {
let demo = { title: "文章标题", description: "文章简介", url: "链接URL", picUrl: "缩略图URL" } let demo={title:"文章标题",description:"文章简介",url:"链接URL",picUrl:"缩略图URL"}
this.dataForm.replyContent = JSON.stringify(demo, null, 4) this.dataForm.replyContent = JSON.stringify(demo, null, 4)
} else { } else {
this.dataForm.replyContent = '媒体素材media_id' this.dataForm.replyContent = '媒体素材media_id'
} }
}, },
onAssetsSelect(assetsInfo) { onAssetsSelect(assetsInfo){
// media_idmediaId if(this.dataForm.replyType=='miniprogrampage' || this.dataForm.replyType=='music'){
if (this.dataForm.replyType =='miniprogrampage' || this.dataForm.replyType =='music') { let data = JSON.parse(this.dataForm.replyContent)
let data = JSON.parse(this.dataForm.replyContent); if(data && data.thumb_media_id)data.thumb_media_id=assetsInfo.mediaId
if (data && data.thumb_media_id) data.thumb_media_id = assetsInfo.mediaId; this.dataForm.replyContent = JSON.stringify(data, null, 4)
this.dataForm.replyContent = JSON.stringify(data, null, 4); }else{
} else { this.dataForm.replyContent = assetsInfo.mediaId
this.dataForm.replyContent = assetsInfo.mediaId;
} }
this.assetsSelectorVisible = false; this.assetsSelectorVisible=false
} }
} }
}; };

@ -1,72 +1,50 @@
<template> <template>
<!-- 整个模块的容器类用于包裹后续的表单表格分页等组件 -->
<div class="mod-config"> <div class="mod-config">
<!-- 内联表单绑定了dataForm数据模型监听回车键native修饰符表示监听原生的键盘事件按下时调用getDataList方法 -->
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()"> <el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<!-- 匹配关键词输入框所在的表单项 -->
<el-form-item> <el-form-item>
<!-- 使用el-input组件实现输入框双向绑定dataForm.matchValue有占位提示文字且可清空输入内容 -->
<el-input v-model="dataForm.matchValue" placeholder="匹配关键词" clearable></el-input> <el-input v-model="dataForm.matchValue" placeholder="匹配关键词" clearable></el-input>
</el-form-item> </el-form-item>
<!-- 操作按钮所在的表单项 -->
<el-form-item> <el-form-item>
<!-- 查询按钮点击时调用getDataList方法获取符合条件的数据列表 -->
<el-button @click="getDataList()"></el-button> <el-button @click="getDataList()"></el-button>
<!-- 新增按钮根据权限调用isAuth方法判断决定是否显示类型为主要按钮点击时调用addOrUpdateHandle方法 -->
<el-button v-if="isAuth('wx:msgreplyrule:save')" type="primary" @click="addOrUpdateHandle()"></el-button> <el-button v-if="isAuth('wx:msgreplyrule:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
<!-- 批量删除按钮根据权限决定是否显示类型为危险按钮点击时调用deleteHandle方法当没有选中的数据项时禁用按钮 -->
<el-button v-if="isAuth('wx:msgreplyrule:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button> <el-button v-if="isAuth('wx:msgreplyrule:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- el-table组件用于展示数据列表绑定了dataList数据显示边框可展开行type="expand"加载数据时显示加载提示监听选择项变化事件 -->
<el-table :data="dataList" border type="expand" v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;"> <el-table :data="dataList" border type="expand" v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;">
<!-- 展开列通过插槽定义展开内容这里展示了更多规则相关详细信息的表单 -->
<el-table-column type="expand"> <el-table-column type="expand">
<template slot-scope="props"> <template slot-scope="props">
<el-form label-position="left" inline class="demo-table-expand"> <el-form label-position="left" inline class="demo-table-expand">
<!-- 作用范围信息展示的表单项根据数据项中的appid值判断显示当前公众号全部公众号 -->
<el-form-item label="作用范围"> <el-form-item label="作用范围">
<span>{{ props.row.appid? '当前公众号' : '全部公众号' }}</span> <span>{{ props.row.appid?'当前公众号':'全部公众号' }}</span>
</el-form-item> </el-form-item>
<!-- 精确匹配信息展示的表单项根据数据项中的exactMatch值判断显示 -->
<el-form-item label="精确匹配"> <el-form-item label="精确匹配">
<span>{{ props.row.exactMatch? '是' : '否' }}</span> <span>{{ props.row.exactMatch?'是':'否' }}</span>
</el-form-item> </el-form-item>
<!-- 是否有效信息展示的表单项根据数据项中的status值判断显示 -->
<el-form-item label="是否有效"> <el-form-item label="是否有效">
<span>{{ props.row.status? '是' : '否' }}</span> <span>{{ props.row.status?'是':'否' }}</span>
</el-form-item> </el-form-item>
<!-- 备注说明信息展示的表单项直接显示数据项中的desc值 -->
<el-form-item label="备注说明"> <el-form-item label="备注说明">
<span>{{ props.row.desc }}</span> <span>{{ props.row.desc }}</span>
</el-form-item> </el-form-item>
<!-- 生效时间信息展示的表单项直接显示数据项中的effectTimeStart值 -->
<el-form-item label="生效时间"> <el-form-item label="生效时间">
<span>{{ props.row.effectTimeStart }}</span> <span>{{ props.row.effectTimeStart }}</span>
</el-form-item> </el-form-item>
<!-- 失效时间信息展示的表单项直接显示数据项中的effectTimeEnd值 -->
<el-form-item label="失效时间"> <el-form-item label="失效时间">
<span>{{ props.row.effectTimeEnd }}</span> <span>{{ props.row.effectTimeEnd }}</span>
</el-form-item> </el-form-item>
</el-form> </el-form>
</template> </template>
</el-table-column> </el-table-column>
<!-- 选择列用于多选操作设置了表头和内容的对齐方式以及宽度 -->
<el-table-column type="selection" header-align="center" align="center" width="50"> <el-table-column type="selection" header-align="center" align="center" width="50">
</el-table-column> </el-table-column>
<!-- 规则名称列对应dataList中数据项的ruleName属性显示溢出提示设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="ruleName" header-align="center" align="center" show-overflow-tooltip label="规则名称"> <el-table-column prop="ruleName" header-align="center" align="center" show-overflow-tooltip label="规则名称">
</el-table-column> </el-table-column>
<!-- 匹配关键词列对应dataList中数据项的matchValue属性显示溢出提示设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="matchValue" header-align="center" align="center" show-overflow-tooltip label="匹配关键词"> <el-table-column prop="matchValue" header-align="center" align="center" show-overflow-tooltip label="匹配关键词">
</el-table-column> </el-table-column>
<!-- 消息类型列对应dataList中数据项的replyType属性使用formatter函数格式化显示内容设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="replyType" header-align="center" align="center" :formatter="replyTypeFormat" label="消息类型"> <el-table-column prop="replyType" header-align="center" align="center" :formatter="replyTypeFormat" label="消息类型">
</el-table-column> </el-table-column>
<!-- 回复内容列对应dataList中数据项的replyContent属性显示溢出提示设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="replyContent" header-align="center" align="center" show-overflow-tooltip label="回复内容"> <el-table-column prop="replyContent" header-align="center" align="center" show-overflow-tooltip label="回复内容">
</el-table-column> </el-table-column>
<!-- 操作列固定在右侧设置了表头和内容的对齐方式宽度以及列标题通过插槽定义了修改和删除按钮 -->
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作"> <el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.ruleId)"></el-button> <el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.ruleId)"></el-button>
@ -74,18 +52,15 @@
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页组件绑定了相关的分页事件和属性用于切换每页显示数量当前页码等操作 -->
<el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper"> <el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
</el-pagination> </el-pagination>
<!-- 新增/修改组件通过属性和事件与父组件进行交互根据addOrUpdateVisible的值决定是否显示 --> <!-- 弹窗, 新增 / 修改 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update> <add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div> </div>
</template> </template>
<script> <script>
// /
import AddOrUpdate from './msg-reply-rule-add-or-update' import AddOrUpdate from './msg-reply-rule-add-or-update'
// VuexmapStateVuex
import { mapState } from 'vuex' import { mapState } from 'vuex'
export default { export default {
components: { components: {
@ -106,20 +81,16 @@ export default {
} }
}, },
computed: mapState({ computed: mapState({
// VuexstatemessageKefuMsgType KefuMsgType: state=>state.message.KefuMsgType
KefuMsgType: state => state.message.KefuMsgType
}), }),
activated() { activated() {
// getDataList
this.getDataList() this.getDataList()
}, },
methods: { methods: {
// //
getDataList() { getDataList() {
// true
this.dataListLoading = true this.dataListLoading = true
// HTTPURL
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/msgReplyRule/list'), url: this.$http.adornUrl('/manage/msgReplyRule/list'),
method: 'get', method: 'get',
@ -129,60 +100,52 @@ export default {
'matchValue': this.dataForm.matchValue 'matchValue': this.dataForm.matchValue
}) })
}).then(({ data }) => { }).then(({ data }) => {
//
if (data && data.code === 200) { if (data && data.code === 200) {
//
this.dataList = data.page.list this.dataList = data.page.list
this.totalCount = data.page.totalCount this.totalCount = data.page.totalCount
} else { } else {
//
this.dataList = [] this.dataList = []
this.totalCount = 0 this.totalCount = 0
} }
// false
this.dataListLoading = false this.dataListLoading = false
}) })
}, },
// 1 //
sizeChangeHandle(val) { sizeChangeHandle(val) {
this.pageSize = val this.pageSize = val
this.pageIndex = 1 this.pageIndex = 1
this.getDataList() this.getDataList()
}, },
// //
currentChangeHandle(val) { currentChangeHandle(val) {
this.pageIndex = val this.pageIndex = val
this.getDataList() this.getDataList()
}, },
// //
selectionChangeHandle(val) { selectionChangeHandle(val) {
this.dataListSelections = val this.dataListSelections = val
}, },
// //trueDOM/id // /
addOrUpdateHandle(id) { addOrUpdateHandle(id) {
this.addOrUpdateVisible = true this.addOrUpdateVisible = true
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.addOrUpdate.init(id) this.$refs.addOrUpdate.init(id)
}) })
}, },
// idHTTP //
deleteHandle(id) { deleteHandle(id) {
// id使ruleId var ids = id ? [id] : this.dataListSelections.map(item => item.ruleId)
var ids = id? [id] : this.dataListSelections.map(item => item.ruleId) this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
// HTTP
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/msgReplyRule/delete'), url: this.$http.adornUrl('/manage/msgReplyRule/delete'),
method: 'post', method: 'post',
data: this.$http.adornData(ids, false) data: this.$http.adornData(ids, false)
}).then(({ data }) => { }).then(({ data }) => {
//
if (data && data.code === 200) { if (data && data.code === 200) {
//
this.$message({ this.$message({
message: '操作成功', message: '操作成功',
type: 'success', type: 'success',
@ -190,16 +153,14 @@ export default {
onClose: () => this.getDataList() onClose: () => this.getDataList()
}) })
} else { } else {
//
this.$message.error(data.msg) this.$message.error(data.msg)
} }
}) })
}) })
}, },
// KefuMsgType
replyTypeFormat(row, column, cellValue) { replyTypeFormat(row, column, cellValue) {
return this.KefuMsgType[cellValue]; return this.KefuMsgType[cellValue];
} }
} }
} }
</script> </script>

@ -1,195 +1,165 @@
<template> <template>
<!-- el-dialog组件用于创建一个对话框设置了标题点击模态框不关闭双向绑定显示隐藏状态等属性 --> <el-dialog title="模板配置" :close-on-click-modal="false" :visible.sync="visible">
<el-dialog title="模板配置" :close-on-click-modal="false" :visible.sync="visible"> <el-form :model="dataForm" :rules="dataRule" ref="dataForm" label-width="100px" size="mini">
<!-- el-form组件用于创建表单绑定了表单数据模型验证规则设置了标签宽度尺寸等属性 --> <el-form-item label="标题" prop="title">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" label-width="100px" size="mini"> <el-input v-model="dataForm.title" placeholder="标题"></el-input>
<!-- 表单项目用于输入标题绑定了dataForm中的title字段设置了提示文字 --> </el-form-item>
<el-form-item label="标题" prop="title"> <el-form-item label="链接" prop="url">
<el-input v-model="dataForm.title" placeholder="标题"></el-input> <el-input v-model="dataForm.url" placeholder="跳转链接"></el-input>
</el-form-item> </el-form-item>
<!-- 表单项目用于输入链接绑定了dataForm中的url字段设置了提示文字 --> <div>
<el-form-item label="链接" prop="url"> <el-form-item label="小程序appid" prop="miniprogram.appid">
<el-input v-model="dataForm.url" placeholder="跳转链接"></el-input> <el-input v-model="dataForm.miniprogram.appid" placeholder="小程序appid"></el-input>
</el-form-item> </el-form-item>
<div> <el-form-item label="小程序路径" prop="miniprogram.pagePath">
<!-- 表单项目用于输入小程序appid绑定了dataForm.miniprogram.appid字段设置了提示文字 --> <el-input v-model="dataForm.miniprogram.pagePath" placeholder="小程序pagePath"></el-input>
<el-form-item label="小程序appid" prop="miniprogram.appid"> </el-form-item>
<el-input v-model="dataForm.miniprogram.appid" placeholder="小程序appid"></el-input> </div>
</el-form-item> <el-row>
<!-- 表单项目用于输入小程序路径绑定了dataForm.miniprogram.pagePath字段设置了提示文字 --> <el-col :span="16">
<el-form-item label="小程序路径" prop="miniprogram.pagePath"> <el-form-item label="模版名称" prop="name">
<el-input v-model="dataForm.miniprogram.pagePath" placeholder="小程序pagePath"></el-input> <el-input v-model="dataForm.name" placeholder="模版名称"></el-input>
</el-form-item> </el-form-item>
</div> </el-col>
<el-row> <el-col :span="8">
<el-col :span="16"> <el-form-item label="有效" prop="status">
<!-- 表单项目用于输入模版名称绑定了dataForm中的name字段设置了提示文字 --> <el-switch v-model="dataForm.status" placeholder="是否有效" :active-value="true" :inactive-value="false"></el-switch>
<el-form-item label="模版名称" prop="name"> </el-form-item>
<el-input v-model="dataForm.name" placeholder="模版名称"></el-input> </el-col>
</el-form-item> </el-row>
</el-col> <div class="form-group-area">
<el-col :span="8"> <el-form-item class="form-group-title">消息填充数据请对照模板内容填写</el-form-item>
<!-- 表单项目使用el-switch组件用于切换是否有效状态绑定了dataForm中的status字段 --> <el-form-item>
<el-form-item label="有效" prop="status"> <el-input type="textarea" disabled autosize v-model="dataForm.content" placeholder="模版"></el-input>
<el-switch v-model="dataForm.status" placeholder="是否有效" :active-value="true" :inactive-value="false"></el-switch> </el-form-item>
</el-form-item> <el-row v-for="(item,index) in dataForm.data" :key="item.name">
</el-col> <el-col :span="16">
</el-row> <el-form-item :label="item.name" :prop="'data.'+index+'.value'" :rules="[{required: true,message: '填充内容不得为空', trigger: 'blur' }]">
<div class="form-group-area"> <el-input type="textarea" autosize rows="1" v-model="item.value" placeholder="填充内容" ></el-input>
<!-- 表单项目用于显示提示信息告知消息填充数据的相关要求 --> </el-form-item>
<el-form-item class="form-group-title">消息填充数据请对照模板内容填写</el-form-item> </el-col>
<!-- 表单项目使用textarea类型的el-input展示模板内容设置为禁用状态 --> <el-col :span="8">
<el-form-item> <el-form-item label="颜色" >
<el-input type="textarea" disabled autosize v-model="dataForm.content" placeholder="模版"></el-input> <el-input type="color" v-model="item.color" placeholder="颜色"></el-input>
</el-form-item> </el-form-item>
<!-- 使用v-for循环遍历dataForm.data数组动态生成表单项目用于输入消息填充数据及设置颜色 --> </el-col>
<el-row v-for="(item, index) in dataForm.data" :key="item.name"> </el-row>
<el-col :span="16"> </div>
<!-- 表单项目根据循环的索引和字段名绑定对应的数据项的value字段设置了必填验证规则 --> </el-form>
<el-form-item :label="item.name" :prop="'data.' + index + '.value'" :rules="[{required: true, message: '填充内容不得为空', trigger: 'blur' }]"> <span slot="footer" class="dialog-footer">
<el-input type="textarea" autosize rows="1" v-model="item.value" placeholder="填充内容"></el-input> <el-button @click="visible = false">取消</el-button>
</el-form-item> <el-button type="primary" @click="dataFormSubmit()"></el-button>
</el-col> </span>
<el-col :span="8"> </el-dialog>
<!-- 表单项目用于输入颜色值绑定对应数据项的color字段 --> </template>
<el-form-item label="颜色">
<el-input type="color" v-model="item.color" placeholder="颜色"></el-input>
</el-form-item>
</el-col>
</el-row>
</div>
</el-form>
<!-- 在对话框的底部插槽中添加取消和确定按钮分别绑定对应的点击事件 -->
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()"></el-button>
</span>
</el-dialog>
</template>
<script> <script>
export default { export default {
data() { data() {
return { return {
visible: false, // visible: false,
// idtemplateId dataForm: {
dataForm: { id: 0,
id: 0, templateId: '',
templateId: '', title: '',
title: '', data: [],
data: [], // url: '',
url: '', miniprogram:{appid:'',pagePath:''},
miniprogram: { appid: '', pagePath: '' }, // content: '',
content: '', // status: true,
status: true, // name: ''
name: '' // },
}, dataRule: {
// title: [
dataRule: { { required: true, message: '标题不能为空', trigger: 'blur' }
title: [ ],
{ required: true, message: '标题不能为空', trigger: 'blur' } data: [
], { required: true, message: '内容不能为空', trigger: 'blur' }
data: [ ],
{ required: true, message: '内容不能为空', trigger: 'blur' } name: [
], { required: true, message: '模版名称不能为空', trigger: 'blur' }
name: [ ]
{ required: true, message: '模版名称不能为空', trigger: 'blur' } }
] }
} },
} methods: {
}, init(id) {
methods: { console.log('init',id)
// id this.dataForm.id = id || 0
init(id) { this.visible = true
console.log('init', id); this.$nextTick(() => {
// idid0 this.$refs['dataForm'].resetFields()
this.dataForm.id = id || 0; if (this.dataForm.id) {
// this.$http({
this.visible = true; url: this.$http.adornUrl(`/manage/msgTemplate/info/${this.dataForm.id}`),
this.$nextTick(() => { method: 'get',
// params: this.$http.adornParams()
this.$refs['dataForm'].resetFields(); }).then(({ data }) => {
if (this.dataForm.id) { if (data && data.code === 200) {
// id使axios$httpaxiosGET this.transformTemplate(data.msgTemplate)
this.$http({ }else{
url: this.$http.adornUrl(`/manage/msgTemplate/info/${this.dataForm.id}`), this.$message.error(data.msg)
method: 'get', }
params: this.$http.adornParams() })
}).then(({ data }) => { }
if (data && data.code === 200) { })
// },
this.transformTemplate(data.msgTemplate); /**
} else { * 根据content信息展开data配置项(content为微信公众平台后台配置的模板)
// * 如content='{{first.DATA}} ↵商品名称:{{keyword1.DATA}} ↵购买时间:{{keyword2.DATA}} ↵{{remark.DATA}}'
this.$message.error(data.msg); * 则生成data=[{name:'first',value:'',color:''},{name:'first',value:'',color:''},{name:'first',value:'',color:''}]
} * 展示表单让管理员给对应的字段填充内容
}); */
} transformTemplate(template){
}); if(!template.miniprogram)template.miniprogram={appid:'',pagePath:''}
}, if(template.data instanceof Array) {//
/** this.dataForm = template
* 根据content信息展开data配置项(content为微信公众平台后台配置的模板) return
* 如content='{{first.DATA}} ↵商品名称:{{keyword1.DATA}} ↵购买时间:{{keyword2.DATA}} ↵{{remark.DATA}}' }
* 则生成data=[{name:'first',value:'',color:''},{name:'first',value:'',color:''},{name:'first',value:'',color:''}]
* 展示表单让管理员给对应的字段填充内容
*/
transformTemplate(template) {
if (!template.miniprogram) template.miniprogram = { appid: '', pagePath: '' };
if (template.data instanceof Array) { //
this.dataForm = template;
return;
}
template.data = []; template.data=[]
// content{{xxx.DATA}}xxxkeysArray let keysArray = template.content.match(/\{\{(\w*)\.DATA\}\}/g) || [] // ["{{first.DATA}}", "{{keyword1.DATA}}", "{{keyword2.DATA}}", "{{remark.DATA}}"]
let keysArray = template.content.match(/\{\{(\w*)\.DATA\}\}/g) || []; // ["{{first.DATA}}", "{{keyword1.DATA}}", "{{keyword2.DATA}}", "{{remark.DATA}}"] keysArray.map(item=>{
keysArray.map(item => { name=item.replace('{{','').replace('.DATA}}','')
const name = item.replace('{{', '').replace('.DATA}}', ''); template.data.push({"name":name,"value":"",color:"#000000"})
// datavalue })
template.data.push({ "name": name, "value": "", color: "#000000" }); this.dataForm = template
}); },
this.dataForm = template; // dataForm //
}, dataFormSubmit() {
// this.$refs['dataForm'].validate((valid) => {
dataFormSubmit() { if (valid) {
// this.$http({
this.$refs['dataForm'].validate((valid) => { url: this.$http.adornUrl(`/manage/msgTemplate/${!this.dataForm.id ? 'save' : 'update'}`),
if (valid) { method: 'post',
// 使axios$httpaxiosPOSTid data: this.$http.adornData(this.dataForm)
this.$http({ }).then(({ data }) => {
url: this.$http.adornUrl(`/manage/msgTemplate/${!this.dataForm.id ? 'save' : 'update'}`), if (data && data.code === 200) {
method: 'post', this.$message({
data: this.$http.adornData(this.dataForm) // message: '操作成功',
}).then(({ data }) => { type: 'success',
if (data && data.code === 200) { duration: 1500,
// refreshDataList onClose: () => {
this.$message({ this.visible = false
message: '操作成功', this.$emit('refreshDataList')
type: 'success', }
duration: 1500, })
onClose: () => { } else {
this.visible = false; // this.$message.error(data.msg)
this.$emit('refreshDataList'); // }
} })
}); }
} else { })
// }
this.$message.error(data.msg); }
} }
}); </script>
} <style scoped>
}); .form-group-area{
} border:1px dotted gray;
} }
} .form-group-title{
</script> color: gray;
font-size: 12px;
<style scoped> }
.form-group-area {
border: 1px dotted gray; /* 添加边框样式,使其更明显 */
}
.form-group-title {
color: gray; /* 设置标题颜色 */
font-size: 12px; /* 设置标题字体大小 */
}
</style> </style>

@ -1,55 +1,36 @@
<template> <template>
<!-- 整个模块的容器类用于包裹后续的表单表格分页以及相关弹窗组件等 -->
<div class="mod-config"> <div class="mod-config">
<!-- 内联表单绑定了dataForm数据模型监听回车键native修饰符表示监听原生的键盘事件按下时调用getDataList方法 -->
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()"> <el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<!-- 标题输入框所在的表单项使用el-input组件实现输入框双向绑定dataForm.title有占位提示文字且可清空输入内容 -->
<el-form-item> <el-form-item>
<el-input v-model="dataForm.title" placeholder="标题" clearable></el-input> <el-input v-model="dataForm.title" placeholder="标题" clearable></el-input>
</el-form-item> </el-form-item>
<!-- 操作按钮所在的表单项 -->
<el-form-item> <el-form-item>
<!-- 查询按钮点击时调用getDataList方法获取符合条件的数据列表 -->
<el-button @click="getDataList()"></el-button> <el-button @click="getDataList()"></el-button>
<!-- 批量复制按钮根据权限调用isAuth方法判断决定是否显示类型为成功按钮点击时调用copyHandle方法当没有选中的数据项时禁用按钮 -->
<el-button v-if="isAuth('wx:msgtemplate:save')" type="success" @click="copyHandle()" :disabled="dataListSelections.length <= 0"></el-button> <el-button v-if="isAuth('wx:msgtemplate:save')" type="success" @click="copyHandle()" :disabled="dataListSelections.length <= 0"></el-button>
<!-- 推送消息按钮根据权限决定是否显示类型为成功按钮点击时调用templateMsgTaskHandle方法当选中的数据项数量不为1时禁用按钮 --> <el-button v-if="isAuth('wx:msgtemplate:save')" type="success" @click="templateMsgTaskHandle()" :disabled="dataListSelections.length!=1"></el-button>
<el-button v-if="isAuth('wx:msgtemplate:save')" type="success" @click="templateMsgTaskHandle()" :disabled="dataListSelections.length!= 1"></el-button>
<!-- 批量删除按钮根据权限决定是否显示类型为危险按钮点击时调用deleteHandle方法当没有选中的数据项时禁用按钮 -->
<el-button v-if="isAuth('wx:msgtemplate:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button> <el-button v-if="isAuth('wx:msgtemplate:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button>
</el-form-item> </el-form-item>
<!-- 靠右对齐的表单项包含同步公众号模板按钮和模板管理指引链接按钮 -->
<el-form-item class="fr"> <el-form-item class="fr">
<!-- 同步公众号模板按钮根据权限决定是否显示点击时调用syncWxTemplate方法按钮文字根据同步状态动态变化正在同步时显示同步中...否则显示同步公众号模板同步过程中按钮禁用 --> <el-button v-if="isAuth('wx:msgtemplate:save')" icon="el-icon-sort" type="success" @click="syncWxTemplate()" :disabled="synchonizingWxTemplate">{{synchonizingWxTemplate?'...':''}}</el-button>
<el-button v-if="isAuth('wx:msgtemplate:save')" icon="el-icon-sort" type="success" @click="syncWxTemplate()" :disabled="synchonizingWxTemplate">{{synchonizingWxTemplate? '...' : ''}}</el-button>
<!-- 模板管理指引链接按钮点击后在新标签页打开指定的链接 -->
<el-button><el-link type="primary" icon="el-icon-link" target="_blank" href="https://kf.qq.com/faq/170209E3InyI170209nIF7RJ.html">模板管理指引</el-link></el-button> <el-button><el-link type="primary" icon="el-icon-link" target="_blank" href="https://kf.qq.com/faq/170209E3InyI170209nIF7RJ.html">模板管理指引</el-link></el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- el-table组件用于展示数据列表绑定了dataList数据显示边框加载数据时显示加载提示监听选择项变化事件 -->
<el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;"> <el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;">
<!-- 选择列用于多选操作设置了表头和内容的对齐方式以及宽度 -->
<el-table-column type="selection" header-align="center" align="center" width="50"> <el-table-column type="selection" header-align="center" align="center" width="50">
</el-table-column> </el-table-column>
<!-- 模板ID列对应dataList中数据项的templateId属性显示溢出提示设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="templateId" show-overflow-tooltip header-align="center" align="center" label="模板ID"> <el-table-column prop="templateId" show-overflow-tooltip header-align="center" align="center" label="模板ID">
</el-table-column> </el-table-column>
<!-- 标题列对应dataList中数据项的title属性通过插槽使用a标签包裹内容使其可点击跳转链接地址取自数据项的url属性设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="title" header-align="center" align="center" label="标题"> <el-table-column prop="title" header-align="center" align="center" label="标题">
<a :href="scope.row.url" slot-scope="scope">{{scope.row.title}}</a> <a :href="scope.row.url" slot-scope="scope">{{scope.row.title}}</a>
</el-table-column> </el-table-column>
<!-- 模版名称列对应dataList中数据项的name属性设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="name" header-align="center" align="center" label="模版名称"> <el-table-column prop="name" header-align="center" align="center" label="模版名称">
</el-table-column> </el-table-column>
<!-- 模版字段列对应dataList中数据项的content属性显示溢出提示设置了表头和内容的对齐方式以及列宽度 -->
<el-table-column prop="content" show-overflow-tooltip header-align="center" align="center" label="模版字段" width="200"> <el-table-column prop="content" show-overflow-tooltip header-align="center" align="center" label="模版字段" width="200">
</el-table-column> </el-table-column>
<!-- 是否有效列对应dataList中数据项的status属性通过插槽根据数据项的status值判断显示设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="status" header-align="center" align="center" label="是否有效"> <el-table-column prop="status" header-align="center" align="center" label="是否有效">
<span slot-scope="scope">{{scope.row.status? "是" : "否"}}</span> <span slot-scope="scope">{{scope.row.status?"是":"否"}}</span>
</el-table-column> </el-table-column>
<!-- 操作列固定在右侧设置了表头和内容的对齐方式宽度以及列标题通过插槽定义了配置和删除按钮 -->
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作"> <el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.id)"></el-button> <el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.id)"></el-button>
@ -57,22 +38,16 @@
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页组件绑定了相关的分页事件和属性用于切换每页显示数量当前页码等操作 -->
<el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper"> <el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
</el-pagination> </el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<!-- 新增/修改组件通过属性和事件与父组件进行交互根据addOrUpdateVisible的值决定是否显示 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update> <add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
<!-- 模板消息任务组件根据templateMsgTaskVisible的值决定是否显示 -->
<template-msg-task v-if="templateMsgTaskVisible" ref="templateMsgTask"></template-msg-task> <template-msg-task v-if="templateMsgTaskVisible" ref="templateMsgTask"></template-msg-task>
</div> </div>
</template> </template>
<script> <script>
// /
import AddOrUpdate from './msg-template-add-or-update' import AddOrUpdate from './msg-template-add-or-update'
//
import TemplateMsgTask from '@/components/template-msg-task' import TemplateMsgTask from '@/components/template-msg-task'
export default { export default {
data() { data() {
@ -87,23 +62,20 @@ export default {
dataListLoading: false, dataListLoading: false,
dataListSelections: [], dataListSelections: [],
addOrUpdateVisible: false, addOrUpdateVisible: false,
templateMsgTaskVisible: false, templateMsgTaskVisible:false,
synchonizingWxTemplate: false synchonizingWxTemplate:false
} }
}, },
components: { components: {
AddOrUpdate, TemplateMsgTask AddOrUpdate,TemplateMsgTask
}, },
activated() { activated() {
// getDataList
this.getDataList() this.getDataList()
}, },
methods: { methods: {
// //
getDataList() { getDataList() {
// true this.dataListLoading = true
this.dataListLoading = true;
// HTTPURL
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/msgTemplate/list'), url: this.$http.adornUrl('/manage/msgTemplate/list'),
method: 'get', method: 'get',
@ -115,159 +87,128 @@ export default {
'order': 'desc' 'order': 'desc'
}) })
}).then(({ data }) => { }).then(({ data }) => {
//
if (data && data.code === 200) { if (data && data.code === 200) {
// this.dataList = data.page.list
this.dataList = data.page.list; this.totalCount = data.page.totalCount
this.totalCount = data.page.totalCount;
} else { } else {
// this.dataList = []
this.dataList = []; this.totalCount = 0
this.totalCount = 0;
} }
// false this.dataListLoading = false
this.dataListLoading = false; })
});
}, },
// 1 //
sizeChangeHandle(val) { sizeChangeHandle(val) {
this.pageSize = val; this.pageSize = val
this.pageIndex = 1; this.pageIndex = 1
this.getDataList(); this.getDataList()
}, },
// //
currentChangeHandle(val) { currentChangeHandle(val) {
this.pageIndex = val; this.pageIndex = val
this.getDataList(); this.getDataList()
}, },
// //
selectionChangeHandle(val) { selectionChangeHandle(val) {
this.dataListSelections = val; this.dataListSelections = val
}, },
// //trueDOM/id // /
addOrUpdateHandle(id) { addOrUpdateHandle(id) {
this.addOrUpdateVisible = true; this.addOrUpdateVisible = true
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.addOrUpdate.init(id); this.$refs.addOrUpdate.init(id)
}); })
}, },
// idHTTP //
deleteHandle(id) { deleteHandle(id) {
// id使id var ids = id ? [id] : this.dataListSelections.map(item => item.id)
var ids = id? [id] : this.dataListSelections.map(item => item.id); this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
// HTTP
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/msgTemplate/delete'), url: this.$http.adornUrl('/manage/msgTemplate/delete'),
method: 'post', method: 'post',
data: this.$http.adornData(ids, false) data: this.$http.adornData(ids, false)
}).then(({ data }) => { }).then(({ data }) => {
//
if (data && data.code === 200) { if (data && data.code === 200) {
//
this.$message({ this.$message({
message: '操作成功', message: '操作成功',
type: 'success', type: 'success',
duration: 1500, duration: 1500,
onClose: () => { onClose: () => {
this.getDataList(); this.getDataList()
} }
}); })
} else { } else {
// this.$message.error(data.msg)
this.$message.error(data.msg);
} }
}); })
}); })
}, },
syncWxTemplate() { syncWxTemplate(){
// if(this.synchonizingWxTemplate)return
if (this.synchonizingWxTemplate) return; this.synchonizingWxTemplate=true
// true
this.synchonizingWxTemplate = true;
// HTTP
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/msgTemplate/syncWxTemplate'), url: this.$http.adornUrl('/manage/msgTemplate/syncWxTemplate'),
method: 'post' method: 'post',
}).then(({ data }) => { }).then(({ data }) => {
// false this.synchonizingWxTemplate=false
this.synchonizingWxTemplate = false; if (data && data.code === 200) {
// this.$message({
if (data && data.code === 200) { message: '同步完成',
// type: 'success',
this.$message({ duration: 1500,
message: '同步完成', onClose: () => {
type: 'success', this.getDataList()
duration: 1500, }
onClose: () => { })
this.getDataList(); } else {
} this.$message.error(data.msg)
}); }
} else { }).catch(()=>this.synchonizingWxTemplate=false)
//
this.$message.error(data.msg);
}
}).catch(() => this.synchonizingWxTemplate = false);
}, },
templateMsgTaskHandle() { templateMsgTaskHandle(){
// true this.templateMsgTaskVisible = true
this.templateMsgTaskVisible = true;
this.$nextTick(() => { this.$nextTick(() => {
// DOM this.$refs.templateMsgTask.init(this.dataListSelections[0])
this.$refs.templateMsgTask.init(this.dataListSelections[0]); })
});
}, },
async copyHandle() { async copyHandle(){
let loading; let loading;
//
for (let i = 0; i < this.dataListSelections.length; i++) { for (let i = 0; i < this.dataListSelections.length; i++) {
let item = this.dataListSelections[i]; let item = this.dataListSelections[i];
// loading=this.$loading({
loading = this.$loading({
lock: true, lock: true,
text: "复制模板:" + item.title, text: "复制模板:"+item.title,
spinner: 'el-icon-loading', spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)' background: 'rgba(0, 0, 0, 0.7)'
}); });
// id item.id='';
item.id = ''; item.updateTime=new Date()
// item.name+='_COPY'
item.updateTime = new Date(); await this.addMsgTemplate(item).catch(()=>loading.close())
// _COPY loading.close()
item.name += '_COPY';
// addMsgTemplate使await
await this.addMsgTemplate(item).catch(() => loading.close());
//
loading.close();
} }
// loading.close()
loading.close(); this.getDataList()
//
this.getDataList();
}, },
addMsgTemplate(msgTemplate) { addMsgTemplate(msgTemplate){
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// HTTP
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/msgTemplate/save'), url: this.$http.adornUrl('/manage/msgTemplate/save'),
method: 'post', method: 'post',
data: this.$http.adornData(msgTemplate) data: this.$http.adornData(msgTemplate)
}).then(({ data }) => { }).then(({ data }) => {
//
if (data && data.code === 200) { if (data && data.code === 200) {
// resolve resolve()
resolve();
} else { } else {
// reject this.$message.error(data.msg)
this.$message.error(data.msg); reject(data.msg)
reject(data.msg);
} }
}).catch(err => reject(err)); }).catch(err=>reject(err))
}); })
} }
} }
} }

@ -1,57 +1,38 @@
<template> <template>
<!-- 整个模块的容器类用于包裹后续的表单表格以及分页组件等 -->
<div class="mod-config"> <div class="mod-config">
<!-- 内联表单绑定了dataForm数据模型监听回车键native修饰符表示监听原生的键盘事件按下时调用getDataList方法 -->
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()"> <el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<!-- openid输入框所在的表单项使用el-input组件实现输入框双向绑定dataForm.touser有占位提示文字且可清空输入内容 -->
<el-form-item> <el-form-item>
<el-input v-model="dataForm.touser" placeholder="openid" clearable></el-input> <el-input v-model="dataForm.touser" placeholder="openid" clearable></el-input>
</el-form-item> </el-form-item>
<!-- 操作按钮所在的表单项 -->
<el-form-item> <el-form-item>
<!-- 查询按钮点击时调用getDataList方法获取符合条件的数据列表 -->
<el-button @click="getDataList()"></el-button> <el-button @click="getDataList()"></el-button>
<!-- 批量删除按钮根据权限调用isAuth方法判断决定是否显示类型为危险按钮点击时调用deleteHandle方法当没有选中的数据项时禁用按钮 -->
<el-button v-if="isAuth('wx:templatemsglog:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button> <el-button v-if="isAuth('wx:templatemsglog:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- el-table组件用于展示数据列表绑定了dataList数据显示边框加载数据时显示加载提示监听选择项变化事件 -->
<el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;"> <el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;">
<!-- 选择列用于多选操作设置了表头和内容的对齐方式以及宽度 -->
<el-table-column type="selection" header-align="center" align="center" width="50"> <el-table-column type="selection" header-align="center" align="center" width="50">
</el-table-column> </el-table-column>
<!-- openid列对应dataList中数据项的touser属性设置了表头和内容的对齐方式以及列宽度 -->
<el-table-column prop="touser" header-align="center" align="center" label="openid" width="100"> <el-table-column prop="touser" header-align="center" align="center" label="openid" width="100">
</el-table-column> </el-table-column>
<!-- 内容列对应dataList中数据项的data属性使用formatter函数tableJsonFormat方法格式化显示内容设置了表头和内容的对齐方式以及列宽度 -->
<el-table-column prop="data" header-align="center" align="center" :formatter="tableJsonFormat" label="内容" width="300"> <el-table-column prop="data" header-align="center" align="center" :formatter="tableJsonFormat" label="内容" width="300">
</el-table-column> </el-table-column>
<!-- 发送结果列对应dataList中数据项的sendResult属性显示溢出提示设置了表头和内容的对齐方式以及列标题和宽度 -->
<el-table-column prop="sendResult" header-align="center" align="center" show-overflow-tooltip label="发送结果" width="150"> <el-table-column prop="sendResult" header-align="center" align="center" show-overflow-tooltip label="发送结果" width="150">
</el-table-column> </el-table-column>
<!-- 发送时间列对应dataList中数据项的sendTime属性设置了表头和内容的对齐方式以及列标题和宽度 -->
<el-table-column prop="sendTime" header-align="center" align="center" width="100" label="发送时间"> <el-table-column prop="sendTime" header-align="center" align="center" width="100" label="发送时间">
</el-table-column> </el-table-column>
<!-- 链接列对应dataList中数据项的url属性显示溢出提示设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="url" header-align="center" align="center" show-overflow-tooltip label="链接"> <el-table-column prop="url" header-align="center" align="center" show-overflow-tooltip label="链接">
</el-table-column> </el-table-column>
<!-- 小程序列对应dataList中数据项的miniprogram属性使用formatter函数tableJsonFormat方法格式化显示内容显示溢出提示设置了表头和内容的对齐方式以及列标题 -->
<el-table-column prop="miniprogram" header-align="center" align="center" :formatter="tableJsonFormat" show-overflow-tooltip label="小程序"> <el-table-column prop="miniprogram" header-align="center" align="center" :formatter="tableJsonFormat" show-overflow-tooltip label="小程序">
</el-table-column> </el-table-column>
<!-- 模板ID列对应dataList中数据项的templateId属性设置了表头和内容的对齐方式以及列标题和宽度 -->
<el-table-column prop="templateId" header-align="center" align="center" label="模板ID" width="150"> <el-table-column prop="templateId" header-align="center" align="center" label="模板ID" width="150">
</el-table-column> </el-table-column>
<!-- 操作列固定在右侧设置了表头和内容的对齐方式宽度以及列标题通过插槽定义了删除按钮 -->
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作"> <el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" size="small" @click="deleteHandle(scope.row.logId)"></el-button> <el-button type="text" size="small" @click="deleteHandle(scope.row.logId)"></el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
<!-- 分页组件绑定了相关的分页事件和属性用于切换每页显示数量当前页码等操作 -->
<el-pagination @size-change="sizeChangeHandle" @current-change="current-changeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalCount" layout="total, sizes, prev, pager, next, jumper">
</el-pagination> </el-pagination>
</div> </div>
</template> </template>
@ -61,7 +42,6 @@ export default {
data() { data() {
return { return {
dataForm: { dataForm: {
// openid
touser: '' touser: ''
}, },
dataList: [], dataList: [],
@ -74,15 +54,12 @@ export default {
} }
}, },
activated() { activated() {
// getDataList
this.getDataList() this.getDataList()
}, },
methods: { methods: {
// //
getDataList() { getDataList() {
// true this.dataListLoading = true
this.dataListLoading = true;
// HTTPURL
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/templateMsgLog/list'), url: this.$http.adornUrl('/manage/templateMsgLog/list'),
method: 'get', method: 'get',
@ -94,76 +71,65 @@ export default {
'order': 'desc' 'order': 'desc'
}) })
}).then(({ data }) => { }).then(({ data }) => {
//
if (data && data.code === 200) { if (data && data.code === 200) {
// this.dataList = data.page.list
this.dataList = data.page.list; this.totalCount = data.page.totalCount
this.totalCount = data.page.totalCount;
} else { } else {
// this.dataList = []
this.dataList = []; this.totalCount = 0
this.totalCount = 0;
} }
// false this.dataListLoading = false
this.dataListLoading = false; })
});
}, },
// 1 //
sizeChangeHandle(val) { sizeChangeHandle(val) {
this.pageSize = val; this.pageSize = val
this.pageIndex = 1; this.pageIndex = 1
this.getDataList(); this.getDataList()
}, },
// //
currentChangeHandle(val) { currentChangeHandle(val) {
this.pageIndex = val; this.pageIndex = val
this.getDataList(); this.getDataList()
}, },
// //
selectionChangeHandle(val) { selectionChangeHandle(val) {
this.dataListSelections = val; this.dataListSelections = val
}, },
// idHTTP //
deleteHandle(id) { deleteHandle(id) {
// id使logId var ids = id ? [id] : this.dataListSelections.map(item => item.logId)
var ids = id? [id] : this.dataListSelections.map(item => item.logId); this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
// HTTP
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/templateMsgLog/delete'), url: this.$http.adornUrl('/manage/templateMsgLog/delete'),
method: 'post', method: 'post',
data: this.$http.adornData(ids, false) data: this.$http.adornData(ids, false)
}).then(({ data }) => { }).then(({ data }) => {
//
if (data && data.code === 200) { if (data && data.code === 200) {
//
this.$message({ this.$message({
message: '操作成功', message: '操作成功',
type: 'success', type: 'success',
duration: 1500, duration: 1500,
onClose: () => { onClose: () => {
this.getDataList(); this.getDataList()
} }
}); })
} else { } else {
// this.$message.error(data.msg)
this.$message.error(data.msg);
} }
}); })
}); })
}, },
tableJsonFormat(row, column, cellValue) { tableJsonFormat(row, column, cellValue){
//
if (!cellValue) { if (!cellValue) {
return ''; return '';
} }
// JSONJSON便 return JSON.stringify(cellValue)
return JSON.stringify(cellValue);
} }
} }
} }
</script> </script>

@ -1,42 +1,41 @@
<template> <template>
<!-- 整个模块的容器类用于包裹后续的表单表格以及相关弹窗组件等 -->
<div class="mod-config"> <div class="mod-config">
<!-- 内联表单绑定了dataForm数据模型监听回车键native修饰符表示监听原生的键盘事件按下时调用getDataList方法 --> <!-- 定义一个内联表单用于搜索和操作 -->
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()"> <el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<!-- 参数名输入框所在的表单项使用el-input组件实现输入框双向绑定dataForm.key有占位提示文字且可清空输入内容 -->
<el-form-item> <el-form-item>
<!-- 输入框用于输入搜索关键词 -->
<el-input v-model="dataForm.key" placeholder="参数名" clearable></el-input> <el-input v-model="dataForm.key" placeholder="参数名" clearable></el-input>
</el-form-item> </el-form-item>
<!-- 操作按钮所在的表单项 -->
<el-form-item> <el-form-item>
<!-- 查询按钮点击时调用getDataList方法获取符合条件的数据列表 --> <!-- 查询按钮触发数据列表的获取 -->
<el-button @click="getDataList()"></el-button> <el-button @click="getDataList()"></el-button>
<!-- 新增按钮根据权限调用isAuth方法判断决定是否显示类型为主要按钮点击时调用addOrUpdateHandle方法 --> <!-- 新增按钮根据权限显示 -->
<el-button v-if="isAuth('wx:wxaccount:save')" type="primary" @click="addOrUpdateHandle()"></el-button> <el-button v-if="isAuth('wx:wxaccount:save')" type="primary" @click="addOrUpdateHandle()"></el-button>
<!-- 批量删除按钮根据权限决定是否显示类型为危险按钮点击时调用deleteHandle方法当没有选中的数据项时禁用按钮 --> <!-- 批量删除按钮根据权限和选中项显示 -->
<el-button v-if="isAuth('wx:wxaccount:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0"></el-button> <el-button v-if="isAuth('wx:wxaccount:delete')" type="danger" @click="deleteHandle()"
:disabled="dataListSelections.length <= 0">批量删除</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 数据列表展示 -->
<!-- el-table组件用于展示数据列表绑定了dataList数据显示边框加载数据时显示加载提示监听选择项变化事件 --> <el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle"
<el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;"> style="width: 100%;">
<!-- 选择用于多选操作设置了表头和内容的对齐方式以及宽度 --> <!-- 选择-->
<el-table-column type="selection" header-align="center" align="center" width="50"> <el-table-column type="selection" header-align="center" align="center" width="50">
</el-table-column> </el-table-column>
<!-- appid列对应dataList中数据项的appid属性设置了表头和内容的对齐方式以及列标题 --> <!-- appid列 -->
<el-table-column prop="appid" header-align="center" align="center" label="appid"> <el-table-column prop="appid" header-align="center" align="center" label="appid">
</el-table-column> </el-table-column>
<!-- 公众号名称列对应dataList中数据项的name属性设置了表头和内容的对齐方式以及列标题 --> <!-- 公众号名称列 -->
<el-table-column prop="name" header-align="center" align="center" label="公众号名称"> <el-table-column prop="name" header-align="center" align="center" label="公众号名称">
</el-table-column> </el-table-column>
<!-- 类型列对应dataList中数据项的type属性使用formatter函数accountTypeFormat方法格式化显示内容设置了表头和内容的对齐方式以及列标题 --> <!-- 类型列使用formatter格式化显示 -->
<el-table-column prop="type" header-align="center" align="center" label="类型" :formatter="accountTypeFormat"> <el-table-column prop="type" header-align="center" align="center" label="类型" :formatter="accountTypeFormat">
</el-table-column> </el-table-column>
<!-- 是否认证列对应dataList中数据项的verified属性通过插槽根据数据项的verified值判断显示设置了表头和内容的对齐方式以及列标题 --> <!-- 是否认证列使用插槽自定义显示 -->
<el-table-column prop="verified" header-align="center" align="center" label="是否认证"> <el-table-column prop="verified" header-align="center" align="center" label="是否认证">
<span slot-scope="scope">{{scope.row.verified? "是" : "否"}}</span> <span slot-scope="scope">{{ scope.row.verified ? "是" : "否" }}</span>
</el-table-column> </el-table-column>
<!-- 操作列固定在右侧设置了表头和内容的对齐方式宽度以及列标题通过插槽定义了接入修改和删除按钮 --> <!-- 操作列包含接入修改和删除按钮 -->
<el-table-column fixed="right" header-align="center" align="center" width="150" label="操作"> <el-table-column fixed="right" header-align="center" align="center" width="150" label="操作">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" size="small" @click="accessInfo(scope.row)"></el-button> <el-button type="text" size="small" @click="accessInfo(scope.row)"></el-button>
@ -45,129 +44,102 @@
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 新增/修改弹窗组件 -->
<!-- 新增/修改组件通过属性和事件与父组件进行交互根据addOrUpdateVisible的值决定是否显示 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update> <add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
<!-- 账号接入信息组件根据accountAccessVisible的值决定是否显示 --> <!-- 接入信息弹窗组件 -->
<account-access v-if="accountAccessVisible" ref="accountAccessDialog"></account-access> <account-access v-if="accountAccessVisible" ref="accountAccessDialog"></account-access>
</div> </div>
</template> </template>
<script> <script>
// / import AddOrUpdate from './account/wx-account-add-or-update' // /
import AddOrUpdate from './account/wx-account-add-or-update' import AccountAccess from './account/wx-account-access-info' //
// import { mapState } from 'vuex' // VuexmapState
import AccountAccess from './account/wx-account-access-info'
// VuexmapStateVuex
import { mapState } from 'vuex'
export default { export default {
data() { data() {
return { return {
dataForm: { dataForm: { key: '' }, //
// dataList: [], //
key: '' dataListLoading: false, //
}, dataListSelections: [], //
dataList: [], addOrUpdateVisible: false, // /
dataListLoading: false, accountAccessVisible: false //
dataListSelections: [],
addOrUpdateVisible: false,
accountAccessVisible: false
} }
}, },
components: { components: { AddOrUpdate, AccountAccess }, //
AddOrUpdate, AccountAccess
},
computed: mapState({ computed: mapState({
// VuexstatewxAccountACCOUNT_TYPES ACCOUNT_TYPES: state => state.wxAccount.ACCOUNT_TYPES // Vuex
ACCOUNT_TYPES: state => state.wxAccount.ACCOUNT_TYPES
}), }),
activated() { activated() {
// getDataList this.getDataList() //
this.getDataList()
}, },
methods: { methods: {
// //
getDataList() { getDataList() {
// true this.dataListLoading = true
this.dataListLoading = true;
// HTTPURL
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/wxAccount/list'), url: this.$http.adornUrl('/manage/wxAccount/list'), //
method: 'get', method: 'get',
params: this.$http.adornParams({ params: this.$http.adornParams({ 'key': this.dataForm.key }) //
'key': this.dataForm.key
})
}).then(({ data }) => { }).then(({ data }) => {
//
if (data && data.code === 200) { if (data && data.code === 200) {
// Vuexmutation this.dataList = data.list //
this.dataList = data.list; this.$store.commit('wxAccount/updateAccountList', data.list) // Vuex
this.$store.commit('wxAccount/updateAccountList', data.list);
} else { } else {
// this.dataList = [] //
this.dataList = [];
} }
// false this.dataListLoading = false //
this.dataListLoading = false; })
});
}, },
// //
selectionChangeHandle(val) { selectionChangeHandle(val) {
this.dataListSelections = val; this.dataListSelections = val //
}, },
// //trueDOM/ // /
addOrUpdateHandle(item) { addOrUpdateHandle(item) {
this.addOrUpdateVisible = true; this.addOrUpdateVisible = true //
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.addOrUpdate.init(item); this.$refs.addOrUpdate.init(item) //
}); })
}, },
//
accessInfo(item) { accessInfo(item) {
// true this.accountAccessVisible = true //
this.accountAccessVisible = true;
this.$nextTick(() => { this.$nextTick(() => {
// DOM this.$refs.accountAccessDialog.init(item) //
this.$refs.accountAccessDialog.init(item); })
});
}, },
// appidHTTP //
deleteHandle(appid) { deleteHandle(appid) {
// appid使appid var ids = appid ? [appid] : this.dataListSelections.map(item => item.appid) // appid
var ids = appid? [appid] : this.dataListSelections.map(item => { this.$confirm(`确定对[appid=${ids.join(',')}]进行[${appid ? '删除' : '批量删除'}]操作?`, '提示', {
return item.appid
});
this.$confirm(`确定对[appid=${ids.join(',')}]进行[${appid? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
// HTTP
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/wxAccount/delete'), url: this.$http.adornUrl('/manage/wxAccount/delete'), //
method: 'post', method: 'post',
data: this.$http.adornData(ids, false) data: this.$http.adornData(ids, false) //
}).then(({ data }) => { }).then(({ data }) => {
//
if (data && data.code === 200) { if (data && data.code === 200) {
//
this.$message({ this.$message({
message: '操作成功', message: '操作成功',
type: 'success', type: 'success',
duration: 1500, duration: 1500,
onClose: () => { onClose: () => {
this.getDataList(); this.getDataList() //
} }
}); })
} else { } else {
// this.$message.error(data.msg) //
this.$message.error(data.msg);
} }
}); })
}); })
}, },
//
accountTypeFormat(row, column, cellValue) { accountTypeFormat(row, column, cellValue) {
// ACCOUNT_TYPES return this.ACCOUNT_TYPES[cellValue] //
return this.ACCOUNT_TYPES[cellValue];
} }
} }
} }

@ -1,24 +1,21 @@
<template> <template>
<!-- 使用el-tabs组件创建选项卡通过v-model双向绑定activeTab来控制当前选中的选项卡监听tab-click事件触发handleTabClick方法 --> <!-- 使用element-ui的el-tabs组件创建一个标签页切换组件 -->
<el-tabs v-model="activeTab" @tab-click="handleTabClick"> <el-tabs v-model="activeTab" @tab-click="handleTabClick">
<!-- 图片素材选项卡面板标签显示为图片素材数量数量通过assetsCount.imageCount动态获取设置name属性为image采用懒加载模式 --> <!-- 图片素材标签页使用el-tab-pane组件定义每个标签页的内容 -->
<el-tab-pane :label="'图片素材('+assetsCount.imageCount+')'" name="image" lazy> <el-tab-pane :label="'图片素材(' + assetsCount.imageCount + ')'" name="image" lazy>
<!-- 引入自定义组件material-file设置其fileType属性为image用于展示和管理图片素材相关功能同时监听其@change事件触发materialCount方法 --> <!-- 引入自定义的MaterialFile组件用于展示图片素材 -->
<material-file fileType="image" ref="imagePanel" @change="materialCount"></material-file> <material-file fileType="image" ref="imagePanel" @change="materialCount"></material-file>
</el-tab-pane> </el-tab-pane>
<!-- 语音素材选项卡面板标签显示为语音素材数量数量通过assetsCount.voiceCount动态获取设置name属性为voice采用懒加载模式 --> <!-- 语音素材标签页 -->
<el-tab-pane :label="'语音素材('+assetsCount.voiceCount+')'" name="voice" lazy> <el-tab-pane :label="'语音素材(' + assetsCount.voiceCount + ')'" name="voice" lazy>
<!-- 引入自定义组件material-file设置其fileType属性为voice用于展示和管理语音素材相关功能同时监听其@change事件触发materialCount方法 -->
<material-file fileType="voice" ref="voicePanel" @change="materialCount"></material-file> <material-file fileType="voice" ref="voicePanel" @change="materialCount"></material-file>
</el-tab-pane> </el-tab-pane>
<!-- 视频素材选项卡面板标签显示为视频素材数量数量通过assetsCount.videoCount动态获取设置name属性为video采用懒加载模式 --> <!-- 视频素材标签页 -->
<el-tab-pane :label="'视频素材('+assetsCount.videoCount+')'" name="video" lazy> <el-tab-pane :label="'视频素材(' + assetsCount.videoCount + ')'" name="video" lazy>
<!-- 引入自定义组件material-file设置其fileType属性为video用于展示和管理视频素材相关功能同时监听其@change事件触发materialCount方法 -->
<material-file fileType="video" ref="videoPanel" @change="materialCount"></material-file> <material-file fileType="video" ref="videoPanel" @change="materialCount"></material-file>
</el-tab-pane> </el-tab-pane>
<!-- 图文素材选项卡面板标签显示为图文素材数量数量通过assetsCount.newsCount动态获取设置name属性为news采用懒加载模式 --> <!-- 图文素材标签页使用自定义的MaterialNews组件 -->
<el-tab-pane :label="'图文素材('+assetsCount.newsCount+')'" name="news" lazy> <el-tab-pane :label="'图文素材(' + assetsCount.newsCount + ')'" name="news" lazy>
<!-- 引入自定义组件material-news用于展示和管理图文素材相关功能同时监听其@change事件触发materialCount方法 -->
<material-news ref="newsPanel" @change="materialCount"></material-news> <material-news ref="newsPanel" @change="materialCount"></material-news>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@ -28,54 +25,49 @@
export default { export default {
data() { data() {
return { return {
// el-tabsimage //
activeTab: 'image', activeTab: 'image',
//
assetsCount: { assetsCount: {
// .. imageCount: '..', //
imageCount: '..', videoCount: '..', //
// .. voiceCount: '..', //
videoCount: '..', newsCount: '..' //
// ..
voiceCount: '..',
// ..
newsCount: '..'
} }
}; };
}, },
// MaterialFileMaterialNews
components: { components: {
// material-file
MaterialFile: () => import('./assets/material-file'), MaterialFile: () => import('./assets/material-file'),
// material-news
MaterialNews: () => import('./assets/material-news') MaterialNews: () => import('./assets/material-news')
}, },
//
mounted() { mounted() {
// materialCount this.materialCount(); // materialCount
this.materialCount();
}, },
methods: { methods: {
//
handleTabClick(tab, event) { handleTabClick(tab, event) {
// DOM
this.$nextTick(() => { this.$nextTick(() => {
// refinitinit // DOMinit
this.$refs[tab.name + 'Panel'].init(); this.$refs[tab.name + 'Panel'].init()
}); })
}, },
//
materialCount() { materialCount() {
// HTTPURL // 使$httpaxios
this.$http({ this.$http({
url: this.$http.adornUrl('/manage/wxAssets/materialCount') url: this.$http.adornUrl('/manage/wxAssets/materialCount') // URL
}).then(({ data }) => { }).then(({ data }) => {
//
if (data && data.code == 200) { if (data && data.code == 200) {
// assetsCount // assetsCount
this.assetsCount = data.data; this.assetsCount = data.data
} else { } else {
// //
this.$message.error(data.msg); this.$message.error(data.msg);
} }
}); })
} }
} }
}; };
</script> </script>

@ -1,38 +1,38 @@
<template> <template>
<!-- 最外层的 div 作为整体容器 -->
<div> <div>
<!-- 菜单输入组容器设置底部边框样式用于展示菜单名称以及删除菜单按钮 --> <!-- 菜单项的输入底部边框 -->
<div class="menu-input-group" style="border-bottom: 2px #e8e8e8 solid;"> <div class="menu-input-group" style="border-bottom: 2px #e8e8e8 solid;">
<!-- 展示菜单名称通过插值表达式绑定 button.name 获取名称值 --> <!-- 显示菜单名称 -->
<div class="menu-name">{{button.name}}</div> <div class="menu-name">{{ button.name }}</div>
<!-- 删除菜单按钮点击时通过 $emit 触发父组件的 'delMenu' 事件由父组件来处理菜单删除相关逻辑 --> <!-- 删除菜单项的按钮点击时触发'delMenu'事件 -->
<div class="menu-del" @click="$emit('delMenu')"></div> <div class="menu-del" @click="$emit('delMenu')"></div>
</div> </div>
<!-- 菜单输入组容器用于输入菜单名称 -->
<!-- 菜单名称的输入框组 -->
<div class="menu-input-group"> <div class="menu-input-group">
<!-- 菜单名称标签 -->
<div class="menu-label">菜单名称</div> <div class="menu-label">菜单名称</div>
<!-- 菜单名称输入框所在的容器 -->
<div class="menu-input"> <div class="menu-input">
<!-- 文本输入框用于输入菜单名称绑定 v-model button.name 实现双向数据绑定监听 input 事件触发 checkMenuName 方法来检查名称长度是否合规 --> <!-- 输入框用于输入菜单名称绑定到button.name并监听输入事件 -->
<input type="text" name="name" placeholder="请输入菜单名称" class="menu-input-text" v-model="button.name" @input="checkMenuName(button.name)"> <input type="text" name="name" placeholder="请输入菜单名称" class="menu-input-text" v-model="button.name"
<!-- 提示信息 menuNameBounds true 时显示提示字数超过上限通过 v-show 根据条件控制显示与否 --> @input="checkMenuName(button.name)">
<!-- 当菜单名称超过字数限制时显示 -->
<p class="menu-tips" style="color:#e15f63" v-show="menuNameBounds"></p> <p class="menu-tips" style="color:#e15f63" v-show="menuNameBounds"></p>
<!-- 提示信息显示菜单名称的字数限制规则根据 selectedMenuLevel 的值动态展示不同的字数上限说明 --> <!-- 显示菜单名称的字数限制 -->
<p class="menu-tips">字数不超过{{selectedMenuLevel==1?'5':'8'}}个汉字</p> <p class="menu-tips">字数不超过{{ selectedMenuLevel == 1 ? '5' : '8' }}个汉字</p>
</div> </div>
</div> </div>
<!-- 根据按钮是否有子按钮或者子按钮数组长度是否为 0 来决定是否显示下面的内容若没有子按钮则展示以下配置项 -->
<div v-show="!button.subButtons || button.subButtons.length==0"> <!-- 当没有子菜单或子菜单为空时显示 -->
<!-- 菜单输入组容器用于选择菜单内容类型 --> <div v-show="!button.subButtons || button.subButtons.length == 0">
<!-- 菜单内容的输入框组 -->
<div class="menu-input-group"> <div class="menu-input-group">
<!-- 菜单内容类型标签 -->
<div class="menu-label">菜单内容</div> <div class="menu-label">菜单内容</div>
<!-- 下拉选择框用于选择菜单的类型通过 v-model 双向绑定 button.type绑定不同的选项值每个选项对应不同的菜单功能 -->
<div class="menu-input"> <div class="menu-input">
<!-- 下拉选择框用于选择菜单类型 -->
<select v-model="button.type" name="type" class="menu-input-text"> <select v-model="button.type" name="type" class="menu-input-text">
<option value="view">跳转网页(view)</option> <option value="view">跳转网页(view)</option>
<option value="media_id">发送消息(media_id)</option> <option value="media_id">发送消息(media_id)</option>
<!-- 注释掉的选项 -->
<!--<option value="view_limited">跳转公众号图文消息链接(view_limited)</option>--> <!--<option value="view_limited">跳转公众号图文消息链接(view_limited)</option>-->
<option value="miniprogram">打开指定小程序(miniprogram)</option> <option value="miniprogram">打开指定小程序(miniprogram)</option>
<option value="click">自定义点击事件(click)</option> <option value="click">自定义点击事件(click)</option>
@ -45,83 +45,55 @@
</select> </select>
</div> </div>
</div> </div>
<!-- 当菜单类型为 'view' 时显示的内容用于配置跳转网页相关信息 -->
<div class="menu-content" v-if="button.type=='view'"> <!-- 根据选择的菜单类型显示不同的内容输入框 -->
<!-- 菜单输入组容器用于输入页面地址 --> <div class="menu-content" v-if="button.type == 'view'">
<div class="menu-input-group"> <div class="menu-input-group">
<!-- 提示信息说明点击该子菜单后的跳转行为 -->
<p class="menu-tips">订阅者点击该子菜单会跳到以下链接</p> <p class="menu-tips">订阅者点击该子菜单会跳到以下链接</p>
<!-- 页面地址标签 -->
<div class="menu-label">页面地址</div> <div class="menu-label">页面地址</div>
<!-- 页面地址输入框所在的容器 -->
<div class="menu-input"> <div class="menu-input">
<!-- 文本输入框用于输入页面地址通过 v-model 双向绑定 button.url -->
<input type="text" placeholder="" class="menu-input-text" v-model="button.url"> <input type="text" placeholder="" class="menu-input-text" v-model="button.url">
</div> </div>
</div> </div>
</div> </div>
<!-- 当菜单类型为 'media_id' 时显示的内容用于配置发送图文消息相关信息 --> <div class="menu-content" v-else-if="button.type == 'media_id'">
<div class="menu-content" v-else-if="button.type=='media_id'">
<!-- 菜单输入组容器用于输入图文消息的 media_id -->
<div class="menu-input-group"> <div class="menu-input-group">
<!-- 提示信息说明点击该菜单后的行为 -->
<p class="menu-tips">订阅者点击该菜单会收到以下图文消息</p> <p class="menu-tips">订阅者点击该菜单会收到以下图文消息</p>
<!-- media_id 标签 -->
<div class="menu-label">media_id</div> <div class="menu-label">media_id</div>
<!-- media_id 输入框所在的容器 -->
<div class="menu-input"> <div class="menu-input">
<!-- 文本输入框用于输入图文消息的 media_id通过 v-model 双向绑定 button.mediaId -->
<input type="text" placeholder="图文消息media_id" class="menu-input-text" v-model="button.mediaId"> <input type="text" placeholder="图文消息media_id" class="menu-input-text" v-model="button.mediaId">
</div> </div>
</div> </div>
</div> </div>
<!-- 当菜单类型为 'miniprogram' 时显示的内容用于配置小程序相关信息包含 appId页面路径以及备用网页等 --> <div class="menu-content" v-else-if="button.type == 'miniprogram'">
<div class="menu-content" v-else-if="button.type=='miniprogram'">
<!-- 菜单输入组容器用于输入小程序的 appId -->
<div class="menu-input-group"> <div class="menu-input-group">
<!-- 提示信息说明点击该子菜单后的跳转行为 -->
<p class="menu-tips">订阅者点击该子菜单会跳到以下小程序</p> <p class="menu-tips">订阅者点击该子菜单会跳到以下小程序</p>
<!-- 小程序 appId 标签 -->
<div class="menu-label">小程序appId</div> <div class="menu-label">小程序appId</div>
<!-- 小程序 appId 输入框所在的容器 -->
<div class="menu-input"> <div class="menu-input">
<!-- 文本输入框用于输入小程序的 appId通过 v-model 双向绑定 button.appId并提示仅认证公众号可配置 --> <input type="text" placeholder="小程序的appId仅认证公众号可配置" class="menu-input-text"
<input type="text" placeholder="小程序的appId仅认证公众号可配置" class="menu-input-text" v-model="button.appId"> v-model="button.appId">
</div> </div>
</div> </div>
<!-- 菜单输入组容器用于输入小程序的页面路径 -->
<div class="menu-input-group"> <div class="menu-input-group">
<!-- 小程序路径标签 -->
<div class="menu-label">小程序路径</div> <div class="menu-label">小程序路径</div>
<!-- 小程序路径输入框所在的容器 -->
<div class="menu-input"> <div class="menu-input">
<!-- 文本输入框用于输入小程序的页面路径通过 v-model 双向绑定 button.pagePath并给出示例路径 --> <input type="text" placeholder="小程序的页面路径 pages/index/index" class="menu-input-text"
<input type="text" placeholder="小程序的页面路径 pages/index/index" class="menu-input-text" v-model="button.pagePath"> v-model="button.pagePath">
</div> </div>
</div> </div>
<!-- 菜单输入组容器用于输入备用网页地址 -->
<div class="menu-input-group"> <div class="menu-input-group">
<!-- 备用网页标签 -->
<div class="menu-label">备用网页</div> <div class="menu-label">备用网页</div>
<!-- 备用网页地址输入框所在的容器 -->
<div class="menu-input"> <div class="menu-input">
<!-- 文本输入框用于输入备用网页地址通过 v-model 双向绑定 button.url并给出相关说明 -->
<input type="text" placeholder="" class="menu-input-text" v-model="button.url"> <input type="text" placeholder="" class="menu-input-text" v-model="button.url">
<p class="menu-tips">旧版微信客户端无法支持小程序用户点击菜单时将会打开备用网页</p> <p class="menu-tips">旧版微信客户端无法支持小程序用户点击菜单时将会打开备用网页</p>
</div> </div>
</div> </div>
</div> </div>
<!-- 当菜单类型为其他值非上述几种情况时显示的内容用于配置菜单 KEY -->
<div class="menu-content" v-else> <div class="menu-content" v-else>
<!-- 菜单输入组容器用于输入菜单 KEY -->
<div class="menu-input-group"> <div class="menu-input-group">
<!-- 提示信息说明 KEY 值的长度限制及用途 -->
<p class="menu-tips">用于消息接口推送不超过128字节</p> <p class="menu-tips">用于消息接口推送不超过128字节</p>
<!-- 菜单 KEY 值标签 -->
<div class="menu-label">菜单KEY值</div> <div class="menu-label">菜单KEY值</div>
<!-- 菜单 KEY 值输入框所在的容器 -->
<div class="menu-input"> <div class="menu-input">
<!-- 文本输入框用于输入菜单 KEY 通过 v-model 双向绑定 button.key -->
<input type="text" placeholder="" class="menu-input-text" v-model="button.key"> <input type="text" placeholder="" class="menu-input-text" v-model="button.key">
</div> </div>
</div> </div>
@ -132,14 +104,13 @@
<script> <script>
export default { export default {
//
props: { props: {
// 1 // 1
selectedMenuLevel: { selectedMenuLevel: {
type: Number, type: Number,
default: 1 default: 1
}, },
// //
button: { button: {
type: Object, type: Object,
required: true required: true
@ -147,35 +118,29 @@ export default {
}, },
data() { data() {
return { return {
// false //
menuNameBounds: false, menuNameBounds: false,
} }
}, },
methods: { methods: {
// //
checkMenuName: function (val) { checkMenuName: function (val) {
// 1 getMenuNameLen 10 // menuNameBounds
if (this.selectedMenuLevel == 1 && this.getMenuNameLen(val) <= 10) { if (this.selectedMenuLevel == 1 && this.getMenuNameLen(val) <= 5) { // 510
// menuNameBounds false this.menuNameBounds = false
this.menuNameBounds = false; } else if (this.selectedMenuLevel == 2 && this.getMenuNameLen(val) <= 8) {
} this.menuNameBounds = false
// 2 16 } else {
else if (this.selectedMenuLevel == 2 && this.getMenuNameLen(val) <= 16) { this.menuNameBounds = true
this.menuNameBounds = false;
}
// menuNameBounds true
else {
this.menuNameBounds = true;
} }
}, },
// //
getMenuNameLen: function (val) { getMenuNameLen: function (val) {
var len = 0; var len = 0;
//
for (var i = 0; i < val.length; i++) { for (var i = 0; i < val.length; i++) {
var a = val.charAt(i); var a = val.charAt(i);
// ASCII 2 1 //
a.match(/[^\x00-\xff]/ig)!= null? len += 2 : len += 1; a.match(/[^\x00-\xff]/ig) != null ? len += 2 : len += 1;
} }
return len; return len;
} }

Loading…
Cancel
Save