diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 00000000..23cdb0b0 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,33 @@ + + + diff --git a/src/assets/css/common.css b/src/assets/css/common.css new file mode 100644 index 00000000..92f92b67 --- /dev/null +++ b/src/assets/css/common.css @@ -0,0 +1,847 @@ +/* 常用辅助css */ + +/* ================== + 布局 + ==================== */ + +/* -- flex弹性布局 -- */ + +.flex { + display: flex; +} + +.basis-xs { + flex-basis: 20%; +} + +.basis-sm { + flex-basis: 40%; +} + +.basis-df { + flex-basis: 50%; +} + +.basis-lg { + flex-basis: 60%; +} + +.basis-xl { + flex-basis: 80%; +} + +.flex-sub { + flex: 1; +} + +.flex-twice { + flex: 2; +} + +.flex-treble { + flex: 3; +} + +.flex-direction { + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.align-start { + align-items: flex-start; +} + +.align-end { + align-items: flex-end; +} + +.align-center { + align-items: center; +} + +.align-stretch { + align-items: stretch; +} + +.self-start { + align-self: flex-start; +} + +.self-center { + align-self: flex-center; +} + +.self-end { + align-self: flex-end; +} + +.self-stretch { + align-self: stretch; +} + +.align-stretch { + align-items: stretch; +} + +.justify-start { + justify-content: flex-start; +} + +.justify-end { + justify-content: flex-end; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.justify-around { + justify-content: space-around; +} + +/* -- 内外边距 -- */ + +.margin-0 { + margin: 0; +} + +.margin-xs { + margin: 5px; +} + +.margin-sm { + margin: 10px; +} + +.margin { + margin: 15px; +} + +.margin-lg { + margin: 20px; +} + +.margin-xl { + margin: 25px; +} + +.margin-top-xs { + margin-top: 5px; +} + +.margin-top-sm { + margin-top: 10px; +} + +.margin-top { + margin-top: 15px; +} + +.margin-top-lg { + margin-top: 20px; +} + +.margin-top-xl { + margin-top: 25px; +} + +.margin-right-xs { + margin-right: 5px; +} + +.margin-right-sm { + margin-right: 10px; +} + +.margin-right { + margin-right: 15px; +} + +.margin-right-lg { + margin-right: 20px; +} + +.margin-right-xl { + margin-right: 25px; +} + +.margin-bottom-xs { + margin-bottom: 5px; +} + +.margin-bottom-sm { + margin-bottom: 10px; +} + +.margin-bottom { + margin-bottom: 15px; +} + +.margin-bottom-lg { + margin-bottom: 20px; +} + +.margin-bottom-xl { + margin-bottom: 25px; +} + +.margin-left-xs { + margin-left: 5px; +} + +.margin-left-sm { + margin-left: 10px; +} + +.margin-left { + margin-left: 15px; +} + +.margin-left-lg { + margin-left: 20px; +} + +.margin-left-xl { + margin-left: 25px; +} + +.margin-lr-xs { + margin-left: 5px; + margin-right: 5px; +} + +.margin-lr-sm { + margin-left: 10px; + margin-right: 10px; +} + +.margin-lr { + margin-left: 15px; + margin-right: 15px; +} + +.margin-lr-lg { + margin-left: 20px; + margin-right: 20px; +} + +.margin-lr-xl { + margin-left: 25px; + margin-right: 25px; +} + +.margin-tb-xs { + margin-top: 5px; + margin-bottom: 5px; +} + +.margin-tb-sm { + margin-top: 10px; + margin-bottom: 10px; +} + +.margin-tb { + margin-top: 15px; + margin-bottom: 15px; +} + +.margin-tb-lg { + margin-top: 20px; + margin-bottom: 20px; +} + +.margin-tb-xl { + margin-top: 25px; + margin-bottom: 25px; +} + +.padding-0 { + padding: 0; +} + +.padding-xs { + padding: 5px; +} + +.padding-sm { + padding: 10px; +} + +.padding { + padding: 15px; +} + +.padding-lg { + padding: 20px; +} + +.padding-xl { + padding: 25px; +} + +.padding-top-xs { + padding-top: 5px; +} + +.padding-top-sm { + padding-top: 10px; +} + +.padding-top { + padding-top: 15px; +} + +.padding-top-lg { + padding-top: 20px; +} + +.padding-top-xl { + padding-top: 25px; +} + +.padding-right-xs { + padding-right: 5px; +} + +.padding-right-sm { + padding-right: 10px; +} + +.padding-right { + padding-right: 15px; +} + +.padding-right-lg { + padding-right: 20px; +} + +.padding-right-xl { + padding-right: 25px; +} + +.padding-bottom-xs { + padding-bottom: 5px; +} + +.padding-bottom-sm { + padding-bottom: 10px; +} + +.padding-bottom { + padding-bottom: 15px; +} + +.padding-bottom-lg { + padding-bottom: 20px; +} + +.padding-bottom-xl { + padding-bottom: 25px; +} + +.padding-left-xs { + padding-left: 5px; +} + +.padding-left-sm { + padding-left: 10px; +} + +.padding-left { + padding-left: 15px; +} + +.padding-left-lg { + padding-left: 20px; +} + +.padding-left-xl { + padding-left: 25px; +} + +.padding-lr-xs { + padding-left: 5px; + padding-right: 5px; +} + +.padding-lr-sm { + padding-left: 10px; + padding-right: 10px; +} + +.padding-lr { + padding-left: 15px; + padding-right: 15px; +} + +.padding-lr-lg { + padding-left: 20px; + padding-right: 20px; +} + +.padding-lr-xl { + padding-left: 25px; + padding-right: 25px; +} + +.padding-tb-xs { + padding-top: 5px; + padding-bottom: 5px; +} + +.padding-tb-sm { + padding-top: 10px; + padding-bottom: 10px; +} + +.padding-tb { + padding-top: 15px; + padding-bottom: 15px; +} + +.padding-tb-lg { + padding-top: 20px; + padding-bottom: 20px; +} + +.padding-tb-xl { + padding-top: 25px; + padding-bottom: 25px; +} + +/* -- 浮动 -- */ + +.cf::after, +.cf::before { + content: " "; + display: table; +} + +.cf::after { + clear: both; +} + +.fl { + float: left; +} + +.fr { + float: right; +} + + +/* ================== + 背景 + ==================== */ + + .line-red::after, + .lines-red::after { + border-color: #e54d42; + } + + .line-orange::after, + .lines-orange::after { + border-color: #f37b1d; + } + + .line-yellow::after, + .lines-yellow::after { + border-color: #fbbd08; + } + + .line-olive::after, + .lines-olive::after { + border-color: #8dc63f; + } + + .line-green::after, + .lines-green::after { + border-color: #39b54a; + } + + .line-cyan::after, + .lines-cyan::after { + border-color: #1cbbb4; + } + + .line-blue::after, + .lines-blue::after { + border-color: #0081ff; + } + + .line-purple::after, + .lines-purple::after { + border-color: #6739b6; + } + + .line-mauve::after, + .lines-mauve::after { + border-color: #9c26b0; + } + + .line-pink::after, + .lines-pink::after { + border-color: #e03997; + } + + .line-brown::after, + .lines-brown::after { + border-color: #a5673f; + } + + .line-grey::after, + .lines-grey::after { + border-color: #8799a3; + } + + .line-gray::after, + .lines-gray::after { + border-color: #aaaaaa; + } + + .line-black::after, + .lines-black::after { + border-color: #333333; + } + + .line-white::after, + .lines-white::after { + border-color: #ffffff; + } + + .bg-red { + background-color: #e54d42; + color: #ffffff; + } + + .bg-orange { + background-color: #f37b1d; + color: #ffffff; + } + + .bg-yellow { + background-color: #fbbd08; + color: #333333; + } + + .bg-olive { + background-color: #8dc63f; + color: #ffffff; + } + + .bg-green { + background-color: #39b54a; + color: #ffffff; + } + + .bg-cyan { + background-color: #1cbbb4; + color: #ffffff; + } + + .bg-blue { + background-color: #0081ff; + color: #ffffff; + } + + .bg-purple { + background-color: #6739b6; + color: #ffffff; + } + + .bg-mauve { + background-color: #9c26b0; + color: #ffffff; + } + + .bg-pink { + background-color: #e03997; + color: #ffffff; + } + + .bg-brown { + background-color: #a5673f; + color: #ffffff; + } + + .bg-grey { + background-color: #8799a3; + color: #ffffff; + } + + .bg-gray { + background-color: #f0f0f0; + color: #333333; + } + + .bg-black { + background-color: #333333; + color: #ffffff; + } + + .bg-white { + background-color: #ffffff; + color: #666666; + } + + .bg-red.light { + color: #e54d42; + background-color: #fadbd9; + } + + .bg-orange.light { + color: #f37b1d; + background-color: #fde6d2; + } + + .bg-yellow.light { + color: #fbbd08; + background-color: #fef2ced2; + } + + .bg-olive.light { + color: #8dc63f; + background-color: #e8f4d9; + } + + .bg-green.light { + color: #39b54a; + background-color: #d7f0dbff; + } + + .bg-cyan.light { + color: #1cbbb4; + background-color: #d2f1f0; + } + + .bg-blue.light { + color: #0081ff; + background-color: #cce6ff; + } + + .bg-purple.light { + color: #6739b6; + background-color: #e1d7f0; + } + + .bg-mauve.light { + color: #9c26b0; + background-color: #ebd4ef; + } + + .bg-pink.light { + color: #e03997; + background-color: #f9d7ea; + } + + .bg-brown.light { + color: #a5673f; + background-color: #ede1d9; + } + + .bg-grey.light { + color: #8799a3; + background-color: #e7ebed; + } + + .bg-gradual-red { + background-image: linear-gradient(45deg, #f43f3b, #ec008c); + color: #ffffff; + } + + .bg-gradual-orange { + background-image: linear-gradient(45deg, #ff9700, #ed1c24); + color: #ffffff; + } + + .bg-gradual-green { + background-image: linear-gradient(45deg, #39b54a, #8dc63f); + color: #ffffff; + } + + .bg-gradual-purple { + background-image: linear-gradient(45deg, #9000ff, #5e00ff); + color: #ffffff; + } + + .bg-gradual-pink { + background-image: linear-gradient(45deg, #ec008c, #6739b6); + color: #ffffff; + } + + .bg-gradual-blue { + background-image: linear-gradient(45deg, #0081ff, #1cbbb4); + color: #ffffff; + } + +/* ================== + 文本 + ==================== */ + +.text-xs { + font-size: 10px; +} + +.text-sm { + font-size: 12px; +} + +.text-df { + font-size: 14px; +} + +.text-lg { + font-size: 16px; +} + +.text-xl { + font-size: 18px; +} + +.text-xxl { + font-size: 22px; +} + +.text-sl { + font-size: 40px; +} + +.text-xsl { + font-size: 60px; +} + +.text-Abc { + text-transform: Capitalize; +} + +.text-ABC { + text-transform: Uppercase; +} + +.text-abc { + text-transform: Lowercase; +} + + +.text-cut { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} + +.text-bold { + font-weight: bold; +} + +.text-center { + text-align: center; +} + +.text-content { + line-height: 1.6; +} + +.text-left { + text-align: left; +} + +.text-right { + text-align: right; +} + +.text-red, +.line-red, +.lines-red { + color: #e54d42; +} + +.text-orange, +.line-orange, +.lines-orange { + color: #f37b1d; +} + +.text-yellow, +.line-yellow, +.lines-yellow { + color: #fbbd08; +} + +.text-olive, +.line-olive, +.lines-olive { + color: #8dc63f; +} + +.text-green, +.line-green, +.lines-green { + color: #39b54a; +} + +.text-cyan, +.line-cyan, +.lines-cyan { + color: #1cbbb4; +} + +.text-blue, +.line-blue, +.lines-blue { + color: #0081ff; +} + +.text-purple, +.line-purple, +.lines-purple { + color: #6739b6; +} + +.text-mauve, +.line-mauve, +.lines-mauve { + color: #9c26b0; +} + +.text-pink, +.line-pink, +.lines-pink { + color: #e03997; +} + +.text-brown, +.line-brown, +.lines-brown { + color: #a5673f; +} + +.text-grey, +.line-grey, +.lines-grey { + color: #8799a3; +} + +.text-gray, +.line-gray, +.lines-gray { + color: #aaaaaa; +} + +.text-black, +.line-black, +.lines-black { + color: #333333; +} + +.text-white, +.line-white, +.lines-white { + color: #ffffff; +} \ No newline at end of file diff --git a/src/assets/css/wx-menu.css b/src/assets/css/wx-menu.css new file mode 100644 index 00000000..eebbb6e9 --- /dev/null +++ b/src/assets/css/wx-menu.css @@ -0,0 +1,365 @@ +@charset "utf-8"; +* { + box-sizing: border-box; +} + +#app-menu ul { + padding: 0; +} + +#app-menu li { + list-style: none; +} + +#app-menu { + overflow: hidden; + width: 100%; +} + +.weixin-preview { + position: relative; + width: 320px; + height: 540px; + float: left; + margin-right: 10px; + border: 1px solid #e7e7eb; +} + +.weixin-preview a { + text-decoration: none; + color: #616161; +} + +.weixin-preview .weixin-hd .weixin-title { + color: #fff; + font-size: 15px; + width: 100%; + text-align: center; + position: absolute; + top: 33px; + left: 0px; +} + +.weixin-preview .weixin-header{ + text-align: center; + padding: 10px 0; + background-color: #616161; + color: #ffffff; +} + +.weixin-preview .weixin-menu { + position: absolute; + bottom: 0; + left: 0; + right: 0; + border-top: 1px solid #e7e7e7; + background-position: 0 0; + background-repeat: no-repeat; + margin-bottom: 0px; +} + +/*一级*/ +.weixin-preview .weixin-menu .menu-item { + position: relative; + float: left; + line-height: 50px; + height: 50px; + text-align: center; + width: 33.33%; + border-left: 1px solid #e7e7e7; + cursor: pointer; + color: #616161; +} + +/*二级*/ +.weixin-preview .weixin-sub-menu { + position: absolute; + bottom: 60px; + left: 0; + right: 0; + border-top: 1px solid #d0d0d0; + margin-bottom: 0px; + background: #fafafa; + display: block; + padding: 0; +} + +.weixin-preview .weixin-sub-menu .menu-sub-item { + line-height: 50px; + height: 50px; + text-align: center; + width: 100%; + border: 1px solid #d0d0d0; + border-top-width: 0px; + cursor: pointer; + position: relative; + color: #616161; +} + +.weixin-preview .weixin-sub-menu .menu-sub-item.on-drag-over{ + border-top: 2px solid #44b549; +} + +.weixin-preview .menu-arrow { + position: absolute; + left: 50%; + margin-left: -6px; +} + +.weixin-preview .arrow_in { + bottom: -4px; + display: inline-block; + width: 0px; + height: 0px; + border-width: 6px 6px 0px; + border-style: solid dashed dashed; + border-color: #fafafa transparent transparent; +} + +.weixin-preview .arrow_out { + bottom: -5px; + display: inline-block; + width: 0px; + height: 0px; + border-width: 6px 6px 0px; + border-style: solid dashed dashed; + border-color: #d0d0d0 transparent transparent; +} + +.weixin-preview .menu-item .menu-item-title, .weixin-preview .menu-sub-item .menu-item-title { + width: 100%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + box-sizing: border-box; +} + + +.weixin-preview .menu-item.current, .weixin-preview .menu-sub-item.current { + border: 1px solid #44b549; + background: #fff; + color: #44b549; +} + +.weixin-preview .weixin-sub-menu.show { + display: block; +} + +.weixin-preview .icon_menu_dot { + /* background: url(../images/index_z354723.png) 0px -36px no-repeat; */ + width: 7px; + height: 7px; + vertical-align: middle; + display: inline-block; + margin-right: 2px; + margin-top: -2px; +} + +.weixin-preview .icon14_menu_add { + /* background: url(../images/index_z354723.png) 0px 0px no-repeat; */ + width: 14px; + height: 14px; + vertical-align: middle; + display: inline-block; + margin-top: -2px; +} + +.weixin-preview li:hover .icon14_menu_add { + /* background: url(../images/index_z354723.png) 0px -18px no-repeat; */ +} + +.weixin-preview .menu-item:hover { + color: #000; +} + +.weixin-preview .menu-sub-item:hover { + background: #eee; +} + +.weixin-preview li.current:hover { + background: #fff; + color: #44b549; +} + +/*菜单内容*/ +.weixin-menu-detail { + width: 600px; + padding: 0px 20px 5px; + background-color: #f4f5f9; + border: 1px solid #e7e7eb; + float: left; + min-height: 540px; +} + +.weixin-menu-detail .menu-name { + float: left; + height: 40px; + line-height: 40px; + font-size: 18px; +} + +.weixin-menu-detail .menu-del { + float: right; + height: 40px; + line-height: 40px; + color: #459ae9; + cursor: pointer; +} + +.weixin-menu-detail .menu-input-group { + width: 540px; + margin: 10px 0 30px 0; + overflow: hidden; +} + +.weixin-menu-detail .menu-label { + float: left; + height: 30px; + line-height: 30px; + width: 80px; + text-align: right; +} + +.weixin-menu-detail .menu-input { + float: left; + width: 380px +} + +.weixin-menu-detail .menu-input-text { + border: 0px; + outline: 0px; + background: #fff; + width: 300px; + padding: 5px 0px 5px 0px; + margin-left: 10px; + text-indent: 10px; + height: 35px; +} + +.weixin-menu-detail .menu-tips { + color: #8d8d8d; + padding-top: 4px; + margin: 0; +} + +.weixin-menu-detail .menu-tips.cursor { + color: #459ae9; + cursor: pointer; +} + +.weixin-menu-detail .menu-input .menu-tips { + margin: 0 0 0 10px; +} + +.weixin-menu-detail .menu-content { + padding: 16px 20px; + border: 1px solid #e7e7eb; + background-color: #fff; +} + +.weixin-menu-detail .menu-content .menu-input-group { + margin: 0px 0 10px 0; +} + +.weixin-menu-detail .menu-content .menu-label { + text-align: left; + width: 100px; +} + +.weixin-menu-detail .menu-content .menu-input-text { + border: 1px solid #e7e7eb; +} + +.weixin-menu-detail .menu-content .menu-tips { + padding-bottom: 10px; +} + +.weixin-menu-detail .menu-msg-content { + padding: 0; + border: 1px solid #e7e7eb; + background-color: #fff; +} + +.weixin-menu-detail .menu-msg-content .menu-msg-head { + overflow: hidden; + border-bottom: 1px solid #e7e7eb; + line-height: 38px; + height: 38px; + padding: 0 20px; +} + +.weixin-menu-detail .menu-msg-content .menu-msg-panel { + padding: 30px 50px; +} + +.weixin-menu-detail .menu-msg-content .menu-msg-select { + padding: 40px 20px; + border: 2px dotted #d9dadc; + text-align: center; +} + +.weixin-menu-detail .menu-msg-content .menu-msg-select:hover { + border-color: #b3b3b3; +} + +.weixin-menu-detail .menu-msg-content strong { + display: block; + padding-top: 3px; + font-weight: 400; + font-style: normal; +} + +.weixin-menu-detail .menu-msg-content .menu-msg-title { + float: left; + width: 310px; + height: 30px; + line-height: 30px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.icon36_common { + width: 36px; + height: 36px; + vertical-align: middle; + display: inline-block; +} + +.icon_msg_sender { + margin-right: 3px; + margin-top: -2px; + width: 20px; + height: 20px; + vertical-align: middle; + display: inline-block; + /* background: url(../images/msg_tab_z25df2d.png) 0 -270px no-repeat; */ +} + +.weixin-btn-group { + text-align: center; + width: 100%; + margin: 30px 0px; + overflow: hidden; +} + +.weixin-btn-group .btn { + width: 100px; + border-radius: 0px; +} + +#material-list { + padding: 20px; + overflow-y: scroll; + height: 558px; +} + +#news-list { + padding: 20px; + overflow-y: scroll; + height: 558px; +} + +#material-list table { + width: 100%; +} diff --git a/src/assets/scss/_base.scss b/src/assets/scss/_base.scss new file mode 100644 index 00000000..366d5566 --- /dev/null +++ b/src/assets/scss/_base.scss @@ -0,0 +1,412 @@ +*, +*:before, +*:after { + box-sizing: border-box; +} + +body { + font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif; + font-size: 14px; + line-height: 1.15; + color: #303133; + background-color: #fff; +} + +a { + color: mix(#fff, $--color-primary, 20%); + text-decoration: none; + + &:focus, + &:hover { + color: $--color-primary; + text-decoration: underline; + } +} + +img { + vertical-align: middle; +} + + +/* Utils +------------------------------ */ +.clearfix:before, +.clearfix:after { + content: " "; + display: table; +} + +.clearfix:after { + clear: both; +} + + +/* Animation +------------------------------ */ +.fade-enter-active, +.fade-leave-active { + transition: opacity .5s; +} + +.fade-enter, +.fade-leave-to { + opacity: 0; +} + + +/* Reset element-ui +------------------------------ */ +.site-wrapper { + .el-pagination { + margin-top: 15px; + text-align: right; + } +} + + +/* Layout +------------------------------ */ +.site-wrapper { + position: relative; + min-width: 1180px; +} + + +/* Sidebar fold +------------------------------ */ +.site-sidebar--fold { + .site-navbar__header, + .site-navbar__brand, + .site-sidebar, + .site-sidebar__inner, + .el-menu.site-sidebar__menu { + width: 64px; + } + + .site-navbar__body, + .site-content__wrapper { + margin-left: 64px; + } + + .site-navbar__brand { + &-lg { + display: none; + } + + &-mini { + display: inline-block; + } + } + + .site-sidebar, + .site-sidebar__inner { + overflow: initial; + } + + .site-sidebar__menu-icon { + margin-right: 0; + font-size: 20px; + } + + .site-content--tabs > .el-tabs > .el-tabs__header { + left: 64px; + } +} + +// animation +.site-navbar__header, +.site-navbar__brand, +.site-navbar__body, +.site-sidebar, +.site-sidebar__inner, +.site-sidebar__menu.el-menu, +.site-sidebar__menu-icon, +.site-content__wrapper, +.site-content--tabs > .el-tabs .el-tabs__header { + transition: inline-block .3s, left .3s, width .3s, margin-left .3s, font-size .3s; +} + + +/* Navbar +------------------------------ */ +.site-navbar { + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 1030; + height: 50px; + box-shadow: 0 2px 4px rgba(0, 0, 0, .08); + background-color: $navbar--background-color; + + &--inverse { + .site-navbar__body { + background-color: transparent; + } + + .el-menu { + > .el-menu-item, + > .el-submenu > .el-submenu__title { + color: #fff; + + &:focus, + &:hover { + color: #fff; + background-color: mix(#000, $navbar--background-color, 15%); + } + } + + > .el-menu-item.is-active, + > .el-submenu.is-active > .el-submenu__title { + border-bottom-color: mix(#fff, $navbar--background-color, 85%); + } + + .el-menu-item i, + .el-submenu__title i, + .el-dropdown { + color: #fff; + } + } + + .el-menu--popup-bottom-start { + background-color: $navbar--background-color; + } + } + + &__header { + position: relative; + float: left; + width: 230px; + height: 50px; + overflow: hidden; + } + + &__brand { + display: table-cell; + vertical-align: middle; + width: 230px; + height: 50px; + margin: 0; + line-height: 50px; + font-size: 20px; + text-align: center; + text-transform: uppercase; + white-space: nowrap; + color: #fff; + + &-lg, + &-mini { + margin: 0 5px; + color: #fff; + + &:focus, + &:hover { + color: #fff; + text-decoration: none; + } + } + + &-mini { + display: none; + } + } + + &__switch { + font-size: 18px; + border-bottom: none !important; + } + + &__avatar { + border-bottom: none !important; + + * { + vertical-align: inherit; + } + + .el-dropdown-link { + > img { + width: 36px; + height: auto; + margin-right: 5px; + border-radius: 100%; + vertical-align: middle; + } + } + } + + &__body { + position: relative; + margin-left: 230px; + padding-right: 15px; + background-color: #fff; + } + + &__menu { + float: left; + background-color: transparent; + border-bottom: 0; + + &--right { + float: right; + } + + a:focus, + a:hover { + text-decoration: none; + } + + .el-menu-item, + .el-submenu > .el-submenu__title { + height: 50px; + line-height: 50px; + } + + .el-submenu > .el-menu { + top: 55px; + } + + .el-badge { + display: inline; + z-index: 2; + + &__content { + line-height: 16px; + } + } + } +} + + +/* Sidebar +------------------------------ */ +.site-sidebar { + position: fixed; + top: 50px; + left: 0; + bottom: 0; + z-index: 1020; + width: 230px; + overflow: hidden; + + &--dark, + &--dark-popper { + background-color: $sidebar--background-color-dark; + + .site-sidebar__menu.el-menu, + > .el-menu--popup { + background-color: $sidebar--background-color-dark; + + .el-menu-item, + .el-submenu > .el-submenu__title { + color: $sidebar--color-text-dark; + + &:focus, + &:hover { + color: mix(#fff, $sidebar--color-text-dark, 50%); + background-color: mix(#fff, $sidebar--background-color-dark, 2.5%); + } + } + + .el-menu, + .el-submenu.is-opened { + background-color: mix(#000, $sidebar--background-color-dark, 15%); + } + + .el-menu-item.is-active, + .el-submenu.is-active > .el-submenu__title { + color: mix(#fff, $sidebar--color-text-dark, 80%); + } + } + } + + &__inner { + position: relative; + z-index: 1; + width: 250px; + height: 100%; + padding-bottom: 15px; + overflow-y: scroll; + } + + &__menu.el-menu { + width: 230px; + border-right: 0; + } + + &__menu-icon { + width: 24px; + margin-right: 5px; + text-align: center; + font-size: 16px; + color: inherit !important; + } +} + + +/* Content +------------------------------ */ +.site-content { + position: relative; + padding: 15px; + + &__wrapper { + position: relative; + padding-top: 50px; + margin-left: 230px; + min-height: 100%; + background: $content--background-color; + } + + &--tabs { + padding: 55px 0 0; + } + + > .el-tabs { + > .el-tabs__header { + position: fixed; + top: 50px; + left: 230px; + right: 0; + z-index: 930; + 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); + background-color: #fff; + + > .el-tabs__nav-wrap { + margin-bottom: 0; + + &:after { + display: none; + } + } + } + + > .el-tabs__content { + padding: 0 15px 15px; + + > .site-tabs__tools { + position: fixed; + top: 50px; + right: 0; + z-index: 931; + height: 40px; + padding: 0 12px; + font-size: 16px; + line-height: 40px; + background-color: $content--background-color; + cursor: pointer; + + .el-icon--right { + margin-left: 0; + } + } + } + } +} + +.el-table__expand-icon { + display: inline-block; + width: 14px; + vertical-align: middle; + margin-right: 5px; +} diff --git a/src/assets/scss/_normalize.scss b/src/assets/scss/_normalize.scss new file mode 100644 index 00000000..1dddea42 --- /dev/null +++ b/src/assets/scss/_normalize.scss @@ -0,0 +1,447 @@ +/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in + * IE on Windows Phone and in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers (opinionated). + */ + +body { + margin: 0; +} + +/** + * Add the correct display in IE 9-. + */ + +article, +aside, +footer, +header, +nav, +section { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + * 1. Add the correct display in IE. + */ + +figcaption, +figure, +main { /* 1 */ + display: block; +} + +/** + * Add the correct margin in IE 8. + */ + +figure { + margin: 1em 40px; +} + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * 1. Remove the gray background on active links in IE 10. + * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. + */ + +a { + background-color: transparent; /* 1 */ + -webkit-text-decoration-skip: objects; /* 2 */ +} + +/** + * 1. Remove the bottom border in Chrome 57- and Firefox 39-. + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Prevent the duplicate application of `bolder` by the next rule in Safari 6. + */ + +b, +strong { + font-weight: inherit; +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font style in Android 4.3-. + */ + +dfn { + font-style: italic; +} + +/** + * Add the correct background and color in IE 9-. + */ + +mark { + background-color: #ff0; + color: #000; +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +audio, +video { + display: inline-block; +} + +/** + * Add the correct display in iOS 4-7. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Remove the border on images inside links in IE 10-. + */ + +img { + border-style: none; +} + +/** + * Hide the overflow in IE. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers (opinionated). + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: sans-serif; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` + * controls in Android 4. + * 2. Correct the inability to style clickable types in iOS and Safari. + */ + +button, +html [type="button"], /* 1 */ +[type="reset"], +[type="submit"] { + -webkit-appearance: button; /* 2 */ +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * 1. Add the correct display in IE 9-. + * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Remove the default vertical scrollbar in IE. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10-. + * 2. Remove the padding in IE 10-. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-cancel-button, +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in IE 9-. + * 1. Add the correct display in Edge, IE, and Firefox. + */ + +details, /* 1 */ +menu { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Scripting + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +canvas { + display: inline-block; +} + +/** + * Add the correct display in IE. + */ + +template { + display: none; +} + +/* Hidden + ========================================================================== */ + +/** + * Add the correct display in IE 10-. + */ + +[hidden] { + display: none; +} \ No newline at end of file diff --git a/src/assets/scss/_variables.scss b/src/assets/scss/_variables.scss new file mode 100644 index 00000000..b136ceec --- /dev/null +++ b/src/assets/scss/_variables.scss @@ -0,0 +1,13 @@ +// 站点主色 +// tips: 要达到整站主题修改效果, 请确保[$--color-primary]站点主色与[/src/element-ui-theme/index.js]文件中[import './element-[#17B3A3]/index.css']当前主题色一致 +$--color-primary: #409EFF; + +// Navbar +$navbar--background-color: $--color-primary; + +// Sidebar +$sidebar--background-color-dark: #263238; +$sidebar--color-text-dark: #8a979e; + +// Content +$content--background-color: #f1f4f5; diff --git a/src/assets/scss/index.scss b/src/assets/scss/index.scss new file mode 100644 index 00000000..0336d9aa --- /dev/null +++ b/src/assets/scss/index.scss @@ -0,0 +1,5 @@ +@import "normalize"; +// api: https://github.com/necolas/normalize.css/ +@import "variables"; +// 站点变量 +@import "base"; diff --git a/src/components/icon-svg/index.vue b/src/components/icon-svg/index.vue new file mode 100644 index 00000000..6d09ba00 --- /dev/null +++ b/src/components/icon-svg/index.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/src/components/table-tree-column/index.vue b/src/components/table-tree-column/index.vue new file mode 100644 index 00000000..e8dd534c --- /dev/null +++ b/src/components/table-tree-column/index.vue @@ -0,0 +1,84 @@ + + + diff --git a/src/components/tags-editor.vue b/src/components/tags-editor.vue new file mode 100644 index 00000000..65e670d0 --- /dev/null +++ b/src/components/tags-editor.vue @@ -0,0 +1,77 @@ + + + diff --git a/src/components/template-msg-task.vue b/src/components/template-msg-task.vue new file mode 100644 index 00000000..eea9a9c3 --- /dev/null +++ b/src/components/template-msg-task.vue @@ -0,0 +1,189 @@ + + + \ No newline at end of file diff --git a/src/components/tinymce-editor.vue b/src/components/tinymce-editor.vue new file mode 100644 index 00000000..8fe1a71e --- /dev/null +++ b/src/components/tinymce-editor.vue @@ -0,0 +1,99 @@ + + \ No newline at end of file diff --git a/src/components/wx-account-selector.vue b/src/components/wx-account-selector.vue new file mode 100644 index 00000000..614773d8 --- /dev/null +++ b/src/components/wx-account-selector.vue @@ -0,0 +1,45 @@ + + \ No newline at end of file diff --git a/src/components/wx-msg-preview.vue b/src/components/wx-msg-preview.vue new file mode 100644 index 00000000..84d73ab9 --- /dev/null +++ b/src/components/wx-msg-preview.vue @@ -0,0 +1,42 @@ + + + \ No newline at end of file diff --git a/src/components/wx-user-tags-manager.vue b/src/components/wx-user-tags-manager.vue new file mode 100644 index 00000000..d9107e52 --- /dev/null +++ b/src/components/wx-user-tags-manager.vue @@ -0,0 +1,139 @@ + + + \ No newline at end of file diff --git a/src/main.js b/src/main.js new file mode 100644 index 00000000..304e5839 --- /dev/null +++ b/src/main.js @@ -0,0 +1,32 @@ +import Vue from 'vue' +import App from './App.vue' +import router from './router' +import store from './store' +import VueCookie from 'vue-cookie' +import ElementUI from 'element-ui'; +import moment from 'moment' + +import 'element-ui/lib/theme-chalk/index.css'; +import './assets/css/common.css' +import './assets/scss/index.scss' +import httpRequest from '@/utils/httpRequest' // api: https://github.com/axios/axios +import { isAuth } from '@/utils' +import VueClipboard from 'vue-clipboard2' + +Vue.use(ElementUI); +Vue.use(VueClipboard) +Vue.use(VueCookie) +Vue.config.productionTip = false + +// 挂载全局 +Vue.prototype.$http = httpRequest // ajax请求方法 +Vue.prototype.isAuth = isAuth // 权限方法 + +moment.locale('zh-cn'); +Vue.prototype.$moment = moment; //时间处理 + +new Vue({ + router, + store, + render: h => h(App) +}).$mount('#app') diff --git a/src/router/import-views.js b/src/router/import-views.js new file mode 100644 index 00000000..331acba4 --- /dev/null +++ b/src/router/import-views.js @@ -0,0 +1 @@ +module.exports = file => () => import('@/views/' + file + '.vue') diff --git a/src/router/index.js b/src/router/index.js new file mode 100644 index 00000000..84cd1aed --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,154 @@ +/** + * 全站路由配置 + * + * 建议: + * 1. 代码中路由统一使用name属性跳转(不使用path属性) + */ +import Vue from 'vue' +import VueRouter from 'vue-router' +import http from '@/utils/httpRequest' +import { isURL } from '@/utils/validate' +import { clearLoginInfo } from '@/utils' + +Vue.use(VueRouter) + +const _import = require('./import-views') +// 全局路由(无需嵌套上左右整体布局) +const globalRoutes = [ + { path: '/404', component: () => import('@/views/common/404'), name: '404', meta: { title: '404未找到' } }, + { path: '/login', component: () => import('@/views/common/login'), name: 'login', meta: { title: '登录' } } +] + +// 主入口路由(需嵌套上左右整体布局) +const mainRoutes = { + path: '/', + component: () => import('@/views/main'), + name: 'main', + redirect: { name: 'home' }, + meta: { title: '主入口整体布局' }, + children: [ + // 通过meta对象设置路由展示方式 + // 1. isTab: 是否通过tab展示内容, true: 是, false: 否 + // 2. iframeUrl: 是否通过iframe嵌套展示内容, '以http[s]://开头': 是, '': 否 + // 提示: 如需要通过iframe嵌套展示内容, 但不通过tab打开, 请自行创建组件使用iframe处理! + { 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) { + let token = Vue.cookie.get('token') + if (!token || !/\S/.test(token)) { + clearLoginInfo() + next({ name: 'login' }) + } + next() + } +} + +const router = new VueRouter({ + mode: 'hash', + scrollBehavior: () => ({ y: 0 }), + isAddDynamicMenuRoutes: false, // 是否已经添加动态(菜单)路由 + routes: globalRoutes.concat(mainRoutes) +}) + +router.beforeEach((to, from, next) => { + // 添加动态(菜单)路由 + // 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() + } + }).catch((e) => { + console.log(`%c${e} 请求菜单列表和权限失败,跳转至登录页!!`, 'color:blue') + router.push({ name: 'login' }) + }) + } +}) + +/** + * 判断当前路由类型, global: 全局路由, main: 主入口路由 + * @param {*} route 当前路由 + */ +function fnCurrentRouteType(route, globalRoutes = []) { + var temp = [] + for (var i = 0; i < globalRoutes.length; i++) { + if (route.path === globalRoutes[i].path) { + return 'global' + } else if (globalRoutes[i].children && globalRoutes[i].children.length >= 1) { + temp = temp.concat(globalRoutes[i].children) + } + } + return temp.length >= 1 ? fnCurrentRouteType(route, temp) : 'main' +} + +/** + * 添加动态(菜单)路由 + * @param {*} menuList 菜单列表 + * @param {*} routes 递归创建的动态(菜单)路由 + */ +function fnAddDynamicMenuRoutes(menuList = [], routes = []) { + var temp = [] + for (var i = 0; i < menuList.length; i++) { + if (menuList[i].list && menuList[i].list.length >= 1) { + temp = temp.concat(menuList[i].list) + } else if (menuList[i].url && /\S/.test(menuList[i].url)) { + menuList[i].url = menuList[i].url.replace(/^\//, '') + var route = { + path: menuList[i].url.replace('/', '-'), + component: null, + name: menuList[i].url.replace('/', '-'), + meta: { + menuId: menuList[i].menuId, + title: menuList[i].name, + isDynamic: true, + isTab: true, + iframeUrl: '' + } + } + // 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) + } else { + mainRoutes.name = 'main-dynamic' + mainRoutes.children = routes + router.addRoutes([ + mainRoutes, + { path: '*', redirect: { name: '404' } } + ]) + sessionStorage.setItem('dynamicMenuRoutes', JSON.stringify(mainRoutes.children || '[]')) + console.log('\n') + console.log('%c!<-------------------- 动态(菜单)路由 s -------------------->', 'color:blue') + console.log(mainRoutes.children) + console.log('%c!<-------------------- 动态(菜单)路由 e -------------------->', 'color:blue') + } +} +export default router diff --git a/src/store/index.js b/src/store/index.js new file mode 100644 index 00000000..ebf0d802 --- /dev/null +++ b/src/store/index.js @@ -0,0 +1,34 @@ +import Vue from 'vue' +import Vuex from 'vuex' + +// 引入各个模块的 Vuex 状态管理文件 +import common from './modules/common' +import user from './modules/user' +import article from './modules/article' +import message from './modules/message' +import wxUserTags from './modules/wxUserTags' +import wxAccount from './modules/wxAccount' + +// 注册 Vuex 插件,使其可用于 Vue 中 +Vue.use(Vuex) + +export default new Vuex.Store({ +// 使用 modules 来组织不同的子模块 +modules: { +// 引入并注册各个模块 +common, // 公共模块,可能用于存储一些通用的状态 +user, // 用户模块,存储用户信息相关的状态 +article, // 文章模块,存储与文章相关的状态 +message, // 消息模块,存储消息相关的状态 +wxUserTags, // 微信用户标签模块,管理微信用户标签的状态 +wxAccount // 微信账号模块,管理微信账号相关的状态 +}, + +// mutations 用于同步修改状态,这里没有定义任何 mutations(可根据需求进行扩展) +mutations: { +// 这里可以添加全局的 mutation,但目前没有定义 +}, + +// 启用严格模式,开发环境下会对状态的修改进行检查,确保只能通过 mutation 修改状态 +strict: true +}) diff --git a/src/store/modules/article.js b/src/store/modules/article.js new file mode 100644 index 00000000..1308ab36 --- /dev/null +++ b/src/store/modules/article.js @@ -0,0 +1,12 @@ +export default { + namespaced: true, + state: { + ARTICLE_TYPES: { + 1: '普通文章', + 5: '帮助中心', + } + }, + mutations: { + + } +} diff --git a/src/store/modules/common.js b/src/store/modules/common.js new file mode 100644 index 00000000..467e447f --- /dev/null +++ b/src/store/modules/common.js @@ -0,0 +1,70 @@ +import router from '@/router' + +export default { + namespaced: true, + state: { + // 页面文档可视高度(随窗口改变大小) + documentClientHeight: 0, + // 导航条, 布局风格, defalut(默认) / inverse(反向) + navbarLayoutType: 'default', + // 侧边栏, 布局皮肤, light(浅色) / dark(黑色) + sidebarLayoutSkin: 'dark', + // 侧边栏, 折叠状态 + sidebarFold: false, + // 侧边栏, 菜单 + menuList: [], + menuActiveName: '', + // 内容, 是否需要刷新 + contentIsNeedRefresh: false, + // 主入口标签页 + mainTabs: [], + mainTabsActiveName: '' + }, + mutations: { + updateDocumentClientHeight(state, height) { + state.documentClientHeight = height + }, + updateNavbarLayoutType(state, type) { + state.navbarLayoutType = type + }, + updateSidebarLayoutSkin(state, skin) { + state.sidebarLayoutSkin = skin + }, + updateSidebarFold(state, fold) { + state.sidebarFold = fold + }, + updateMenuList(state, list) { + state.menuList = list + }, + updateMenuActiveName(state, name) { + state.menuActiveName = name + }, + updateContentIsNeedRefresh(state, status) { + state.contentIsNeedRefresh = status + }, + updateMainTabs(state, tabs) { + state.mainTabs = tabs + }, + updateMainTabsActiveName(state, name) { + state.mainTabsActiveName = name + }, + removeTab(state, tabName) { + state.mainTabs = state.mainTabs.filter(item => item.name !== tabName) + if (state.mainTabs.length >= 1) { + // 当前选中tab被删除 + if (tabName === state.mainTabsActiveName) { + var tab = state.mainTabs[state.mainTabs.length - 1] + router.push({ name: tab.name, query: tab.query, params: tab.params }, () => { + state.mainTabsActiveName = tab.name + }) + } + } else { + state.menuActiveName = '' + router.push({ name: 'home' }) + } + }, + closeCurrentTab(state) { + this.commit('common/removeTab', state.mainTabsActiveName) + } + } +} \ No newline at end of file diff --git a/src/store/modules/message.js b/src/store/modules/message.js new file mode 100644 index 00000000..9b689651 --- /dev/null +++ b/src/store/modules/message.js @@ -0,0 +1,33 @@ +export default { + namespaced: true, + state: { + XmlMsgType:{ + "text":"文字", + "image":"图片", + "voice":"语音", + "shortvideo":"短视频", + "video":"视频", + "news":"图文", + "music":"音乐", + "location":"位置", + "link":"链接", + "event":"事件", + "transfer_customer_service":"转客服" + }, + KefuMsgType: { + "text": "文本消息", + "image": "图片消息", + "voice": "语音消息", + "video": "视频消息", + "music": "音乐消息", + "news": "文章链接", + "mpnews": "公众号图文消息", + "wxcard": "卡券消息", + "miniprogrampage": "小程序消息", + "msgmenu": "菜单消息" + } + }, + mutations: { + + } +} diff --git a/src/store/modules/user.js b/src/store/modules/user.js new file mode 100644 index 00000000..6f3798d0 --- /dev/null +++ b/src/store/modules/user.js @@ -0,0 +1,15 @@ +export default { + namespaced: true, + state: { + id: 0, + name: '' + }, + mutations: { + updateId(state, id) { + state.id = id + }, + updateName(state, name) { + state.name = name + } + } +} diff --git a/src/store/modules/wxAccount.js b/src/store/modules/wxAccount.js new file mode 100644 index 00000000..78903d08 --- /dev/null +++ b/src/store/modules/wxAccount.js @@ -0,0 +1,59 @@ +import Vue from 'vue'; + +export default { +// Vuex模块启用命名空间 +namespaced: true, + +// Vuex的state,用来存储应用状态 +state: { +// 账户类型,映射数字ID到账户类型名称 +ACCOUNT_TYPES: { +1: '订阅号', +2: '服务号' +}, + +// 存储账户列表 +accountList: [], + +// 当前选中的Appid,用来标识选择的账号 +selectedAppid: '' +}, + +// Vuex的mutations,用来修改state +mutations: { +// 更新账户列表 +updateAccountList (state, list) { +// 更新state中的accountList +state.accountList = list; + +// 如果列表为空,直接返回 +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(); +} +} +} +}; diff --git a/src/store/modules/wxUserTags.js b/src/store/modules/wxUserTags.js new file mode 100644 index 00000000..6cb02ef4 --- /dev/null +++ b/src/store/modules/wxUserTags.js @@ -0,0 +1,20 @@ +export default { + // 启用 Vuex 模块的命名空间,避免命名冲突 + namespaced: true, + + // state 存储模块的状态 + state: { + // tags 用来存储标签数据的数组 + tags: [] + }, + + // mutations 用来修改 state 中的状态 + mutations: { + // 更新 tags 数组的内容 + updateTags (state, tags) { + // 将传入的 tags 更新到 state 中 + state.tags = tags; + } + } + }; + \ No newline at end of file diff --git a/src/utils/httpRequest.js b/src/utils/httpRequest.js new file mode 100644 index 00000000..9a5ad605 --- /dev/null +++ b/src/utils/httpRequest.js @@ -0,0 +1,84 @@ +import Vue from 'vue' +import axios from 'axios' +import router from '@/router' +import qs from 'qs' +import merge from 'lodash/merge' +import { clearLoginInfo } from '@/utils' + +const baseUrl = '/wx' // 设置请求的基础路径 + +// 创建axios实例 +const http = axios.create({ +timeout: 1000 * 30, // 设置请求超时为30秒 +withCredentials: true, // 允许携带跨域请求的cookie +headers: { +'Content-Type': 'application/json; charset=utf-8' // 默认请求头为json格式 +} +}) + +/** +* 请求拦截器 +* 在每个请求发送之前,加入token(从cookie中获取) +*/ +http.interceptors.request.use(config => { +config.headers['token'] = Vue.cookie.get('token') // 在请求头中加入token +return config // 返回请求配置 +}, error => { +return Promise.reject(error) // 请求出错时,返回Promise拒绝 +}) + +/** +* 响应拦截器 +* 对响应数据进行拦截处理 +* 如果返回的状态码为401(未授权),则清除登录信息并跳转到登录页 +*/ +http.interceptors.response.use(response => { +if (response.data && response.data.code === 401) { // 判断返回的code是否为401,代表token失效 +clearLoginInfo() // 清除登录信息 +router.push({ name: 'login' }) // 跳转到登录页面 +} +return response // 返回响应数据 +}, error => { +return Promise.reject(error) // 响应出错时,返回Promise拒绝 +}) + +/** +* 请求地址处理函数 +* @param {*} actionName 接口的名称,拼接成完整的URL +* @returns {string} 拼接后的完整URL +*/ +http.adornUrl = (actionName) => { +// 在开发环境下,如果开启了代理,则请求路径会带上代理前缀 +return baseUrl + actionName // 返回完整的请求URL +} + +/** +* get请求的参数处理 +* @param {*} params 请求的参数对象 +* @param {*} openDefultParams 是否开启默认参数 +* @returns {object} 处理后的参数对象 +*/ +http.adornParams = (params = {}, openDefultParams = true) => { +const defaults = { +'t': new Date().getTime() // 添加时间戳参数,防止缓存 +} +return openDefultParams ? merge(defaults, params) : params // 合并默认参数和传入的参数 +} + +/** +* post请求的数据处理 +* @param {*} data 请求的数据对象 +* @param {*} openDefultdata 是否开启默认数据 +* @param {*} contentType 数据格式类型('json'或'form') +* @returns {string} 处理后的数据 +*/ +http.adornData = (data = {}, openDefultdata = true, contentType = 'json') => { +const defaults = { +'t': new Date().getTime() // 添加时间戳参数,防止缓存 +} +data = openDefultdata ? merge(defaults, data) : data // 合并默认数据和传入的数据 +// 根据不同的contentType,处理数据格式 +return contentType === 'json' ? JSON.stringify(data) : qs.stringify(data) +} + +export default http // 导出axios实例,供其他模块使用 diff --git a/src/utils/index.js b/src/utils/index.js new file mode 100644 index 00000000..aa52b209 --- /dev/null +++ b/src/utils/index.js @@ -0,0 +1,78 @@ +import Vue from 'vue' +import router from '@/router' +import store from '@/store' + +/** +* 获取UUID +* 生成一个标准的UUID(例如:xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx) +* 使用随机数和指定格式的规则生成UUID +*/ +export function getUUID() { +return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { +return (c === 'x' ? (Math.random() * 16 | 0) : ('r&0x3' | '0x8')).toString(16) +}) +} + +/** +* 检查是否有某个权限 +* @param {*} key 权限的标识符 +* @returns {boolean} 如果权限列表中包含该权限返回true,否则返回false +*/ +export function isAuth(key) { +// 从 sessionStorage 中获取权限列表,并转换为数组,如果没有权限列表,默认返回空数组 +return JSON.parse(sessionStorage.getItem('permissions') || '[]').indexOf(key) !== -1 || false +} + +/** +* 将平面数据转换为树形数据 +* @param {*} data 原始平面数据 +* @param {*} id 唯一标识符字段,默认为'id' +* @param {*} pid 父级标识符字段,默认为'parentId' +* @returns {Array} 转换后的树形数据 +*/ +export function treeDataTranslate(data, id = 'id', pid = 'parentId') { +var res = [] // 存储最终的树形结构 +var temp = {} // 临时存储每个节点,以便快速查找父节点 + +// 将数据转换为临时对象,key为节点的id,值为节点本身 +for (var i = 0; i < data.length; i++) { +temp[data[i][id]] = data[i] +} + +// 遍历数据,根据pid将节点组织成树形结构 +for (var k = 0; k < data.length; k++) { +// 如果节点的父节点存在,并且当前节点的id不等于父节点的id +if (temp[data[k][pid]] && data[k][id] !== data[k][pid]) { +// 如果父节点没有'children'属性,则初始化为数组 +if (!temp[data[k][pid]]['children']) { +temp[data[k][pid]]['children'] = [] +} +// 如果父节点没有'_level'属性,则设置为1 +if (!temp[data[k][pid]]['_level']) { +temp[data[k][pid]]['_level'] = 1 +} +// 当前节点的级别为父节点的级别+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() { +// 删除cookie中的'token' +Vue.cookie.delete('token') +// 目前注释掉了重置store的操作,若需要可以解除注释 +// store.commit('resetStore') +// 重置动态菜单路由标志 +router.options.isAddDynamicMenuRoutes = false +} diff --git a/src/utils/validate.js b/src/utils/validate.js new file mode 100644 index 00000000..c25e3f44 --- /dev/null +++ b/src/utils/validate.js @@ -0,0 +1,36 @@ +/** +* 验证邮箱格式 +* @param {*} s - 需要验证的邮箱地址 +* @returns {boolean} - 返回是否是有效的邮箱地址 +*/ +export function isEmail(s) { + return /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/.test(s) + } + + /** + * 验证手机号码格式(中国手机号) + * @param {*} s - 需要验证的手机号 + * @returns {boolean} - 返回是否是有效的手机号码 + */ + export function isMobile(s) { + return /^1[0-9]{10}$/.test(s) + } + + /** + * 验证固定电话号码格式 + * @param {*} s - 需要验证的电话号码 + * @returns {boolean} - 返回是否是有效的电话号码(包括区号和本地号码) + */ + export function isPhone(s) { + return /^([0-9]{3,4}-)?[0-9]{7,8}$/.test(s) + } + + /** + * 验证URL地址格式 + * @param {*} s - 需要验证的URL地址 + * @returns {boolean} - 返回是否是有效的URL地址(包括http或https协议) + */ + export function isURL(s) { + return /^http[s]?:\/\/.*/.test(s) + } + \ No newline at end of file diff --git a/src/views/common/404.vue b/src/views/common/404.vue new file mode 100644 index 00000000..c96b106e --- /dev/null +++ b/src/views/common/404.vue @@ -0,0 +1,81 @@ + + + + + + \ No newline at end of file diff --git a/src/views/common/home.vue b/src/views/common/home.vue new file mode 100644 index 00000000..fe655f7e --- /dev/null +++ b/src/views/common/home.vue @@ -0,0 +1,18 @@ + + + + \ No newline at end of file diff --git a/src/views/common/login.vue b/src/views/common/login.vue new file mode 100644 index 00000000..1914a120 --- /dev/null +++ b/src/views/common/login.vue @@ -0,0 +1,220 @@ + + + + \ No newline at end of file diff --git a/src/views/common/theme.vue b/src/views/common/theme.vue new file mode 100644 index 00000000..388b2ff6 --- /dev/null +++ b/src/views/common/theme.vue @@ -0,0 +1,53 @@ + + + \ No newline at end of file diff --git a/src/views/main-content.vue b/src/views/main-content.vue new file mode 100644 index 00000000..987dcd84 --- /dev/null +++ b/src/views/main-content.vue @@ -0,0 +1,103 @@ + + + + diff --git a/src/views/main-navbar-update-password.vue b/src/views/main-navbar-update-password.vue new file mode 100644 index 00000000..4c2cd4a7 --- /dev/null +++ b/src/views/main-navbar-update-password.vue @@ -0,0 +1,109 @@ + + + + diff --git a/src/views/main-navbar.vue b/src/views/main-navbar.vue new file mode 100644 index 00000000..75364b13 --- /dev/null +++ b/src/views/main-navbar.vue @@ -0,0 +1,102 @@ + + + diff --git a/src/views/main-sidebar-sub-menu.vue b/src/views/main-sidebar-sub-menu.vue new file mode 100644 index 00000000..b45b1b61 --- /dev/null +++ b/src/views/main-sidebar-sub-menu.vue @@ -0,0 +1,50 @@ + + + diff --git a/src/views/main-sidebar.vue b/src/views/main-sidebar.vue new file mode 100644 index 00000000..795a07d0 --- /dev/null +++ b/src/views/main-sidebar.vue @@ -0,0 +1,90 @@ + + + diff --git a/src/views/main.vue b/src/views/main.vue new file mode 100644 index 00000000..2b044f4b --- /dev/null +++ b/src/views/main.vue @@ -0,0 +1,86 @@ + + + diff --git a/src/views/modules/oss/oss-config.vue b/src/views/modules/oss/oss-config.vue new file mode 100644 index 00000000..ae429ca5 --- /dev/null +++ b/src/views/modules/oss/oss-config.vue @@ -0,0 +1,150 @@ + + + \ No newline at end of file diff --git a/src/views/modules/oss/oss-uploader-tencent.vue b/src/views/modules/oss/oss-uploader-tencent.vue new file mode 100644 index 00000000..81e3ddb2 --- /dev/null +++ b/src/views/modules/oss/oss-uploader-tencent.vue @@ -0,0 +1,105 @@ + + + + + + \ No newline at end of file diff --git a/src/views/modules/oss/oss-uploader.vue b/src/views/modules/oss/oss-uploader.vue new file mode 100644 index 00000000..64bc5914 --- /dev/null +++ b/src/views/modules/oss/oss-uploader.vue @@ -0,0 +1,73 @@ + + + + \ No newline at end of file diff --git a/src/views/modules/oss/oss.vue b/src/views/modules/oss/oss.vue new file mode 100644 index 00000000..4c403ce3 --- /dev/null +++ b/src/views/modules/oss/oss.vue @@ -0,0 +1,117 @@ + + + diff --git a/src/views/modules/sys/config-add-or-update.vue b/src/views/modules/sys/config-add-or-update.vue new file mode 100644 index 00000000..898bfb61 --- /dev/null +++ b/src/views/modules/sys/config-add-or-update.vue @@ -0,0 +1,117 @@ + + + diff --git a/src/views/modules/sys/config.vue b/src/views/modules/sys/config.vue new file mode 100644 index 00000000..b20162c1 --- /dev/null +++ b/src/views/modules/sys/config.vue @@ -0,0 +1,172 @@ + + + diff --git a/src/views/modules/sys/log.vue b/src/views/modules/sys/log.vue new file mode 100644 index 00000000..5223704c --- /dev/null +++ b/src/views/modules/sys/log.vue @@ -0,0 +1,107 @@ + + + diff --git a/src/views/modules/sys/menu-add-or-update.vue b/src/views/modules/sys/menu-add-or-update.vue new file mode 100644 index 00000000..ccee5e29 --- /dev/null +++ b/src/views/modules/sys/menu-add-or-update.vue @@ -0,0 +1,253 @@ + + + + + + \ No newline at end of file diff --git a/src/views/modules/sys/menu.vue b/src/views/modules/sys/menu.vue new file mode 100644 index 00000000..bcdbd004 --- /dev/null +++ b/src/views/modules/sys/menu.vue @@ -0,0 +1,148 @@ + + + + \ No newline at end of file diff --git a/src/views/modules/sys/role-add-or-update.vue b/src/views/modules/sys/role-add-or-update.vue new file mode 100644 index 00000000..c02f64e5 --- /dev/null +++ b/src/views/modules/sys/role-add-or-update.vue @@ -0,0 +1,148 @@ + + + + \ No newline at end of file diff --git a/src/views/modules/sys/role.vue b/src/views/modules/sys/role.vue new file mode 100644 index 00000000..c02f64e5 --- /dev/null +++ b/src/views/modules/sys/role.vue @@ -0,0 +1,148 @@ + + + + \ No newline at end of file diff --git a/src/views/modules/sys/user-add-or-update.vue b/src/views/modules/sys/user-add-or-update.vue new file mode 100644 index 00000000..7db0c06e --- /dev/null +++ b/src/views/modules/sys/user-add-or-update.vue @@ -0,0 +1,177 @@ + + + diff --git a/src/views/modules/sys/user.vue b/src/views/modules/sys/user.vue new file mode 100644 index 00000000..6454a769 --- /dev/null +++ b/src/views/modules/sys/user.vue @@ -0,0 +1,140 @@ + + + diff --git a/src/views/modules/wx/account/wx-account-access-info.vue b/src/views/modules/wx/account/wx-account-access-info.vue new file mode 100644 index 00000000..fb1f853e --- /dev/null +++ b/src/views/modules/wx/account/wx-account-access-info.vue @@ -0,0 +1,54 @@ + + + + diff --git a/src/views/modules/wx/account/wx-account-add-or-update.vue b/src/views/modules/wx/account/wx-account-add-or-update.vue new file mode 100644 index 00000000..27090997 --- /dev/null +++ b/src/views/modules/wx/account/wx-account-add-or-update.vue @@ -0,0 +1,118 @@ + + + diff --git a/src/views/modules/wx/article-add-or-update.vue b/src/views/modules/wx/article-add-or-update.vue new file mode 100644 index 00000000..f400a3a8 --- /dev/null +++ b/src/views/modules/wx/article-add-or-update.vue @@ -0,0 +1,155 @@ + + + diff --git a/src/views/modules/wx/article.vue b/src/views/modules/wx/article.vue new file mode 100644 index 00000000..076d1606 --- /dev/null +++ b/src/views/modules/wx/article.vue @@ -0,0 +1,157 @@ + + + diff --git a/src/views/modules/wx/assets/assets-selector.vue b/src/views/modules/wx/assets/assets-selector.vue new file mode 100644 index 00000000..1123ea88 --- /dev/null +++ b/src/views/modules/wx/assets/assets-selector.vue @@ -0,0 +1,38 @@ + + \ No newline at end of file diff --git a/src/views/modules/wx/assets/material-file-add-or-update.vue b/src/views/modules/wx/assets/material-file-add-or-update.vue new file mode 100644 index 00000000..5f7c8d02 --- /dev/null +++ b/src/views/modules/wx/assets/material-file-add-or-update.vue @@ -0,0 +1,103 @@ + + + diff --git a/src/views/modules/wx/assets/material-file.vue b/src/views/modules/wx/assets/material-file.vue new file mode 100644 index 00000000..cc0b0180 --- /dev/null +++ b/src/views/modules/wx/assets/material-file.vue @@ -0,0 +1,185 @@ + + + \ No newline at end of file diff --git a/src/views/modules/wx/assets/material-news-add-or-update.vue b/src/views/modules/wx/assets/material-news-add-or-update.vue new file mode 100644 index 00000000..c2df6169 --- /dev/null +++ b/src/views/modules/wx/assets/material-news-add-or-update.vue @@ -0,0 +1,221 @@ + + + + \ No newline at end of file diff --git a/src/views/modules/wx/assets/material-news.vue b/src/views/modules/wx/assets/material-news.vue new file mode 100644 index 00000000..42836b75 --- /dev/null +++ b/src/views/modules/wx/assets/material-news.vue @@ -0,0 +1,206 @@ + + + \ No newline at end of file diff --git a/src/views/modules/wx/msg-reply-rule-add-or-update.vue b/src/views/modules/wx/msg-reply-rule-add-or-update.vue new file mode 100644 index 00000000..0a3e5ab4 --- /dev/null +++ b/src/views/modules/wx/msg-reply-rule-add-or-update.vue @@ -0,0 +1,211 @@ + + + \ No newline at end of file diff --git a/src/views/modules/wx/msg-reply-rule.vue b/src/views/modules/wx/msg-reply-rule.vue new file mode 100644 index 00000000..c728b4da --- /dev/null +++ b/src/views/modules/wx/msg-reply-rule.vue @@ -0,0 +1,166 @@ + + + diff --git a/src/views/modules/wx/msg-template-add-or-update.vue b/src/views/modules/wx/msg-template-add-or-update.vue new file mode 100644 index 00000000..199aed64 --- /dev/null +++ b/src/views/modules/wx/msg-template-add-or-update.vue @@ -0,0 +1,165 @@ + + + + \ No newline at end of file diff --git a/src/views/modules/wx/msg-template.vue b/src/views/modules/wx/msg-template.vue new file mode 100644 index 00000000..49e12606 --- /dev/null +++ b/src/views/modules/wx/msg-template.vue @@ -0,0 +1,215 @@ + + + \ No newline at end of file diff --git a/src/views/modules/wx/template-msg-log.vue b/src/views/modules/wx/template-msg-log.vue new file mode 100644 index 00000000..13422461 --- /dev/null +++ b/src/views/modules/wx/template-msg-log.vue @@ -0,0 +1,135 @@ + + + diff --git a/src/views/modules/wx/wx-account.vue b/src/views/modules/wx/wx-account.vue new file mode 100644 index 00000000..541164e5 --- /dev/null +++ b/src/views/modules/wx/wx-account.vue @@ -0,0 +1,137 @@ + + + diff --git a/src/views/modules/wx/wx-assets.vue b/src/views/modules/wx/wx-assets.vue new file mode 100644 index 00000000..65acd980 --- /dev/null +++ b/src/views/modules/wx/wx-assets.vue @@ -0,0 +1,57 @@ + + \ No newline at end of file diff --git a/src/views/modules/wx/wx-menu-button-editor.vue b/src/views/modules/wx/wx-menu-button-editor.vue new file mode 100644 index 00000000..2ec9cab2 --- /dev/null +++ b/src/views/modules/wx/wx-menu-button-editor.vue @@ -0,0 +1,125 @@ + + \ No newline at end of file diff --git a/src/views/modules/wx/wx-menu.vue b/src/views/modules/wx/wx-menu.vue new file mode 100644 index 00000000..cc7d8dd8 --- /dev/null +++ b/src/views/modules/wx/wx-menu.vue @@ -0,0 +1,159 @@ + + + \ No newline at end of file diff --git a/src/views/modules/wx/wx-msg-reply.vue b/src/views/modules/wx/wx-msg-reply.vue new file mode 100644 index 00000000..7721aeb3 --- /dev/null +++ b/src/views/modules/wx/wx-msg-reply.vue @@ -0,0 +1,84 @@ + + + + \ No newline at end of file diff --git a/src/views/modules/wx/wx-msg.vue b/src/views/modules/wx/wx-msg.vue new file mode 100644 index 00000000..eec873d2 --- /dev/null +++ b/src/views/modules/wx/wx-msg.vue @@ -0,0 +1,184 @@ + + + + \ No newline at end of file diff --git a/src/views/modules/wx/wx-qrcode-add-or-update.vue b/src/views/modules/wx/wx-qrcode-add-or-update.vue new file mode 100644 index 00000000..a3a42383 --- /dev/null +++ b/src/views/modules/wx/wx-qrcode-add-or-update.vue @@ -0,0 +1,86 @@ + + + diff --git a/src/views/modules/wx/wx-qrcode.vue b/src/views/modules/wx/wx-qrcode.vue new file mode 100644 index 00000000..38ce9fa9 --- /dev/null +++ b/src/views/modules/wx/wx-qrcode.vue @@ -0,0 +1,142 @@ + + + diff --git a/src/views/modules/wx/wx-user-tagging.vue b/src/views/modules/wx/wx-user-tagging.vue new file mode 100644 index 00000000..ff4b2664 --- /dev/null +++ b/src/views/modules/wx/wx-user-tagging.vue @@ -0,0 +1,102 @@ + + \ No newline at end of file diff --git a/src/views/modules/wx/wx-user.vue b/src/views/modules/wx/wx-user.vue new file mode 100644 index 00000000..963425cd --- /dev/null +++ b/src/views/modules/wx/wx-user.vue @@ -0,0 +1,209 @@ + + + + \ No newline at end of file