init
This commit is contained in:
@@ -0,0 +1,473 @@
|
||||
$tabBtnColor: #577ae9;//tab主题色
|
||||
$treeCheckColor: #577ae9;//树主题色
|
||||
$treeCheckBorderColor: #dddee1;//边框色
|
||||
$treeCheckGroupColor: #f7f7f7;
|
||||
|
||||
.fix {
|
||||
*zoom: 1;
|
||||
&::before,
|
||||
&::after {
|
||||
display: table;
|
||||
content: "";
|
||||
line-height: 0;
|
||||
}
|
||||
&::after {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
.fl {
|
||||
float: left;
|
||||
}
|
||||
.fr {
|
||||
float: right;
|
||||
}
|
||||
.align-middle {
|
||||
position: relative;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.my-tab_btn {
|
||||
margin: 10px auto;
|
||||
position: relative;
|
||||
.item-tab_btn {
|
||||
font-size: 16px;
|
||||
font-weight: 550;
|
||||
color: #656667;
|
||||
float: left;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.item-tab_btn.active {
|
||||
color: #272829;
|
||||
}
|
||||
|
||||
.my-tab_slider {
|
||||
width: 42px;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
bottom: -1px;
|
||||
z-index: 1;
|
||||
transition: transform 0.3s ease-in-out;
|
||||
transform-origin: 0 0;
|
||||
& > .indicator {
|
||||
height: 2px;
|
||||
width: 17px;
|
||||
background: $tabBtnColor;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.my-tab_card {
|
||||
margin: 15px 0 15px 10px;
|
||||
padding-right: 30px;
|
||||
position: relative;
|
||||
.item-wrap{
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
.item-tab_card {
|
||||
float: left;
|
||||
font-size: 13px;
|
||||
padding: 0px 15px;
|
||||
color: #808080;
|
||||
height: 27px;
|
||||
line-height: 27px;
|
||||
transition: all 200ms;
|
||||
}
|
||||
.item-tab_card.active {
|
||||
color: #272829;
|
||||
background: #f7f7f7;
|
||||
border-radius: 27px;
|
||||
}
|
||||
}
|
||||
|
||||
.my-modal {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0.5;
|
||||
background: #000;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.tree-popup {
|
||||
position: fixed;
|
||||
background: #fff;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
transform: translateY(0);
|
||||
backface-visibility: hidden;
|
||||
transition: 0.2s ease-in;
|
||||
width: 100%;
|
||||
height: 85%;
|
||||
z-index: 101;
|
||||
border-radius: 8px 8px 0 0;
|
||||
&_show {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
&_hide {
|
||||
transform: translateY(0%);
|
||||
}
|
||||
|
||||
&_header {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@extend .my-border_bottom;
|
||||
|
||||
& .close-btn {
|
||||
position: absolute;
|
||||
padding: 5px;
|
||||
right: 3%;
|
||||
top: 10px;
|
||||
}
|
||||
}
|
||||
&_body {
|
||||
overflow-y: auto;
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
&_footer {
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
position: relative;
|
||||
padding: 0 10px 0 20px;
|
||||
@extend .my-border_top;
|
||||
.check-box {
|
||||
float: left;
|
||||
&_text {
|
||||
margin-left: 3px;
|
||||
margin-right: 30px;
|
||||
vertical-align: middle;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
& p {
|
||||
font-size: 16px;
|
||||
color: #808080;
|
||||
|
||||
span {
|
||||
font-size: 18px;
|
||||
color: $treeCheckColor;
|
||||
}
|
||||
}
|
||||
& button {
|
||||
width: 80px;
|
||||
height: 40px;
|
||||
color: #fff;
|
||||
background: $treeCheckColor;
|
||||
border-radius: 5px;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-list {
|
||||
background: #f7f7f7;
|
||||
height: calc(100% - 46px);
|
||||
margin-top:46px;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
.search-head {
|
||||
height: 46px;
|
||||
line-height: 46px;
|
||||
padding: 0 10px 0 15px;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
box-sizing: border-box;
|
||||
background: #f7f7f7;
|
||||
input {
|
||||
background: #efefef;
|
||||
-webkit-appearance: none;
|
||||
outline: none;
|
||||
width: calc(100% - 70px);
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 15px;
|
||||
border-radius: 28px;
|
||||
font-size: 14px;
|
||||
&::-webkit-input-placeholder {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
span {
|
||||
margin-left: 5px;
|
||||
color: #272829;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
width: 18px;
|
||||
line-height: 18px;
|
||||
height: 18px;
|
||||
position: absolute;
|
||||
right: 60px;
|
||||
top: 15px;
|
||||
}
|
||||
}
|
||||
.search-item {
|
||||
background: #fff;
|
||||
height: 46px;
|
||||
line-height: 46px;
|
||||
padding-left: 12px;
|
||||
a {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
color: #333;
|
||||
outline: none;
|
||||
@extend .my-border_bottom;
|
||||
}
|
||||
&_text {
|
||||
margin-left: 2px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
&_group {
|
||||
float: right;
|
||||
color: #808080;
|
||||
font-size: 12px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-popup {
|
||||
position: fixed;
|
||||
background: #fff;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
transform: translateY(0);
|
||||
backface-visibility: hidden;
|
||||
transition: 0.2s ease-in;
|
||||
width: 100%;
|
||||
height: 75%;
|
||||
z-index: 105;
|
||||
border-radius: 8px 8px 0 0;
|
||||
&_show {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
&_hide {
|
||||
transform: translateY(0%);
|
||||
}
|
||||
&_header {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@extend .my-border_bottom;
|
||||
h4 {
|
||||
font-size: 18px;
|
||||
margin: 12px 0 5px;
|
||||
color: #272829;
|
||||
text-align: center;
|
||||
}
|
||||
h6 {
|
||||
color: #595959;
|
||||
font-size: 13px;
|
||||
margin: 5px 0 15px;
|
||||
text-align: center;
|
||||
}
|
||||
& .close-btn {
|
||||
position: absolute;
|
||||
padding: 5px;
|
||||
right: 3%;
|
||||
top: 10px;
|
||||
}
|
||||
}
|
||||
&_body {
|
||||
overflow-y: auto;
|
||||
height: calc(100% - 70px);
|
||||
padding: 12px 12px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
&_ul {
|
||||
@extend .fix;
|
||||
li {
|
||||
background: #f4f8ff;
|
||||
float: left;
|
||||
margin: 4px;
|
||||
width: calc(33.333% - 8px);
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
h4,
|
||||
h6 {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
h4 {
|
||||
font-size: 16px;
|
||||
margin: 6px 0;
|
||||
color: #272829;
|
||||
text-align: center;
|
||||
}
|
||||
h6 {
|
||||
color: #595959;
|
||||
font-size: 12px;
|
||||
height: 16px;
|
||||
margin: 6px 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-box {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
box-sizing: border-box;
|
||||
padding: 2px;
|
||||
top: 2px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.bg-arrow_grey {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAABE0lEQVRYR+3VMQ6CMBQG4PdImPUIHsEjeBRZCJ30KDqVsNSjeASPwBF0Y6KmiSQM0NfXdiDmsZDQ0n79Wx4IG7twYx4QELUjkpAkRCVAtcsZ+p+EjDH7YRj2SqmeWhWnveu6Y1mWfVVV76X3Vs9Q27Y3RLyM41gppR6cSdf6aq3PRVEYa+29aZorC+RWYq19AsAuB2rCAMAHEU91Xb9YINc5FyoU4+YkP/tUFAcTBEpJiosJBsWgYjAsEAcVi2GDQlApmCiQD5WKiQYtodwzV/SoOkMVWPKz9w0wLwm/ft6iR2GSEpoGn6HAV4FDMFlAbhCt9cHdc/yIk7YsdNWcfgKi0pKEJCEqAapdzpAkRCVAtX8BqXjGJWAVZBoAAAAASUVORK5CYII=");
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
zoom: 0.3;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.conta-wrap {
|
||||
position: relative;
|
||||
.tree-item {
|
||||
padding: 12px;
|
||||
color: #333;
|
||||
position: relative;
|
||||
a {
|
||||
color: #333;
|
||||
display: inline-block;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
label {
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
margin-left: 2px;
|
||||
}
|
||||
&_group {
|
||||
background: $treeCheckGroupColor;
|
||||
@extend .my-border_bottom;
|
||||
}
|
||||
|
||||
&_arrow {
|
||||
&::before {
|
||||
@extend .bg-arrow_grey;
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: 2%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto 0;
|
||||
transition: 300ms cubic-bezier(0.47, 0, 0.745, 0.715);
|
||||
}
|
||||
}
|
||||
&_open,
|
||||
&_close {
|
||||
@extend .tree-item_arrow;
|
||||
}
|
||||
&_close::before {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.my-checkbox-core {
|
||||
border-radius: 3px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
background-color: #fff;
|
||||
border: 1px solid $treeCheckBorderColor;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.my-checkbox-core.active {
|
||||
background-color: $treeCheckColor;
|
||||
border-color: $treeCheckColor;
|
||||
&::after {
|
||||
border: 2px solid transparent;
|
||||
border-left: 0;
|
||||
border-top: 0;
|
||||
content: " ";
|
||||
top: 3px;
|
||||
left: 7px;
|
||||
position: absolute;
|
||||
width: 4px;
|
||||
height: 8px;
|
||||
-webkit-transform: rotate(45deg) scale(0);
|
||||
transform: rotate(45deg) scale(0);
|
||||
transition: -webkit-transform 0.2s;
|
||||
-webkit-transition: -webkit-transform 0.2s;
|
||||
transition: transform 0.2s;
|
||||
transition: transform 0.2s, -webkit-transform 0.2s;
|
||||
border-color: #fff;
|
||||
-webkit-transform: rotate(45deg) scale(1);
|
||||
transform: rotate(45deg) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.my-border_bottom::after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
color: #d9d9d9;
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
height: 1px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
-webkit-transform: scaleY(0.5);
|
||||
transform: scaleY(0.5);
|
||||
transition: 0;
|
||||
}
|
||||
|
||||
.my-border_top::after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
color: #d9d9d9;
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
border-top: 1px solid #d9d9d9;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
-webkit-transform: scaleY(0.5);
|
||||
transform: scaleY(0.5);
|
||||
transition: 0;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 300ms ease-in-out;
|
||||
}
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -0,0 +1,505 @@
|
||||
<template>
|
||||
<div>
|
||||
<div :id="domId" :class="['tree-popup', isPopupShow ? 'tree-popup_show' : 'tree-popup_hide']">
|
||||
<div class="tree-popup_header">
|
||||
<div class="my-tab_btn fix">
|
||||
<div
|
||||
:class="{
|
||||
'item-tab_btn': true,
|
||||
active: firstTabIndex == tindex,
|
||||
}"
|
||||
@click="tabBtnChange(tindex)"
|
||||
v-for="(titem, tindex) in firstTabData"
|
||||
>
|
||||
{{ titem }}
|
||||
</div>
|
||||
<div class="my-tab_slider" v-if="firstTabData.length > 0" :style="tabSliderStyle">
|
||||
<div class="indicator"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-tab_card fix" v-if="secondTabData.length > 1">
|
||||
<div class="item-wrap">
|
||||
<div
|
||||
:class="{
|
||||
'item-tab_card': true,
|
||||
active: secondTabIndex == tindex,
|
||||
}"
|
||||
@click="tabCardChange(tindex)"
|
||||
v-for="(titem, tindex) in secondTabData"
|
||||
>
|
||||
{{ titem }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-box" @click="isSearchShow = !isSearchShow">
|
||||
<svg t="1626398915347" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4152" width="16" height="16">
|
||||
<path
|
||||
d="M921.3 874L738.1 690.8c51.3-62.6 82.1-142.5 82.1-229.6 0-200.1-162.8-363-363-363-200.1 0-363 162.8-363 363s162.8 363 363 363c87 0 167-30.8 229.6-82.1L870 925.3c7.1 7.1 16.4 10.6 25.7 10.6s18.6-3.5 25.7-10.6c14.1-14.2 14.1-37.2-0.1-51.3zM166.8 461.2c0-160.1 130.3-290.4 290.4-290.4s290.4 130.3 290.4 290.4-130.3 290.4-290.4 290.4-290.4-130.3-290.4-290.4z"
|
||||
p-id="4153"
|
||||
fill="#808080"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="close-btn" @click="popupColse">
|
||||
<svg t="1626397122082" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3133" width="14" height="14">
|
||||
<path d="M877.216 491.808" p-id="3134" fill="#595959"></path>
|
||||
<path
|
||||
d="M575.328 510.496 946.784 140.672c17.568-17.504 17.664-45.824 0.192-63.424-17.504-17.632-45.792-17.664-63.36-0.192L512.032 446.944 143.712 77.216C126.304 59.712 97.92 59.648 80.384 77.12 62.848 94.624 62.816 123.008 80.288 140.576l368.224 369.632L77.216 879.808c-17.568 17.504-17.664 45.824-0.192 63.424 8.736 8.8 20.256 13.216 31.776 13.216 11.424 0 22.848-4.352 31.584-13.056l371.36-369.696 371.68 373.088C892.192 955.616 903.68 960 915.168 960c11.456 0 22.912-4.384 31.648-13.088 17.504-17.504 17.568-45.824 0.096-63.392L575.328 510.496 575.328 510.496zM575.328 510.496"
|
||||
p-id="3135"
|
||||
fill="#595959"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tree-popup_body">
|
||||
<tree v-if="!isSearchShow" :treeData="currentTree" :userids="tempUserIds"></tree>
|
||||
<div v-else class="search-list">
|
||||
<div class="search-head" :style="searchHeadStyle">
|
||||
<input type="text" placeholder="请输入关键字" v-model="searchText" />
|
||||
<div class="close-icon" v-show="searchText" @click="searchText = null">
|
||||
<svg t="1626484787749" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2043" width="18" height="18">
|
||||
<path
|
||||
d="M512 51.2C258.048 51.2 51.2 258.048 51.2 512s206.848 460.8 460.8 460.8 460.8-206.848 460.8-460.8S765.952 51.2 512 51.2z m181.248 612.352c8.192 8.192 8.192 20.48 0 28.672-4.096 4.096-9.216 6.144-14.336 6.144s-10.24-2.048-14.336-6.144L512 540.672 360.448 693.248c-4.096 4.096-9.216 6.144-14.336 6.144s-10.24-2.048-14.336-6.144c-8.192-8.192-8.192-20.48 0-28.672L483.328 512 330.752 360.448c-8.192-8.192-8.192-20.48 0-28.672s20.48-8.192 28.672 0L512 483.328l151.552-151.552c8.192-8.192 20.48-8.192 28.672 0s8.192 20.48 0 28.672L540.672 512l152.576 151.552z"
|
||||
p-id="2044"
|
||||
fill="#cccccc"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<span @click="isSearchShow = false">取消</span>
|
||||
</div>
|
||||
<div class="search-item" v-for="item in searchList">
|
||||
<a href="javascript:;" @click="checkTreeById(item.id, !item.isCheck)">
|
||||
<span :class="['my-checkbox-core', item.isCheck ? 'active' : '']"></span>
|
||||
<span class="search-item_text">{{ item.name }}</span>
|
||||
|
||||
<span class="search-item_group">{{ item.group }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tree-popup_footer fix">
|
||||
<div class="check-box align-middle" v-show="!isSearchShow">
|
||||
<label @click="saveAllCheck">
|
||||
<span :class="['my-checkbox-core', isAllTreeCheck ? 'active' : '']"></span>
|
||||
<span class="check-box_text">全选</span>
|
||||
</label>
|
||||
</div>
|
||||
<p class="fl align-middle" @click="isUserModalShow = true">
|
||||
已选人数:
|
||||
<span>{{ tempUserIds.length }}</span>
|
||||
</p>
|
||||
<button v-show="isSearchShow" class="fr align-middle" @click="isSearchShow = false">确认</button>
|
||||
<button v-show="!isSearchShow" class="fr align-middle" @click="submit">提交</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isModalShow" class="my-modal" @click="popupColse"></div>
|
||||
<div :class="['user-popup', isUserModalShow ? 'user-popup_show' : 'user-popup_hide']">
|
||||
<div class="user-popup_header">
|
||||
<h4>已选人员</h4>
|
||||
<h6>共{{ tempUserToNames.length }}人</h6>
|
||||
<div class="close-btn" @click="isUserModalShow = false">
|
||||
<svg t="1626397122082" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3133" width="14" height="14">
|
||||
<path d="M877.216 491.808" p-id="3134" fill="#595959"></path>
|
||||
<path
|
||||
d="M575.328 510.496 946.784 140.672c17.568-17.504 17.664-45.824 0.192-63.424-17.504-17.632-45.792-17.664-63.36-0.192L512.032 446.944 143.712 77.216C126.304 59.712 97.92 59.648 80.384 77.12 62.848 94.624 62.816 123.008 80.288 140.576l368.224 369.632L77.216 879.808c-17.568 17.504-17.664 45.824-0.192 63.424 8.736 8.8 20.256 13.216 31.776 13.216 11.424 0 22.848-4.352 31.584-13.056l371.36-369.696 371.68 373.088C892.192 955.616 903.68 960 915.168 960c11.456 0 22.912-4.384 31.648-13.088 17.504-17.504 17.568-45.824 0.096-63.392L575.328 510.496 575.328 510.496zM575.328 510.496"
|
||||
p-id="3135"
|
||||
fill="#595959"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-popup_body">
|
||||
<ul class="user-popup_ul">
|
||||
<li v-for="item in tempUserToNames">
|
||||
<h4>{{ item.name }}</h4>
|
||||
<h6>{{ item.group }}</h6>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isUserModalShow" style="z-index: 103" class="my-modal" @click="isUserModalShow = false"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import { Toast } from "vant";
|
||||
require("./index.scss");
|
||||
import tree from "./tree.vue";
|
||||
export default {
|
||||
components: { tree },
|
||||
props: {
|
||||
treeData: {
|
||||
type: Array,
|
||||
default: [],
|
||||
},
|
||||
userids: {
|
||||
type: Array,
|
||||
default: [],
|
||||
},
|
||||
},
|
||||
model: {
|
||||
prop: "userids",
|
||||
event: "change",
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
checkTreeById: this.checkTreeById,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tempUserIds: [],
|
||||
firstTabIndex: 0,
|
||||
secondTabIndex: 0,
|
||||
tabData: [],
|
||||
currentTree: [],
|
||||
tabSliderStyle: {},
|
||||
searchHeadStyle: {},
|
||||
isPopupShow: false,
|
||||
isModalShow: false,
|
||||
isUserModalShow: false,
|
||||
isAllTreeCheck: false,
|
||||
isSearchShow: false,
|
||||
searchText: null,
|
||||
cacheMap: new Map(),
|
||||
duplicateIds: [],
|
||||
domId: "dom_" + new Date().getTime(),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
firstTabData() {
|
||||
if (this.treeData.length > 0) return this.treeData.map(x => x.name);
|
||||
else return [];
|
||||
},
|
||||
secondTabData() {
|
||||
if (this.treeData.length == 0) return [];
|
||||
return this.treeData[this.firstTabIndex].data.map(x => x.name);
|
||||
},
|
||||
currentTreeFlatten() {
|
||||
const flatten = (data, pid, pname) => {
|
||||
return data.reduce((arr, { id, name, children = [], type, isCheck }) => arr.concat([{ id, name, pid, type, isCheck, group: pname }], flatten(children, id, name)), []);
|
||||
};
|
||||
if (!this.currentTree) return [];
|
||||
var newArr = flatten(this.currentTree, 0, "");
|
||||
return newArr;
|
||||
},
|
||||
searchList() {
|
||||
const _this = this;
|
||||
let flattenArr = _this.currentTreeFlatten;
|
||||
flattenArr = flattenArr.filter(x => x.type == 2);
|
||||
if (!_this.searchText) return flattenArr;
|
||||
else return flattenArr.filter(x => x.name.indexOf(_this.searchText) > -1);
|
||||
},
|
||||
tempUserToNames() {
|
||||
const _this = this;
|
||||
let flattenArr = _this.currentTreeFlatten;
|
||||
var arr = flattenArr.filter(x => _this.tempUserIds.includes(x.id));
|
||||
return arr;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.tabData = this.treeData.map(x => x.name);
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
show() {
|
||||
let timer = null;
|
||||
if (this.firstTabData.length == 0) {
|
||||
Toast.loading({ forbidClick: true });
|
||||
timer = setTimeout(() => {
|
||||
this.show();
|
||||
}, 100);
|
||||
return;
|
||||
} else {
|
||||
Toast.clear();
|
||||
if (!!timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
this.isModalShow = this.isPopupShow = true;
|
||||
this.tabBtnChange(0);
|
||||
var parentId = "#" + this.domId;
|
||||
setTimeout(() => {
|
||||
let treeH = document.querySelector(parentId + ".tree-popup").offsetHeight,
|
||||
headerH = document.querySelector(parentId + " .tree-popup_header").offsetHeight;
|
||||
document.querySelector(parentId + " .tree-popup_body").style.height = treeH - headerH - 60 + "px";
|
||||
this.searchHeadStyle = { top: headerH + "px" };
|
||||
}, 0);
|
||||
},
|
||||
popupColse() {
|
||||
this.isPopupShow = false;
|
||||
this.firstTabIndex = 0;
|
||||
this.secondTabIndex = 0;
|
||||
setTimeout(() => {
|
||||
this.isModalShow = false;
|
||||
}, 250);
|
||||
},
|
||||
tabBtnChange(index) {
|
||||
this.firstTabIndex = index;
|
||||
this.getTabSliderStyle(index);
|
||||
this.changeEmpty();
|
||||
this.tabCardChange(0);
|
||||
},
|
||||
tabCardChange(index) {
|
||||
const _this = this;
|
||||
var wrap = document.querySelector("#" + this.domId + " .item-wrap");
|
||||
var tabs = document.querySelectorAll("#" + this.domId + " .item-tab_card");
|
||||
let currentItem = tabs[index];
|
||||
var pageX = document.documentElement.clientWidth,
|
||||
blockW = currentItem.scrollWidth,
|
||||
moveX = 0;
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const element = tabs[i];
|
||||
if (i > index) break;
|
||||
moveX += element.scrollWidth;
|
||||
}
|
||||
var left = moveX - pageX / 2 + blockW / 2;
|
||||
wrap.scrollLeft = left;
|
||||
|
||||
this.secondTabIndex = index;
|
||||
this.isAllTreeCheck = false;
|
||||
this.isSearchShow = false;
|
||||
this.initTreeData();
|
||||
},
|
||||
initTreeData() {
|
||||
const _this = this;
|
||||
const checkTree = tree => {
|
||||
_this.currentTree = tree;
|
||||
_this.tempUserIds = JSON.parse(JSON.stringify(_this.userids));
|
||||
let duplArr = _this.duplicateIds.map(x => x.id);
|
||||
_this.tempUserIds.forEach(element => {
|
||||
let id = element;
|
||||
if (duplArr.length > 0 && duplArr.includes(element)) {
|
||||
let parent = _this.duplicateIds.find(x => x.id == element);
|
||||
//处理重复id逻辑,仅选择第一个
|
||||
parent && (id += "," + parent.pid[0]);
|
||||
}
|
||||
_this.checkTreeById(id, true);
|
||||
});
|
||||
};
|
||||
let first = _this.firstTabIndex,
|
||||
second = _this.secondTabIndex,
|
||||
cacheMap = _this.cacheMap,
|
||||
tData = _this.treeData;
|
||||
var tree = tData[first].data[second].data;
|
||||
var cacheKey = first + "," + second;
|
||||
if (!!tree && typeof tree === "function") {
|
||||
var cacheData = cacheMap.get(cacheKey);
|
||||
if (!cacheData) {
|
||||
tree().then(data => {
|
||||
_this.duplicateIds = _this.dupTreeIds(data);
|
||||
var jtree = JSON.parse(JSON.stringify(data));
|
||||
cacheMap.set(cacheKey, jtree);
|
||||
var ftree = _this.formatTree(jtree);
|
||||
checkTree(ftree);
|
||||
});
|
||||
} else {
|
||||
var ftree = _this.formatTree(cacheData);
|
||||
checkTree(ftree);
|
||||
}
|
||||
} else {
|
||||
_this.duplicateIds = _this.dupTreeIds(tree);
|
||||
var jtree = JSON.parse(JSON.stringify(tree));
|
||||
var ftree = _this.formatTree(jtree);
|
||||
checkTree(ftree);
|
||||
}
|
||||
},
|
||||
getTabSliderStyle(index) {
|
||||
var dom = document.querySelectorAll(".item-tab_btn")[index];
|
||||
if (!dom) return;
|
||||
this.tabSliderStyle = {
|
||||
width: dom.offsetWidth + "px",
|
||||
transform: `translateX(${dom.offsetLeft}px)`,
|
||||
};
|
||||
},
|
||||
saveAllCheck() {
|
||||
const _this = this;
|
||||
_this.isAllTreeCheck = !_this.isAllTreeCheck;
|
||||
const recursionCheckAll = (tree, checked) => {
|
||||
tree.forEach(element => {
|
||||
element.isCheck = checked;
|
||||
if (typeof element.children !== "undefined" && element.children.length > 0) recursionCheckAll(element.children, checked);
|
||||
});
|
||||
};
|
||||
recursionCheckAll(_this.currentTree, _this.isAllTreeCheck);
|
||||
_this.getTreeCheckIds();
|
||||
},
|
||||
changeEmpty() {
|
||||
this.tempUserIds = [];
|
||||
},
|
||||
submit() {
|
||||
const _this = this;
|
||||
let objArr = [],
|
||||
idsArr = this.tempUserIds.map(x => _this.getSplitId(x));
|
||||
idsArr = Array.from(new Set(idsArr));
|
||||
this.$emit("change", idsArr);
|
||||
function delKey(obj, ...args) {
|
||||
args.forEach(v => delete obj[v]);
|
||||
return obj;
|
||||
}
|
||||
idsArr.forEach(element => {
|
||||
let findObj = _this.currentTreeFlatten.find(x => _this.getSplitId(x.id) == element);
|
||||
delKey(findObj, "id", "isCheck", "pid", "type");
|
||||
objArr.push({ ...findObj, id: element });
|
||||
});
|
||||
this.$emit("on-submit", objArr);
|
||||
this.popupColse();
|
||||
},
|
||||
getSplitId(strId) {
|
||||
if (typeof strId === "string" && strId.indexOf(",") != -1) return parseInt(strId.split(",")[0]);
|
||||
else return strId;
|
||||
},
|
||||
//#region treeOpera
|
||||
formatTree(tree, level = 0) {
|
||||
const _this = this;
|
||||
tree.forEach(element => {
|
||||
var findObj = _this.duplicateIds.map(x => x.id).find(x => x == element.id);
|
||||
if (!!findObj) element.id = element.id + "," + element.pid; //重复id加上组id以免重复
|
||||
element.show = level == 0;
|
||||
element.isCheck = false;
|
||||
element.type = 1;
|
||||
if (typeof element.children !== "undefined" && element.children.length > 0) element.children = _this.formatTree(element.children, level + 1);
|
||||
else {
|
||||
// element.id = element.id + "," + element.pid; //防止用户在多个组重复id,加上组id以免重复
|
||||
element.type = 2;
|
||||
}
|
||||
});
|
||||
return tree;
|
||||
},
|
||||
flattenTree(tree) {
|
||||
const flatten = (data, pid) => {
|
||||
return data.reduce((arr, { id, name, children = [], type }) => arr.concat([{ id, name, type, pid }], flatten(children, id)), []);
|
||||
};
|
||||
return flatten(tree, 0);
|
||||
},
|
||||
dupTreeIds(tree) {
|
||||
//一个人可能在多个组,取出这些id
|
||||
const flatten = (data, pid) => {
|
||||
return data.reduce((arr, { id, name, children = [] }) => arr.concat([{ id, name, pid }], flatten(children, id)), []);
|
||||
};
|
||||
// var flattenArr = flatten(tree, 0);
|
||||
|
||||
var flattenArr = this.flattenTree(tree);
|
||||
var ctnArr = flattenArr.reduce((arr, { id, pid }) => {
|
||||
var findObj = arr.find(x => x.id == id);
|
||||
if (!!findObj) {
|
||||
findObj.count += 1;
|
||||
findObj.pid.push(pid);
|
||||
} else arr.push({ id, count: 1, pid: [pid] });
|
||||
return arr;
|
||||
}, []);
|
||||
// var dupArr = ctnArr.filter((x) => x.count > 1).map((x) => x.id);
|
||||
var dupArr = ctnArr.filter(x => x.count > 1);
|
||||
return dupArr;
|
||||
},
|
||||
findTreePids(id, tree) {
|
||||
const _this = this;
|
||||
var flattenArr = _this.flattenTree(tree);
|
||||
var arr = [];
|
||||
while (id > 0) {
|
||||
var findObj = flattenArr.find(x => x.id == id);
|
||||
if (findObj) {
|
||||
id = findObj.pid;
|
||||
arr.push(id);
|
||||
} else {
|
||||
id = -1;
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
},
|
||||
findTreeChildIds(id, tree) {
|
||||
const _this = this;
|
||||
const recursionChildIds = tree => {
|
||||
let arrIds = [];
|
||||
for (const key in tree) {
|
||||
const element = tree[key];
|
||||
arrIds.push(element.id);
|
||||
if (typeof element.children !== "undefined" && element.children) {
|
||||
var childIds = recursionChildIds(element.children);
|
||||
arrIds = [...arrIds, ...childIds];
|
||||
}
|
||||
}
|
||||
return arrIds;
|
||||
};
|
||||
let childTree = [];
|
||||
if (id == 0) childTree = tree;
|
||||
else {
|
||||
var ctree = _this.findTreeByid(id, tree);
|
||||
if (ctree && typeof ctree.children !== "undefined" && ctree.children) childTree = ctree.children;
|
||||
else return [];
|
||||
}
|
||||
|
||||
var ids = recursionChildIds(childTree);
|
||||
return ids;
|
||||
},
|
||||
findTreeByid(id, tree) {
|
||||
const _this = this;
|
||||
var findTree;
|
||||
for (const key in tree) {
|
||||
const element = tree[key];
|
||||
if (element.id == id) findTree = element;
|
||||
if (typeof element.children !== "undefined" && element.children && !findTree) {
|
||||
findTree = _this.findTreeByid(id, element.children);
|
||||
}
|
||||
}
|
||||
return findTree;
|
||||
},
|
||||
findTreeByPid(id, tree) {
|
||||
const _this = this;
|
||||
var findId;
|
||||
for (const key in tree) {
|
||||
const element = tree[key];
|
||||
if (element.id == id) {
|
||||
findId = element.pid;
|
||||
}
|
||||
if (typeof element.children !== "undefined" && element.children && !findId) {
|
||||
findId = _this.findTreeByPid(id, element.children);
|
||||
}
|
||||
}
|
||||
return findId;
|
||||
},
|
||||
checkTreeById(id, checked) {
|
||||
const _this = this;
|
||||
var pids = this.findTreePids(id, this.currentTree);
|
||||
var cids = this.findTreeChildIds(id, this.currentTree);
|
||||
var idArr = [...cids, id];
|
||||
checked && (idArr = [...pids, ...idArr]);
|
||||
idArr.forEach(element => {
|
||||
var t = this.findTreeByid(element, this.currentTree);
|
||||
t && (t.isCheck = checked);
|
||||
});
|
||||
|
||||
if (!checked) {
|
||||
pids.forEach(element => {
|
||||
var t = _this.findTreeByid(element, this.currentTree);
|
||||
if (!t || typeof t.children === "undefined") return;
|
||||
var findIndex = t.children.findIndex(x => x.isCheck && x.id != id);
|
||||
findIndex === -1 && (t.isCheck = false);
|
||||
});
|
||||
}
|
||||
this.getTreeCheckIds();
|
||||
},
|
||||
getTreeCheckIds() {
|
||||
const _this = this;
|
||||
var isEndWithType = true; //是否只返回最末端的结点(type=2)
|
||||
const recursionCheckIds = tree => {
|
||||
let arrIds = [];
|
||||
for (const key in tree) {
|
||||
const element = tree[key];
|
||||
if (element.isCheck && (isEndWithType ? element.type == 2 : true)) arrIds.push(element.id);
|
||||
if (typeof element.children !== "undefined" && element.children) {
|
||||
var childIds = recursionCheckIds(element.children);
|
||||
arrIds = [...arrIds, ...childIds];
|
||||
}
|
||||
}
|
||||
return arrIds;
|
||||
};
|
||||
var t = recursionCheckIds(_this.currentTree);
|
||||
this.tempUserIds = t;
|
||||
},
|
||||
//#endregion
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<ul class="conta-wrap">
|
||||
<li v-for="(item, index) in treeData">
|
||||
<div
|
||||
:class="['tree-item', getItemClass(item)]"
|
||||
:style="{ 'padding-left': 30 * level + 12 + 'px' }"
|
||||
@click="itemClick(item)"
|
||||
>
|
||||
<a
|
||||
href="javascript:;"
|
||||
style="dispaly: block"
|
||||
@click.stop="itemCheck(item, $event)"
|
||||
>
|
||||
<input type="checkbox" v-model="item.isCheck" />
|
||||
<span
|
||||
:class="[
|
||||
'my-checkbox-core',
|
||||
item.isCheck ? 'active' : '',
|
||||
]"
|
||||
></span>
|
||||
<label>{{ item.name }}</label>
|
||||
</a>
|
||||
</div>
|
||||
<transition name="fade">
|
||||
<tree
|
||||
v-if="item.show"
|
||||
:userids="userids"
|
||||
:treeData="item.children"
|
||||
:level="level + 1"
|
||||
></tree>
|
||||
</transition>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
require("./index.scss");
|
||||
export default {
|
||||
name: "tree",
|
||||
inject: ["checkTreeById"],
|
||||
props: {
|
||||
level: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
treeData: {
|
||||
type: Array,
|
||||
default: [],
|
||||
},
|
||||
userids: {
|
||||
type: Array,
|
||||
default: [],
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getItemClass(item) {
|
||||
if (item.type == 2) return;
|
||||
let className = " tree-item_" + (item.show ? "open" : "close");
|
||||
className += this.level === 0 ? " tree-item_group " : "";
|
||||
return className;
|
||||
},
|
||||
itemCheck(item, e) {
|
||||
if (
|
||||
typeof e !== "undefined" &&
|
||||
e.target.tagName.toLowerCase() === "input"
|
||||
)
|
||||
return;
|
||||
this.checkTreeById(item.id, !item.isCheck);
|
||||
},
|
||||
itemClick(item) {
|
||||
if (item.type == 2) {
|
||||
// this.itemCheck(item);
|
||||
this.checkTreeById(item.id, !item.isCheck);
|
||||
} else if (item.type == 1) {
|
||||
item.show = !item.show;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<Modal v-model="modal" :title="name" @on-visible-change="showChange" width="70%" footer-hide>
|
||||
<Button class="js_copy_code" data-clipboard-action="copy" :data-clipboard-text="currentHtml">复制代码</Button>
|
||||
<div v-highlight>
|
||||
<pre v-show="isLoad">
|
||||
<code id="test" class="html"></code>
|
||||
</pre>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { load } from "../../assets/js/code";
|
||||
export default {
|
||||
props: {
|
||||
name: String,
|
||||
url: String,
|
||||
type: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
modal: false,
|
||||
isLoad: false,
|
||||
currentHtml: null,
|
||||
clipboard: null,
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
const _this = this;
|
||||
this.modal = true;
|
||||
this.isLoad = false;
|
||||
let { html, encode } = await load(_this.url);
|
||||
$("#test").attr("class", _this.type).html(encode);
|
||||
this.isLoad = true;
|
||||
this.currentHtml = html;
|
||||
this.clipboard = new ClipboardJS(".js_copy_code");
|
||||
this.clipboard.on("success", function (e) {
|
||||
_this.$Message.success("copy success!");
|
||||
e.clearSelection();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
showChange(state) {
|
||||
if (!state) this.destroy();
|
||||
},
|
||||
destroy() {
|
||||
this.clipboard.destroy();
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(this.$el);
|
||||
}, 300);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,24 @@
|
||||
import Vue from "vue";
|
||||
import codemodal from "./code_modal.vue";
|
||||
import { Message } from "view-design";
|
||||
export default props => {
|
||||
// 创建实例
|
||||
const vm = new Vue({
|
||||
render(h) {
|
||||
// h为createElement,返回VNode虚拟Node
|
||||
return h(codemodal, { props });
|
||||
}
|
||||
}).$mount();
|
||||
|
||||
// 手动挂载实例到body
|
||||
document.body.appendChild(vm.$el);
|
||||
|
||||
const comp = vm.$children[0];
|
||||
// 销毁方法
|
||||
// comp.remove = function() {
|
||||
// document.body.removeChild(vm.$el);
|
||||
// vm.$destroy();
|
||||
// };
|
||||
// 返回组件
|
||||
return comp;
|
||||
};
|
||||
@@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 富文本编辑器 -->
|
||||
<quill-editor v-model="content"
|
||||
ref="myTextEditor"
|
||||
:options="editorOption"
|
||||
@change="onChange"
|
||||
>
|
||||
<div id="toolbar" slot="toolbar">
|
||||
<span class="ql-formats"><button type="button" class="ql-bold"></button></span>
|
||||
<span class="ql-formats"><button type="button" class="ql-italic"></button></span>
|
||||
<span class="ql-formats"><button type="button" class="ql-underline"></button></span>
|
||||
<span class="ql-formats"><button type="button" class="ql-strike"></button></span>
|
||||
<!-- 有序和无序 -->
|
||||
<span class="ql-formats"><button type="button" class="ql-list" value="ordered"></button></span>
|
||||
<span class="ql-formats"><button type="button" class="ql-list" value="bullet"></button></span>
|
||||
<!-- 缩进 -->
|
||||
<span class="ql-formats"><button type="button" class="ql-indent" value="-1"></button></span>
|
||||
<span class="ql-formats"><button type="button" class="ql-indent" value="+1"></button></span>
|
||||
<span class="ql-formats"> <button type="button" class="ql-direction" value="rtl"></button></span>
|
||||
<!-- 自定义下拉 -->
|
||||
<span class="ql-formats">
|
||||
<select class="ql-size" id="ql-size">
|
||||
<option value="small">小号</option>
|
||||
<option value="">正常</option>
|
||||
<option value="large">大号</option>
|
||||
<option value="huge">特大</option>
|
||||
</select>
|
||||
<!-- <i-select size="small" @on-change="mychange">
|
||||
<i-option value="small">小号</i-option>
|
||||
<i-option value="large">正常</i-option>
|
||||
<i-option value="huge">特大</i-option>
|
||||
</i-select> -->
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<select class="ql-header">
|
||||
<option value="1">标题1</option>
|
||||
<option value="2">标题2</option>
|
||||
<option value="3">标题3</option>
|
||||
<option value="4">标题4</option>
|
||||
<option value="5">标题5</option>
|
||||
<option value="6">标题6</option>
|
||||
<option selected="selected">正文</option>
|
||||
</select>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<select class="ql-color" v-model="fontColor">
|
||||
<option v-for="item in colorArry" :key="item" :value="'#'+item"></option>
|
||||
</select>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<select class="ql-background" v-model="bgColor">
|
||||
<option v-for="item in colorArry" :key="item" :value="'#'+item"></option>
|
||||
</select>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<select class="ql-font">
|
||||
<option selected="selected"></option>
|
||||
<option value="serif"></option>
|
||||
<option value="monospace"></option>
|
||||
</select>
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<select class="ql-align">
|
||||
<option selected="selected"></option>
|
||||
<option value="center"></option>
|
||||
<option value="right"></option>
|
||||
<option value="justify"></option>
|
||||
</select>
|
||||
</span>
|
||||
<!-- <span class="ql-formats">
|
||||
<button type="button" @click="isLink=true" class="ql-link"></button>
|
||||
</span> -->
|
||||
<span class="ql-formats">
|
||||
<button type="button" class="my-formats-image">
|
||||
<Upload
|
||||
:action="formItem.uploadUrl"
|
||||
:on-success="handleSuccess"
|
||||
:data="uploadData"
|
||||
accept="image/gif,image/jpeg,image/jpg,image/png">
|
||||
<Icon type="image" size="16"></Icon>
|
||||
</Upload>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</quill-editor>
|
||||
<!-- 添加链接 -->
|
||||
<Modal v-model="isLink" title="添加链接" width="450px">
|
||||
<i-form :label-width="85" class="myform" ref="formItem.link" :model="formItem.link">
|
||||
<Form-Item label="链接地址:" prop="url">
|
||||
<i-input v-model="formItem.link.url" placeholder="请输入链接地址"></i-input>
|
||||
</Form-Item>
|
||||
<Form-Item label="链接名称:" prop="tit">
|
||||
<i-input v-model="formItem.link.tit" placeholder="请输入链接名称"></i-input>
|
||||
</Form-Item>
|
||||
</i-form>
|
||||
<div slot="footer">
|
||||
<i-Button type="primary" @click="addLinkSave()">提交</i-Button>
|
||||
<i-Button type="ghost" @click="myCancel('isLink','formItem.link')" style="margin-left: 8px">取消</i-Button>
|
||||
</div>
|
||||
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
// import {quillEditor} from 'vue-quill-editor'
|
||||
|
||||
export default {
|
||||
// components: {quillEditor},
|
||||
props: {
|
||||
// 提示内容
|
||||
placeholder:{
|
||||
type: String
|
||||
},
|
||||
/*编辑器内容*/
|
||||
value: {
|
||||
type: String
|
||||
},
|
||||
/*上传图片的地址*/
|
||||
uploadUrl: {
|
||||
type: String
|
||||
},
|
||||
readonly:Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
content:this.value,
|
||||
size:'',
|
||||
fontColor:"000000",
|
||||
bgColor:"ffffff",
|
||||
colorArry:['000000','e60000','ff9900','ffff00','008a00','0066cc','9933ff','ffffff','facccc','ffebcc','ffffcc','cce8cc','cce0f5','ebd6ff','bbbbbb','f06666','ffc266','ffff66','66b966','66a3e0','c285ff','888888','a10000','b26b00','b2b200','006100','0047b2','6b24b2','444444','5c0000','663d00','666600','003700','002966','3d1466'],
|
||||
editorOption: {
|
||||
modules: {
|
||||
toolbar: '#toolbar',
|
||||
},
|
||||
placeholder:this.placeholder || '请输入内容……',
|
||||
readOnly: this.readonly,
|
||||
},
|
||||
showCrop:false,
|
||||
isLink:false,
|
||||
isImage:false,
|
||||
formItem:{
|
||||
link:{
|
||||
url:'',
|
||||
tit:'',
|
||||
},
|
||||
uploadUrl:this.uploadUrl || '',
|
||||
},
|
||||
uploadData:{
|
||||
AppType:window.location.pathname.split('/')[1],
|
||||
},
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(){
|
||||
this.$emit('input', this.content)
|
||||
},
|
||||
myCancel(name, resetName) {
|
||||
var vm = this;
|
||||
vm[name] = false;
|
||||
if (resetName) {
|
||||
console.log(vm.$refs[resetName])
|
||||
this.$refs[resetName].resetFields();
|
||||
}
|
||||
},
|
||||
addLinkSave(){
|
||||
var _that=this;
|
||||
_that.isLink=false;
|
||||
_that.editor.focus();
|
||||
_that.editor.insertText(_that.editor.getSelection().index, ' ', '', true);
|
||||
_that.editor.insertText(_that.editor.getSelection().index-1, _that.formItem.link.tit+' ', 'link','http://'+ _that.formItem.link.url);
|
||||
},
|
||||
handleSuccess(res, file){
|
||||
this.editor.focus();
|
||||
console.log(res);
|
||||
var url=res.host+res.fileurl;
|
||||
this.editor.insertEmbed(this.editor.getSelection().index, 'image', url);
|
||||
},
|
||||
// onFocus(){
|
||||
// $(".ql-editor img").removeClass("active");
|
||||
// },
|
||||
},
|
||||
computed: {
|
||||
editor() {
|
||||
return this.$refs.myTextEditor.quill;
|
||||
}
|
||||
},
|
||||
|
||||
mounted(){
|
||||
var _that=this;
|
||||
// $(".ql-editor img").on('click',function(){
|
||||
// _that.editor.blur();
|
||||
// $(this).addClass("active");
|
||||
// })
|
||||
},
|
||||
watch: {
|
||||
'value'(newVal, oldVal) {
|
||||
if (this.editor) {
|
||||
if (newVal !== this.content) {
|
||||
this.content = newVal
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.ql-tooltip{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.my-formats-image{
|
||||
outline: none;
|
||||
ul.ivu-upload-list{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.ql-container .ql-editor {
|
||||
min-height: 20em;
|
||||
padding-bottom: 1em;
|
||||
max-height: 25em;
|
||||
}
|
||||
.ql-editor{
|
||||
position: relative;
|
||||
img{
|
||||
position: relative;
|
||||
}
|
||||
img::after {
|
||||
content:'123123';
|
||||
display: block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
position: absolute;
|
||||
top:0;right: 0;
|
||||
background: red;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<Modal v-model="modal" @on-visible-change="showChange" :title="title || '提示'">
|
||||
<Alert type="warning" class="mb-1" show-icon>
|
||||
<Icon type="ios-bulb-outline" :class="{ 'mt-5': !info }" slot="icon" :size="28"></Icon>
|
||||
<div v-if="msg" :class="['message-box', info ? '' : 'mt-10 mb-10 ml-10']">{{ msg }}</div>
|
||||
<span v-if="info" slot="desc">{{ info }}</span>
|
||||
</Alert>
|
||||
<div slot="footer">
|
||||
<Button type="primary" @click="ok">确定</Button>
|
||||
<Button @click="cancle">取消</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
title: String,
|
||||
msg: String,
|
||||
info: String,
|
||||
onOk: Function,
|
||||
onCancel: Function,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
modal: false,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.modal = true;
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
ok() {
|
||||
this.modal = false;
|
||||
typeof this.onOk === "function" && this.onOk();
|
||||
},
|
||||
cancle() {
|
||||
this.modal = false;
|
||||
typeof this.onCancel === "function" && this.onCancel();
|
||||
},
|
||||
showChange(state) {
|
||||
if (!state) this.destroy();
|
||||
},
|
||||
destroy() {
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(this.$el);
|
||||
}, 300);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.my-confirwm {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.message-box {
|
||||
font-size: 16px;
|
||||
color: #17233d;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,23 @@
|
||||
import Vue from "vue";
|
||||
import confirm from "./confirm.vue";
|
||||
export default props => {
|
||||
// 创建实例
|
||||
const vm = new Vue({
|
||||
render(h) {
|
||||
// h为createElement,返回VNode虚拟Node
|
||||
return h(confirm, { props });
|
||||
}
|
||||
}).$mount();
|
||||
|
||||
// 手动挂载实例到body
|
||||
document.body.appendChild(vm.$el);
|
||||
|
||||
const comp = vm.$children[0];
|
||||
// 销毁方法
|
||||
// comp.remove = function() {
|
||||
// document.body.removeChild(vm.$el);
|
||||
// vm.$destroy();
|
||||
// };
|
||||
// 返回组件
|
||||
return comp;
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<div :id="domId" :ref="domId" class="ztree"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
require("@public/libs/ztree/zTreeStyle.css");
|
||||
import "@public/libs/ztree/jquery.ztree.all.min.js";
|
||||
export default {
|
||||
props: {
|
||||
treeData: {
|
||||
type: Object,
|
||||
},
|
||||
setting: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
domId: "",
|
||||
treeSetting: {
|
||||
view: {
|
||||
dblClickExpand: false,
|
||||
showIcon: false,
|
||||
},
|
||||
check: {
|
||||
enable: false,
|
||||
},
|
||||
callback: {},
|
||||
},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.$watch("treeData", (val) => {
|
||||
if (val) {
|
||||
this.$nextTick(() => {
|
||||
this.initTree(this.treeData, this.domId);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
mounted() {
|
||||
this.domId = this.uuid();
|
||||
},
|
||||
methods: {
|
||||
uuid() {
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
|
||||
/[xy]/g,
|
||||
function (c) {
|
||||
var r = (Math.random() * 16) | 0,
|
||||
v = c == "x" ? r : (r & 0x3) | 0x8;
|
||||
return v.toString(16);
|
||||
}
|
||||
);
|
||||
},
|
||||
initTree(_data, _domId) {
|
||||
const _this = this;
|
||||
_this.treeSetting.callback.onClick = (
|
||||
event,
|
||||
treeId,
|
||||
treeNode,
|
||||
clickFlag
|
||||
) => {
|
||||
// console.log(event, treeId, treeNode, clickFlag);
|
||||
_this.$emit("on-tree-click", treeNode);
|
||||
};
|
||||
var zNodes = _this.openFistNode(_data);
|
||||
$.fn.zTree.init($("#" + _domId), _this.treeSetting, zNodes);
|
||||
var treeObj = $.fn.zTree.getZTreeObj(_domId);
|
||||
var nodes = treeObj.getNodes();
|
||||
if (nodes && nodes.length > 0) {
|
||||
setTimeout(function () {
|
||||
$("#" + nodes[0].tId + "_a").click();
|
||||
}, 0);
|
||||
}
|
||||
},
|
||||
openFistNode(_zNodes) {
|
||||
//展开一级节点
|
||||
if (_zNodes && _zNodes.length > 0) {
|
||||
for (var key in _zNodes) {
|
||||
if (_zNodes.hasOwnProperty(key)) {
|
||||
var element = _zNodes[key];
|
||||
element.open = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_zNodes.open = true;
|
||||
}
|
||||
return _zNodes;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<div :id="domId" :ref="domId" class="ztree"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
require("@public/libs/ztree/zTreeStyle.css");
|
||||
import "@public/libs/ztree/jquery.ztree.all.min.js";
|
||||
export default {
|
||||
props: {
|
||||
treeData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
setting: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
// 外部传入需要默认选中的节点id,不传则选中第一个一级节点
|
||||
selectId: {
|
||||
type: [Number, String],
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
domId: "",
|
||||
treeSetting: {
|
||||
view: {
|
||||
dblClickExpand: false,
|
||||
showIcon: false,
|
||||
},
|
||||
check: {
|
||||
enable: false,
|
||||
},
|
||||
callback: {},
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
treeData(val) {
|
||||
if (val && val.length) {
|
||||
this.$nextTick(() => {
|
||||
this.initTree(this.treeData, this.domId);
|
||||
});
|
||||
}
|
||||
},
|
||||
// 监听外部传入选中id变化,自动重新选中
|
||||
selectId() {
|
||||
this.$nextTick(() => {
|
||||
this.handleDefaultSelect();
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$watch("treeData", (val) => {
|
||||
if (val) {
|
||||
this.$nextTick(() => {
|
||||
this.initTree(this.treeData, this.domId);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
mounted() {
|
||||
this.domId = this.uuid();
|
||||
},
|
||||
methods: {
|
||||
getZtreeInstance() {
|
||||
return $.fn.zTree.getZTreeObj(this.domId);
|
||||
},
|
||||
uuid() {
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
|
||||
/[xy]/g,
|
||||
function (c) {
|
||||
var r = (Math.random() * 16) | 0,
|
||||
v = c == "x" ? r : (r & 0x3) | 0x8;
|
||||
return v.toString(16);
|
||||
}
|
||||
);
|
||||
},
|
||||
/**
|
||||
* 核心方法:根据节点id选中节点,并逐层展开所有父节点
|
||||
* @param {String|Number} targetId 目标节点id
|
||||
*/
|
||||
selectNodeById(targetId) {
|
||||
const treeObj = $.fn.zTree.getZTreeObj(this.domId);
|
||||
if (!treeObj || !targetId) return;
|
||||
|
||||
const targetNode = treeObj.getNodeByParam('id', targetId);
|
||||
if (!targetNode) return;
|
||||
|
||||
// 高亮选中节点
|
||||
treeObj.selectNode(targetNode);
|
||||
|
||||
// 循环向上展开所有父节点
|
||||
let currNode = targetNode;
|
||||
while (currNode) {
|
||||
treeObj.expandNode(currNode, true, false, false);
|
||||
currNode = currNode.getParentNode();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 默认选中逻辑:有selectId选对应节点,无则选第一个一级节点
|
||||
*/
|
||||
handleDefaultSelect() {
|
||||
const treeObj = $.fn.zTree.getZTreeObj(this.domId);
|
||||
if (!treeObj) return;
|
||||
|
||||
const rootNodes = treeObj.getNodes();
|
||||
if (!rootNodes || rootNodes.length === 0) return;
|
||||
|
||||
if (this.selectId !== null && this.selectId !== undefined) {
|
||||
// 外部传入id,选中指定节点
|
||||
this.selectNodeById(this.selectId);
|
||||
} else {
|
||||
// 无传入id,默认选中第一个一级节点
|
||||
const firstRoot = rootNodes[0];
|
||||
treeObj.selectNode(firstRoot);
|
||||
treeObj.expandNode(firstRoot, true, false, false);
|
||||
}
|
||||
},
|
||||
initTree(_data, _domId) {
|
||||
const _this = this;
|
||||
// 合并自定义setting
|
||||
_this.treeSetting = Object.assign({}, _this.treeSetting, _this.setting);
|
||||
|
||||
// 绑定点击回调
|
||||
_this.treeSetting.callback.onClick = (event, treeId, treeNode) => {
|
||||
_this.$emit("on-tree-click", treeNode);
|
||||
};
|
||||
// 绑定复选框点击回调
|
||||
_this.treeSetting.callback.onCheck = (event, treeId, treeNode) => {
|
||||
const zTreeObj = $.fn.zTree.getZTreeObj(treeId);
|
||||
// getCheckedNodes(true) 获取所有手动勾选节点,不分层级
|
||||
const allChecked = zTreeObj.getCheckedNodes(true);
|
||||
this.$emit('on-tree-Check', allChecked);
|
||||
};
|
||||
// 展开所有一级节点
|
||||
var zNodes = _this.openFistNode(_data);
|
||||
// 初始化树
|
||||
$.fn.zTree.init($("#" + _domId), _this.treeSetting, zNodes);
|
||||
// 执行默认选中逻辑
|
||||
this.$nextTick(() => {
|
||||
this.handleDefaultSelect();
|
||||
})
|
||||
},
|
||||
openFistNode(_zNodes) {
|
||||
if (_zNodes && _zNodes.length > 0) {
|
||||
for (var i = 0; i < _zNodes.length; i++) {
|
||||
_zNodes[i].open = true;
|
||||
}
|
||||
}
|
||||
return _zNodes;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div>
|
||||
<Table
|
||||
:columns="columns"
|
||||
:data="listData"
|
||||
@on-selection-change="selectChange"
|
||||
class="link-table table-bottom-box"
|
||||
:height="tableHeight"
|
||||
no-data-text="暂无数据"
|
||||
></Table>
|
||||
<div class="page-panel">
|
||||
<div class="fl">
|
||||
<slot :sel-ids="selectList"></slot>
|
||||
</div>
|
||||
<Page
|
||||
class="fr"
|
||||
:total="total"
|
||||
:current="dataParmas.page"
|
||||
:page-size="dataParmas.count"
|
||||
:page-size-opts="[10, 20, 30, 40]"
|
||||
@on-page-size-change="pageSizeUpdate"
|
||||
@on-change="pageUpdate"
|
||||
placement="top"
|
||||
show-total
|
||||
show-sizer
|
||||
></Page>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
tableHeight: {
|
||||
default: 0,
|
||||
type: Number,
|
||||
},
|
||||
dataUrl: {
|
||||
default: "",
|
||||
type: String,
|
||||
},
|
||||
dataParmas: {
|
||||
default: {},
|
||||
type: Object,
|
||||
},
|
||||
dataFilter: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
columns: {
|
||||
default: [],
|
||||
type: Array,
|
||||
},
|
||||
autoLoad: {
|
||||
default: true,
|
||||
Type: Boolean,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
listData: [],
|
||||
selectList: [],
|
||||
total: 30,
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
this.dataParmas.page = 1;
|
||||
this.dataParmas.count = parseInt(localStorage.getItem("customPageCount") || 10);
|
||||
if (this.autoLoad) this.getData();
|
||||
},
|
||||
|
||||
methods: {
|
||||
getData() {
|
||||
const _this = this;
|
||||
let item = {},
|
||||
{ dataUrl, dataParmas } = this;
|
||||
// console.log("dataParmas", dataParmas);
|
||||
for (const key in dataParmas) {
|
||||
const element = dataParmas[key];
|
||||
item[key] = element;
|
||||
}
|
||||
_this.$api.get(dataUrl, item, resp => {
|
||||
let rData = resp.data;
|
||||
if (typeof _this.dataFilter === "function") {
|
||||
let fData = _this.dataFilter(rData);
|
||||
_this.listData = JSON.parse(JSON.stringify(fData));
|
||||
} else _this.listData = rData;
|
||||
});
|
||||
},
|
||||
selectChange(selection) {
|
||||
this.selectList = selection;
|
||||
},
|
||||
pageUpdate(page) {
|
||||
this.dataParmas.page = page;
|
||||
this.getData();
|
||||
},
|
||||
pageSizeUpdate(size) {
|
||||
this.dataParmas.page = 1;
|
||||
this.dataParmas.count = size;
|
||||
localStorage.setItem("customPageCount", size);
|
||||
this.getData();
|
||||
},
|
||||
reloadData() {
|
||||
this.getData();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<div class="type_set-box">
|
||||
<div class="type_name">
|
||||
<i class="iconfont icon-stop fl mr-10" v-if="!isEdit && !active" style="color: #ff5959"></i>
|
||||
<Input v-if="isEdit" v-model="type.name" ref="type_input" @on-blur="update" @on-enter="update" />
|
||||
<span v-else class="fl">{{ type.name }}</span>
|
||||
</div>
|
||||
<div class="type_action flex">
|
||||
<div class="action_item" @click="open">
|
||||
<span v-if="active">停用</span>
|
||||
<span v-else>启用</span>
|
||||
</div>
|
||||
<div class="action_edit" @click="edit">
|
||||
<span>编辑</span>
|
||||
</div>
|
||||
<div class="action_delete" @click="remove">
|
||||
<span>删除</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="divider2"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TypeSetBox",
|
||||
props: {
|
||||
type: {
|
||||
name: { type: String, default: "" },
|
||||
id: { type: String, default: "" },
|
||||
},
|
||||
active: { type: Boolean, default: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isEdit: false,
|
||||
eventType: "",
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
type: {
|
||||
deep: true,
|
||||
handler(newVal) {
|
||||
if (this.isEdit) {
|
||||
this.isEdit = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
"type.name"(newVal) {
|
||||
if (this.isEdit) {
|
||||
this.isEdit = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.$emit("active", this.type.id);
|
||||
},
|
||||
edit() {
|
||||
this.isEdit = !this.isEdit;
|
||||
if (this.isEdit) this.$nextTick(() => this.$refs["type_input"].focus());
|
||||
},
|
||||
update(e) {
|
||||
if (!this.type.name) {
|
||||
this.$Message.error("值不得为空!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.eventType == "") {
|
||||
this.eventType = e.type;
|
||||
this.isEdit = false;
|
||||
this.$emit("edit", this.type.id, this.type.name);
|
||||
setTimeout(() => {
|
||||
this.eventType = "";
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
remove() {
|
||||
this.$emit("remove", this.type.id);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
.type_set-box {
|
||||
width: 222px;
|
||||
background: #ffffff;
|
||||
margin-right: 15px;
|
||||
margin-bottom: 15px;
|
||||
.type_name {
|
||||
height: 66px;
|
||||
border: 1px solid #e9e9e9;
|
||||
border-bottom: none;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
line-height: 66px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
span {
|
||||
height: 24px;
|
||||
font-size: 15px;
|
||||
font-family: PingFangSC-Semibold, PingFang SC;
|
||||
font-weight: 500;
|
||||
color: #272829;
|
||||
line-height: 24px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 222px;
|
||||
display: inline-block;
|
||||
}
|
||||
.iconfont {
|
||||
font-size: 26px;
|
||||
}
|
||||
}
|
||||
.type_action {
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 222px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
background: #f7f7f7;
|
||||
border-radius: 0px 0px 2px 2px;
|
||||
border-top: none;
|
||||
border: 1px solid #e9e9e9;
|
||||
span {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
div {
|
||||
cursor: pointer;
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
div:hover {
|
||||
color: #6c91f0;
|
||||
}
|
||||
}
|
||||
.divider {
|
||||
// position: relative;
|
||||
height: 14px;
|
||||
width: 1px;
|
||||
background: #e8e8e8;
|
||||
// left: 73px;
|
||||
// top: -23px;
|
||||
float: left;
|
||||
margin-left: 71px;
|
||||
margin-top: -22px;
|
||||
}
|
||||
.divider2 {
|
||||
// position: relative;
|
||||
// left: 146px;
|
||||
// top: -37px;
|
||||
height: 14px;
|
||||
width: 1px;
|
||||
background: #e8e8e8;
|
||||
float: left;
|
||||
margin-left: 147px;
|
||||
margin-top: -22px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div>
|
||||
<ul class="public-upload-img fix">
|
||||
<li v-for="(item, index) in uploadList">
|
||||
<template v-if="item.status === 'finished'">
|
||||
<img :src="item.url" alt="" />
|
||||
<div class="upload-list-cover">
|
||||
<Icon type="ios-eye-outline" size="24" title="预览图片" @click.native="handleView(item.url)"></Icon>
|
||||
<Icon size="24" class="ml-10" title="删除图片" type="ios-trash-outline" @click.native="handleRemove(index)"></Icon>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<Progress v-if="item.showProgress" :percent="item.percentage" class="mt-24" hide-info></Progress>
|
||||
</template>
|
||||
</li>
|
||||
<li class="add-li">
|
||||
<Upload
|
||||
ref="upload"
|
||||
:show-upload-list="false"
|
||||
:on-success="handleSuccess"
|
||||
:format="['jpg', 'jpeg', 'png', 'gif']"
|
||||
accept="image/gif,image/jpeg,image/jpg,image/png"
|
||||
:max-size="2048"
|
||||
:on-format-error="handleFormatError"
|
||||
:on-exceeded-size="handleMaxSize"
|
||||
:default-file-list="defaultList"
|
||||
type="drag"
|
||||
:action="uploadUrl"
|
||||
>
|
||||
<div class="add-box">
|
||||
<Icon type="ios-add" size="50"></Icon>
|
||||
</div>
|
||||
</Upload>
|
||||
</li>
|
||||
</ul>
|
||||
<Modal title="图片预览" v-model="visible" footer-hide>
|
||||
<img :src="imgName" v-if="visible" style="width: 100%" />
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
baseUrl: { type: String, default: "" },
|
||||
imgList: { type: Array, default: [] },
|
||||
uploadUrl: { type: String, default: "" },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imgName: "",
|
||||
visible: false,
|
||||
uploadList: [],
|
||||
defaultList: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
let arr = [];
|
||||
|
||||
this.imgList.forEach(element => {
|
||||
arr.push({
|
||||
name: "",
|
||||
url: this.baseUrl + element,
|
||||
});
|
||||
});
|
||||
this.defaultList = arr;
|
||||
},
|
||||
mounted() {
|
||||
console.log(this.$refs.upload.fileList);
|
||||
this.uploadList = this.$refs.upload.fileList;
|
||||
},
|
||||
methods: {
|
||||
handleView(name) {
|
||||
this.imgName = name;
|
||||
this.visible = true;
|
||||
},
|
||||
handleRemove(index) {
|
||||
this.$refs.upload.fileList.splice(index, 1);
|
||||
this.imgList.splice(index, 1);
|
||||
},
|
||||
handleSuccess(res, file) {
|
||||
console.log(res, file);
|
||||
file.url = this.baseUrl + res.data;
|
||||
this.imgList.push(res.data);
|
||||
},
|
||||
handleFormatError(file) {
|
||||
console.error(file);
|
||||
},
|
||||
handleMaxSize(file) {
|
||||
this.$Notice.warning({
|
||||
title: "图片过大",
|
||||
desc: "图片最大上传大小为20M.",
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,456 @@
|
||||
<template>
|
||||
<Modal v-model="modal" title="选择用户" width="720px" :mask-closable="false" :closable="false">
|
||||
<Tabs class="tabs-panel" type="card" v-if="firstTabIndex && firstTabIndex.length > 1" v-model="firstTabIndex" @on-click="firstTabChange">
|
||||
<TabPane v-for="(item, index) in firstTabData" :label="item" :name="index + ''"></TabPane>
|
||||
</Tabs>
|
||||
<AutoComplete @on-search="handleSearch" @on-select="handleSelect" placeholder="输入名字搜索" v-model="searchTreeName" :clearable="true" class="auto-box">
|
||||
<Option v-for="item in currentFlatten" :value="item.name">
|
||||
<span>{{ item.name }}</span>
|
||||
<span class="fr info">{{ item.group }}</span>
|
||||
</Option>
|
||||
</AutoComplete>
|
||||
<div class="tree-wrap fix">
|
||||
<div class="tree-panel fl">
|
||||
<ul class="tree-tab-ul fix">
|
||||
<li
|
||||
@click="secondTabChange(index)"
|
||||
:style="{ width: 100 / secondTabData.length + '%' }"
|
||||
:class="{ active: index == secondTabIndex, fl: true }"
|
||||
v-for="(item, index) in secondTabData"
|
||||
>
|
||||
{{ item }}
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tree-box">
|
||||
<div :id="ztreeDomId" class="ztree"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="arrow-box fl">
|
||||
<svg t="1626916477711" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10021" width="36" height="36">
|
||||
<path
|
||||
d="M138.935 256.078v511.844h63.42V256.078h-63.42z m126.842 0v511.844h127.215V256.078H265.777zM885.064 512L583.254 64.323v191.755H456.414v511.844h126.842v191.755L885.064 512z"
|
||||
p-id="10022"
|
||||
fill="#2d8cf0"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="user-panel fr">
|
||||
<div class="title-box fix">
|
||||
<Checkbox @on-change="checkAllUser" v-model="isCheckUserAll">全选</Checkbox>
|
||||
<div class="fr">{{ tempUserList.length }}</div>
|
||||
<a href="javascript:;" @click="delCheckUser" v-if="tempUserList.findIndex(x => x.isCheck) > -1">删除选中</a>
|
||||
</div>
|
||||
<ul class="user-box">
|
||||
<li :class="{ active: item.isCheck }" v-for="(item, index) in tempUserList" @click="checkUserOne(item)">
|
||||
{{ item.name }}
|
||||
<span class="info">{{ item.group }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<i-Button type="primary" @click="submit">提交</i-Button>
|
||||
<i-Button @click="modal = false" style="margin-left: 8px">取消</i-Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
require("@public/libs/ztree/zTreeStyle.css");
|
||||
import "@public/libs/ztree/jquery.ztree.all.min.js";
|
||||
export default {
|
||||
props: {
|
||||
treeData: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
userids: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
ignoreIds: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
},
|
||||
model: {
|
||||
prop: "userids",
|
||||
event: "change",
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ztreeDomId: null,
|
||||
modal: false,
|
||||
isCheckUserAll: false,
|
||||
firstTabIndex: 0,
|
||||
secondTabIndex: 0,
|
||||
cacheMap: new Map(),
|
||||
currentTree: [],
|
||||
currentFlatten: [],
|
||||
searchTreeName: null,
|
||||
tempUserList: [],
|
||||
isDisabled: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
searchTreeName(val) {
|
||||
this.handleSearch(val);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
firstTabData() {
|
||||
if (this.treeData.length > 1) return this.treeData.map(x => x.name);
|
||||
else return [];
|
||||
},
|
||||
secondTabData() {
|
||||
if (this.treeData.length == 0) return [];
|
||||
return this.treeData[this.firstTabIndex].data.map(x => x.name);
|
||||
},
|
||||
currentTreeFlatten() {
|
||||
const flatten = (data, pid, pname) => {
|
||||
return data.reduce((arr, { id, name, children = [], type }) => arr.concat([{ id, name, pid, type, group: pname }], flatten(children, id, name)), []);
|
||||
};
|
||||
if (!this.currentTree) return [];
|
||||
var newArr = flatten(this.currentTree, 0, "");
|
||||
return newArr;
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.isDisabled = this.ignoreIds.length > 0;
|
||||
this.ztreeDomId = this.uuid();
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.modal = true;
|
||||
this.firstTabChange("0");
|
||||
},
|
||||
firstTabChange(index) {
|
||||
this.firstTabIndex = index;
|
||||
this.secondTabChange(0);
|
||||
},
|
||||
secondTabChange(index) {
|
||||
const _this = this;
|
||||
if (index != 0 && this.secondTabIndex == index) return;
|
||||
this.secondTabIndex = index;
|
||||
this.changeEmpty();
|
||||
this.initTree();
|
||||
},
|
||||
handleSearch(query) {
|
||||
const _this = this;
|
||||
let arr = [],
|
||||
searchArr = this.currentTreeFlatten.filter(x => x.type == 2 && x.name.indexOf(query) != -1);
|
||||
for (const item of searchArr) {
|
||||
!arr.map(x => _this.getSplitId(x.id)).includes(_this.getSplitId(item.id)) && arr.push(item);
|
||||
}
|
||||
this.currentFlatten = arr;
|
||||
},
|
||||
handleSelect(query) {
|
||||
const _this = this;
|
||||
if (query.indexOf(",") != -1) query = query.split(",")[0];
|
||||
var treeObj = $.fn.zTree.getZTreeObj(this.ztreeDomId);
|
||||
var node = treeObj.getNodeByParam("name", query, null);
|
||||
if (!!node) treeObj.checkNode(node, true, true, true);
|
||||
},
|
||||
formatTree(tree, level = 0) {
|
||||
const _this = this;
|
||||
tree.forEach(element => {
|
||||
element.open = level == 0;
|
||||
element.type = 1;
|
||||
element.id = element.id + "," + element.pid;
|
||||
element.checked = false;
|
||||
if (typeof element.children !== "undefined" && element.children.length > 0) element.children = _this.formatTree(element.children, level + 1);
|
||||
else {
|
||||
element.type = 2;
|
||||
}
|
||||
});
|
||||
return tree;
|
||||
},
|
||||
initTree() {
|
||||
const _this = this;
|
||||
let first = _this.firstTabIndex,
|
||||
second = _this.secondTabIndex,
|
||||
cacheKey = first + "," + second,
|
||||
cacheMap = _this.cacheMap,
|
||||
tree = this.treeData[first].data[second].data;
|
||||
|
||||
if (!!tree && typeof tree === "function") {
|
||||
var cacheData = cacheMap.get(cacheKey);
|
||||
if (!cacheData) {
|
||||
tree().then(data => {
|
||||
cacheMap.set(cacheKey, data);
|
||||
_this.createTree(data, _this.ztreeDomId);
|
||||
});
|
||||
} else {
|
||||
_this.createTree(cacheData, _this.ztreeDomId);
|
||||
}
|
||||
} else {
|
||||
_this.createTree(tree, _this.ztreeDomId);
|
||||
}
|
||||
},
|
||||
createTree(_data, _domId) {
|
||||
const _this = this;
|
||||
var jtree = JSON.parse(JSON.stringify(_data));
|
||||
var ftree = _this.formatTree(jtree);
|
||||
_this.currentTree = ftree;
|
||||
//_this.tempUserList = JSON.parse(JSON.stringify(_this.userids));
|
||||
|
||||
let treeSetting = {
|
||||
view: {
|
||||
dblClickExpand: false,
|
||||
showIcon: false,
|
||||
},
|
||||
check: {
|
||||
enable: true,
|
||||
chkboxType: { Y: "ps", N: "ps" },
|
||||
},
|
||||
callback: {},
|
||||
};
|
||||
function formatUser(tree, checked) {
|
||||
const recursionCheckIds = tree => {
|
||||
let arrIds = [];
|
||||
for (const key in tree) {
|
||||
const element = tree[key];
|
||||
if (element.type == 2 && !element.chkDisabled) arrIds.push({ id: element.id, tid: element.tId });
|
||||
if (typeof element.children !== "undefined" && element.children) {
|
||||
var childIds = recursionCheckIds(element.children);
|
||||
arrIds = [...arrIds, ...childIds];
|
||||
}
|
||||
}
|
||||
return arrIds;
|
||||
};
|
||||
var checkIds = recursionCheckIds([tree]);
|
||||
var checkUserList = _this.currentTreeFlatten.filter(x => checkIds.map(x => x.id).includes(x.id));
|
||||
let checkUserArr = [];
|
||||
for (const element of checkUserList) {
|
||||
var findObj = checkIds.find(x => x.id == element.id);
|
||||
if (!!findObj && checkUserArr.findIndex(x => x.id == element.id) === -1 && _this.tempUserList.findIndex(x => x.id == element.id) === -1) {
|
||||
checkUserArr.push({ ...element, isCheck: false, tid: findObj.tid });
|
||||
}
|
||||
}
|
||||
if (checked) _this.tempUserList = _this.tempUserList.concat(checkUserArr);
|
||||
else {
|
||||
var ids = checkUserList.map(x => x.id);
|
||||
_this.tempUserList = _this.tempUserList.filter(x => !ids.includes(x.id));
|
||||
}
|
||||
}
|
||||
|
||||
//当禁止时,是否还显示在右侧
|
||||
var isCheckFormat = treeNode => {
|
||||
let id = _this.getSplitId(treeNode.id);
|
||||
return _this.ignoreIds.findIndex(x => x == id) === -1;
|
||||
};
|
||||
|
||||
treeSetting.callback.onClick = (event, treeId, treeNode, clickFlag) => {
|
||||
let check = treeNode.checked;
|
||||
let treeObj = $.fn.zTree.getZTreeObj(treeId);
|
||||
treeObj.checkNode(treeNode, !check, true);
|
||||
if (isCheckFormat(treeNode)) formatUser(treeNode, treeNode.checked);
|
||||
};
|
||||
treeSetting.callback.onCheck = (event, treeId, treeNode) => {
|
||||
if (isCheckFormat(treeNode)) formatUser(treeNode, treeNode.checked);
|
||||
};
|
||||
var treeObj = $.fn.zTree.init($("#" + _domId), treeSetting, ftree);
|
||||
//checkUser
|
||||
var nodes = treeObj.getNodesByFilter(x => _this.userids.includes(_this.getSplitId(x.id)));
|
||||
var arr = [];
|
||||
nodes.forEach(element => {
|
||||
var splitId = _this.getSplitId(element.id); //重复id只选择第一个
|
||||
if (!arr.includes(splitId)) treeObj.checkNode(element, true, true, true);
|
||||
arr.push(splitId);
|
||||
});
|
||||
|
||||
var ignorNodes = treeObj.getNodesByFilter(x => _this.ignoreIds.includes(_this.getSplitId(x.id)));
|
||||
ignorNodes.forEach(element => {
|
||||
treeObj.checkNode(element, true, true, true);
|
||||
treeObj.setChkDisabled(element, true);
|
||||
});
|
||||
},
|
||||
checkAllUser(check) {
|
||||
//全选用户
|
||||
const _this = this;
|
||||
_this.tempUserList.forEach(element => {
|
||||
element.isCheck = check;
|
||||
});
|
||||
},
|
||||
checkUserOne(item) {
|
||||
item.isCheck = !item.isCheck;
|
||||
let tlist = this.tempUserList;
|
||||
this.isCheckUserAll = tlist.length > 0 && tlist.filter(x => x.isCheck).length == tlist.length;
|
||||
},
|
||||
delCheckUser() {
|
||||
//删除选中用户
|
||||
const _this = this;
|
||||
let tlist = _this.tempUserList,
|
||||
delArr = [];
|
||||
for (const item of tlist) {
|
||||
item.isCheck && delArr.push(item);
|
||||
}
|
||||
var treeObj = $.fn.zTree.getZTreeObj(_this.ztreeDomId);
|
||||
delArr.forEach(element => {
|
||||
var node = treeObj.getNodeByTId(element.tid);
|
||||
treeObj.checkNode(node, false, true, true);
|
||||
});
|
||||
_this.isCheckUserAll = false;
|
||||
},
|
||||
changeEmpty() {
|
||||
this.tempUserList = [];
|
||||
this.searchTreeName = "";
|
||||
},
|
||||
submit() {
|
||||
const _this = this;
|
||||
let objArr = [],
|
||||
idsArr = this.tempUserList.map(x => _this.getSplitId(x.id));
|
||||
idsArr = Array.from(new Set(idsArr));
|
||||
this.$emit("change", idsArr);
|
||||
function delKey(obj, ...args) {
|
||||
args.forEach(v => {
|
||||
delete obj[v];
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
idsArr.forEach(element => {
|
||||
let findObj = _this.tempUserList.find(x => _this.getSplitId(x.id) == element);
|
||||
delKey(findObj, "id", "isCheck", "tid", "pid", "type");
|
||||
let item = { ...findObj, id: element };
|
||||
if (_this.isDisabled) item.disabled = this.userids.findIndex(x => x == element) !== -1;
|
||||
objArr.push(item);
|
||||
});
|
||||
|
||||
if (_this.isDisabled) {
|
||||
var treeObj = $.fn.zTree.getZTreeObj(_this.ztreeDomId);
|
||||
var nodes = treeObj.getNodesByFilter(x => x.chkDisabled);
|
||||
var disIds = nodes.map(x => _this.getSplitId(x.id));
|
||||
objArr.forEach(element => {
|
||||
element.disabled = disIds.findIndex(x => element.id == x) > -1;
|
||||
});
|
||||
}
|
||||
this.$emit("on-submit", objArr);
|
||||
this.modal = false;
|
||||
},
|
||||
getSplitId(strId) {
|
||||
if (typeof strId === "string" && strId.indexOf(",") != -1) return parseInt(strId.split(",")[0]);
|
||||
else return strId;
|
||||
},
|
||||
uuid() {
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
||||
var r = (Math.random() * 16) | 0,
|
||||
v = c == "x" ? r : (r & 0x3) | 0x8;
|
||||
return v.toString(16);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tree-wrap {
|
||||
height: 430px;
|
||||
position: relative;
|
||||
}
|
||||
.tree-panel {
|
||||
border: 1px solid #eee;
|
||||
width: 280px;
|
||||
height: 430px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.tree-tab-ul {
|
||||
li {
|
||||
background: #f7f7f7;
|
||||
border-bottom: 1px solid #eee;
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
li.active {
|
||||
background: #fff;
|
||||
color: #2d8cf0;
|
||||
border-bottom: none;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
.tree-box {
|
||||
padding: 4px 0;
|
||||
height: 394px;
|
||||
width: auto;
|
||||
overflow-y: auto;
|
||||
border: none;
|
||||
}
|
||||
.user-panel {
|
||||
border: 1px solid #eee;
|
||||
width: 280px;
|
||||
.title-box {
|
||||
background: #f7f7f7;
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
padding: 0 12px;
|
||||
a {
|
||||
text-decoration: none;
|
||||
float: right;
|
||||
margin-right: 20px;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
.user-box {
|
||||
height: 396px;
|
||||
overflow-y: auto;
|
||||
li {
|
||||
line-height: 32px;
|
||||
padding-left: 12px;
|
||||
cursor: pointer;
|
||||
.info {
|
||||
float: right;
|
||||
margin-right: 12px;
|
||||
font-size: 12px;
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
li:hover {
|
||||
background: #f3f3f3;
|
||||
}
|
||||
li.active {
|
||||
background: #e4edf6;
|
||||
}
|
||||
}
|
||||
}
|
||||
.arrow-box {
|
||||
width: 128px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
top: 50%;
|
||||
margin-top: -17px;
|
||||
}
|
||||
|
||||
.auto-box {
|
||||
width: 200px;
|
||||
margin-bottom: 10px;
|
||||
.ivu-select-item .info {
|
||||
font-size: 12px;
|
||||
color: gray;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.ztree li {
|
||||
overflow: hidden;
|
||||
}
|
||||
.ztree li a {
|
||||
width: 100%;
|
||||
}
|
||||
.ztree li a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.ivu-auto-complete .ivu-select-dropdown-list {
|
||||
max-height: 310px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user