一、pass-word组件(自定义密码输入框,可预览)
使用场景:修改密码时,对密码进行多方位验证,并显示验证结果。
<!DOCTYPE html>
<html lang="en" ng-app="myModel">
<head>
<meta charset="UTF-8">
<title>passWord</title>
<script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
</head>
<body>
<div ng-controller="thisCtrl">
<pass-word
label-width="150"
is-must-fill="true"
label-text="原密码"
this-type="password"
placeholder="原密码"
is-disabled="false"
self-show='pastShow'
is-past-input='true'
self-data="oldPassword"
verify="verify"
></pass-word>
<pass-word
label-width="150"
is-must-fill="true"
label-text="输入新密码"
this-type="password"
placeholder="输入新密码"
is-disabled="false"
is-first-input='true'
key-of-reg-exp="simplePassword"
self-data="params.newPassword"
sibling-data="newPasswordSecond"
one-tip="oneTip"
many-tips="manyTips"
self-show='selfShow'
sibling-show='siblingShow'
></pass-word>
<pass-word
label-width="150"
is-must-fill="true"
label-text="再次输入新密码"
this-type="password"
placeholder="再次输入新密码"
is-disabled="false"
key-of-reg-exp="simplePassword"
self-data="newPasswordSecond"
sibling-data="params.newPassword"
one-tip="oneTip"
many-tips="manyTips"
self-show='siblingShow'
sibling-show='selfShow'
></pass-word>
</div>
</body>
</html>
<script>
var app = angular.module('myModel', []);
app.controller('thisCtrl', function ($scope) {
$scope.params = {};//发给后台
$scope.pastShow = {
isShowOneTip:false,
isGreenOneTip:false,
isShowManyTips:false,
};
$scope.selfShow = {
isShowOneTip:false,
isGreenOneTip:false,
isShowManyTips:false,
};
$scope.siblingShow = {
isShowOneTip:false,
isGreenOneTip:false,
isShowManyTips:false,
};
$scope.oneTip = {//由当前页传至本组件
success: '自定义_输入符合要求!',
empty: '自定义_该项不能为空!',
different: '自定义_两次密码输入不一致!',
formatError: '自定义_输入不符合要求,请输入以字母开头的4-24个字符!'
};
$scope.manyTips= [
'密码要求:',
'1.必须包含:数字',
'2.必须包含:字母',
'3.必须包含:小写字母',
'4.必须包含:大写字母',
'5.必须包含:下划线',
];
$scope.oldPassword= '';
$scope.params.newPassword='';
$scope.newPasswordSecond= '';
$scope.verify = function() {
//把原密码告诉后台,根据后台判断结果,决定是否继续验证
return true
}
});
app.directive('passWord', function () {
var html = `
<div style="display:flex;align-items:center">
<div ng-style="{width:(labelWidth||130)+'px'}" style="text-align:right;margin-right:10px;">
<span ng-show="isMustFill" style="color:red;">*</span>
<label ng-bind="labelText||'密码'"></label>
</div>
<div style="padding:10px 0; position:relative;">
<input
type="{{ thisType ? thisType : 'text' }}"
ng-model="selfData" placeholder="{{placeholder||''}}"
ng-disabled="isDisabled" ng-focus="focus()"
ng-change="changeOrBlur()"
ng-blur="changeOrBlur()"
ng-trim="false"
style="width:290px;height:30px"
>
<img
src="{{servicePicture.eyeClose}}"
ng-click="isShowEyeOpen=true;thisType='text'"
ng-show="!isShowEyeOpen"
style="width:14px;height:14px;position:absolute;right:10px;top:20px;cursor:pointer;padding:5px;"
>
<img
src="{{servicePicture.eyeOpen}}"
ng-click="isShowEyeOpen=false;thisType='password'"
ng-show="isShowEyeOpen"
style="width:14px;height:14px;position:absolute;right:10px;top:20px;cursor:pointer;padding:5px;"
>
</div>
<div style="position:relative">
<div ng-show="selfShow.isShowOneTip">
<img
src="{{selfShow.isGreenOneTip ? servicePicture.correct : servicePicture.error}}"
style="width:14px;height:14px;position:relative;left:5px;">
<span
ng-bind="singleTipText"
ng-style="selfShow.isGreenOneTip?{ \'color\':\'green\'}:{\'color\':\'red\'}"
style="position:relative;left:5px;">
></span>
</div>
<div ng-show="selfShow.isShowManyTips && manyTips.length>0" style="position:absolute;top:0;display:flex">
<img
src="{{servicePicture.triangle}}"
style="width:14px;height:14px;position:relative;z-index:3;left:5px;"
>
<div style="width:200px;border:1px solid #DCDCDC;position:relative;top:-5px;left:3px;padding:5px;">
<div ng-repeat="li in manyTips" ng-bind="li"></div>
</div>
</div>
</div>
</div>
`;
return {
restrict: 'E',
template: html,
scope: {
//以下是16种配置项。这是输入框组件,适用于密码输入、普通输入(isPastInput/isFirstInput:true)、旁边提示和弹窗提示。
labelWidth: '@labelWidth',//标签宽度
labelText: '@labelText',//标签文字
placeholder: '@placeholder',//输入框内提示文字
thisType: '@thisType',//输入框类型(文字或密码)
keyOfRegExp: '@keyOfRegExp',//会用到的正则
isMustFill: '=isMustFill',//该项是否必填
isDisabled: '=isDisabled',//该项是否禁用
isPastInput: '=isPastInput',//该项是不是原密码xxxxxxxxxxx
isFirstInput: '=isFirstInput',//该项是不是第一次输入新密码xxxxxxxxxxxx
selfData: '=selfData',//自身数据============
siblingData: '=siblingData',//兄弟数据===============
oneTip: '=oneTip',//单条提示
manyTips: '=manyTips',//多条提示
selfShow: '=selfShow',//自身的提示是否显示出来============
siblingShow: '=siblingShow',//兄弟的提示是否显示出来=============
verify: '=verify',//verify是函数,但按照变量传进来更好xxxxxxxxxxx
},
replace: true,
controller: function ($scope, serviceRegExp, servicePicture) {
$scope.servicePicture = servicePicture;
$scope.inputChange = function (params, message) {
var params = params;
var config = {
init: [false, false, false],
focus: [false, false, true],
empty: [true, false, false],
formatError: [true, false, false],
different: [true, false, false],
wrong: [true, false, false],
success: [true, true, false],
};
$scope.selfShow.isShowOneTip = config[params][0];
$scope.selfShow.isGreenOneTip = config[params][1];
$scope.selfShow.isShowManyTips = config[params][2];
$scope.singleTipText = ($scope.oneTip && $scope.oneTip[params]) || message;
};
$scope.focus = function () {
if($scope.isPastInput){
$scope.inputChange('init');
return
}
if($scope.isFirstInput){
$scope.siblingData='';
$scope.siblingShow = {
isShowOneTip:false,
isGreenOneTip:false,
isShowManyTips:false,
};
}
$scope.inputChange('focus');
};
$scope.changeOrBlur = function () {
if ($scope.isMustFill && $scope.selfData=="") {//判断该项是否可以为空
$scope.inputChange('empty', '该项不能为空!');
return
}
if ($scope.keyOfRegExp && !(serviceRegExp[$scope.keyOfRegExp].test($scope.selfData))) {
$scope.inputChange('formatError', '格式错误!');
return
}
if (!$scope.isPastInput && !$scope.isFirstInput && $scope.selfData != $scope.siblingData) {
$scope.inputChange('different', '两次密码输入不一致!');
return
}
if(angular.isFunction($scope.verify) && !$scope.verify()){//验证原密码是否正确
$scope.inputChange('wrong', '原密码输入错误!');
return
};
$scope.inputChange('success', '输入符合要求!');
};
},
link: function (scope, ele, attrs) {
}
};
});
app.factory('serviceRegExp', function () {
return {
simplePassword: /^[a-zA-Z][a-zA-Z0-9]{4,24}$/,//简单密码验证,以大小写字母开头,由大小写字母和数字构成的,共5-25位的密码
complexPassword: /^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&*?_|~=+\-<>[{}/\'\":;,.\]*]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&*?_|~=+\-<>[{}/\'\":;,.\]*]+$)(?![\d!@#$%^&*?_|~=+\-<>[{}/\'\":;,.\]*]+$)[a-zA-Z\d!@#$%^&*?_|~=+\-<>[{}/\'\":;,.\]*]{6,25}$/,//复杂密码验证,同时包含字母、数字、特殊符号如!@#$%^&*?_|~=+\-<>[{}/\'\":;,.\]
ipFrom0To255: /^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$/,//ip地址验证,从0.0.0.0~255.255.255.255
portFrom0To65535:/^([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/,
mac:/^[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}$/,
mobilePhone: /^1[3456789]\d{9}$/,//手机号码验证,中国大陆
numberNotLimit: /^(0|[1-9][0-9]*)$/,//0或非0开头的数字
numberWithLimit: /^(0|[1-9][0-9]{3,4})$/,//0或非0开头的数字4-5位
emailNotLimit: /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/,//邮箱验证
emailWithLimit: /^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,4}$/,//邮箱验证
chineseCharacters: /^[\u4e00-\u9fa5]{0,}$/,//汉字验证
blankSpace: /^\s*$/ //零到多个空格
};
});
app.factory('servicePicture', function () {
return {
eyeClose: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAABatJREFUeAHtmX9InVUYx71Xu6bmzWorlGr90IQgiNS21qSIYX/0h+0fKYQRq/xtaAOjtZo4GMwMJSF/tNavrVEUbQtyGZXSqsHuhaA2GrElW6HmaFmk+LvPMzyX68v7nvu+i6vOzguHc87zPOd5nu/3fc655+UmJJjHMGAYMAwYBgwDhgHDgGHAMGAYMAwYBgwDhoH/GwM+AVxZWVk4MzPzvM/ny52bmzuVmJjY1NHRcWwlkVFRUXE/2HbQcsH1Exh3gbHfB/gHAN+LMKAAQ8S43+9/aKWQUFVVtX56evoL8F2pMNJPgbHIPzs7+xKTCHgxgKUU5AdYeJXML+enoaEhHfDvgSEavEC6Apwv+hnkyMz6oLyFynjZKr/c5qOjo6+Q8xqHvHOEgJMOSqmECrbIRif9cpeXlZUVgeFpTZ4nhIA2jUECVbCvpqbmVp3NctTx4m7jLHtXlxvktPq7urqOYLRXY3jD1NRUT319/bUam2Wlqq6uvo4X1wPA650Sg5w3uru7e6UCEpKTk+sRnHMyxlHu2NjY4cbGRutB4rRkyeS8qBQOvU9I4A6nJARrIBB4VvQX7wEykP1C95mMNc/RtLS04tbW1j80NkumkjdPtR4mgfW6JCCgiMr/XGwSlWE4HD6dn5+fwnyDktn0NxNgU15eXg/2F2z0SybiBd5OpX5FAnfrkgD8bsB3K5tIBYgABz5uTPvpH1cGDv0IjkoViw42iyYm54e5t+wj4CpdUHI+0NnZWUo/p+wungFqIorMzMwn6PuUzKFfDUm95eXl3XLRcLCJu7i2tjbIm38d8HKQxwLfN48tAl4SXFABKmOcXs34a9pdSqbpz3KlrOPafDCaWY39f1bNV+omHLUxvsmFwx+wKeTUH7Xa2hIgRnV1dRnj4+MfE+BB6yK7OeB/Rr6NEvsoXkTMA3+MvolY2XZ52Mj6U1NTH21ra/vTRmdfAcqQn73A4ODgXgKWKlmsHvBnsO/E7n0YPxvL3o2e030Nh28Jvp/B941u1ogN9vsp+y3gmHRa41gBagEBfez1ncxfUDKXvey179geh9ijx7lrhNvb2/9ys3Z+C94DgHuxLyaHdfQxc432TVz53N2OjwV7PtpGxq6dklQRzvaQjJs9Z40jc0lEtok0uUdM4E/dLi/gV75IZS4fZ9Jc54Zt5MHnOXw9Jbe8iFAz8BRETt3JyckWAug+MDTh4q7aQ6VtdVtpko0nAmQB4OWu8CP9nTJfLg9v/lPuJY94zSdyE3S7cHh4eIuUmFv7RbTLLigo6AmFQr95ibngIhRrIZ+Y13Cg7dbYaQ8czTovKqcYPl7Ma5z4njB5MuYTs4lMHW9clGEDp28tNkdpTol6Aats5/D9jfimf0sJrT0E5A0NDT1plevmrs8A+RTmTjBMkKCDw+NZWVnrsJsVPb8amXRrafkknS/JMXYkD130c541YdaEAB2m6o5xqg+KwfwF7RQ622991oU4CwqinenGrgngF2D1xMTE73bOCCqg1xI4ZKdXMkhZBaAaAO1Qsuge3U50rwL2fLTcOuZeshkC3rbKZU4uQ+Qh5Lt6XG+BwsJC+e3+1c4ryRBTD17WCTAAjtj5EBl+RmKBFztivQNQ+Vaxe763EzrJXBNQUlIywxvaiqMZi7NvMzIynrPI4j6FgM20AUugUXLcbpFpp64JEC986HxAgI0E/pB2hLYtGAwWNTc3/62NEgcluQwQfwP57KL/kvZmUlLSfVx/w17CJXkxFlsC99FJu6SHRKl0+x8I0XlxSi7ym+/1G2VBCE8VsGDlJU4AedppKcSccdLFS77oBHAIyuH1ixUQxAykp6f3W+Xxni86AZzyY4Aqpp1Q4AB/kr1c3NLS8o+Srfiecvfx52s2v+k5Ml7xgA1Aw4BhwDBgGDAMGAYMA4YBw4BhwDBgGDAMGAaWDQP/AqK0GxEcsDd9AAAAAElFTkSuQmCC',
eyeOpen: 'data:image/png;base64,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',
correct: 'data:image/png;base64,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',
error: 'data:image/png;base64,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',
triangle: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAoCAYAAAD+MdrbAAAAAXNSR0IArs4c6QAAAvpJREFUSA2tl7uLE1EUxmcmk6cJ2WTzQDBBBGEVbBR8oY3IglraiCCWVlaipa1iLVYW7oKVIuL6YC0s7f0T8jQmhAghD/L0+wZnPJPE3TvJXpi9Z07u+c059373zqyuHVCbTqd6qVR6diA4wMxisbhVKBSm+qrEarUaGQ6Hb8C5TtZKQJSYnEwmH8G5QBjb0sByuXxkPB7vgnHSIv39Y8gbVRtlnkBm3zHeBQsEAt4zRGbnAfuEhUjKBEKhkJZOp70BMWfXAHqLKyJhkUhES6VSlku5ZGR2B6APs7BYLObASFQCQmMPUOYWYKbMLB6Pa4lEQrr2LhkAHbCniHgko3Rdt0DRaFS6Ldv1RPkrs8GcvYTvrvQTxvkKh8PS7dgLdQhQGCVS/TeckTAMw7BWMhgMSrfLngNiP3JSqP6LcqTP59MymYzm9/ule872SQ/Vj/tvuM5Iv2maWjab3RfGGCdDqH9jNBrtYu7yEkb1MzOWq9IsIObsHEBU/7oMctSPhVBtOubsGFbuB2AuDUj1q8I4zgTsySyM6p8VrCrUBOyyHEz181q2caZdsw39Lcuy4gj7KgntdltrNpvS5ck2sJIPEfFTRnU6Ha1er2uYDulWsg0I9he0dhWLU5YR/X7fgnqdAkdglUolB2Gz/A0J5lajsLn1VJoD5GBsvXVk9BmlnpXB3Ho83vfbx4xxAemo1WqHBoPBO0A3eW83bj1myq24V5sDcjBgfmzHbfS3ZDChPAu5Jf/XXBq0B2GBhrlc7jb657aPPReo0Who3W5Xul32QiBHADbN5/P30T+WEcja0in1uqgtLHl2IA6QewC/AMyVwKJtqgTkAzCnN1Hya5iu858vqmTy3ztfGUgoZHUF3zPvYcZ4bzd51HkCEoCT/TQ+377AzNhA9s5hLJ2qNso/jvK5q47KmKU+lmwA5HO41+vxHXTK9rH3XLIMbrVaa5DPDqCXbL9LBrZTtcdr4jfktIlrRzVGaRwy5Ef7K+h19Y92+4mAWv9W/AHAizQaNQfgsAAAAABJRU5ErkJggg=='
};
});
</script>
二、single-select和both-select
<!DOCTYPE html>
<html lang="en" ng-app="myModel">
<head>
<meta charset="UTF-8">
<title>onlySelect</title>
<script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
</head>
<body>
<div ng-controller="thisCtrl">
<div>一、此消彼长式</div>
<div>使用场景:</div>
<div>有上下两行数据,上行有很多备选项</div>
<div>每点击上行的某项,则该项“会”消失并在下行出现</div>
<div>每点击下行的某项,则该项“会”消失并在上行出现</div>
<div style="margin-bottom:20px;">
<span ng-click="getDataUp()" style="border-radius:5px;padding:2px;background:gray;">点击获取选中数据</span>
<span ng-bind="selectedDatasUp"></span>
</div>
<div style="width:1000px;">
<single-select form-datas-up="formDatasUp" current-datas-up="currentDatasUp" two-label-up="twoLabelUp"></single-select>
</div>
<div style="width:1000px;border:1px solid gray;margin:100px 0"></div>
<div>二、此静彼长式</div>
<div>使用场景:</div>
<div>有上下两行数据,上行有很多备选项</div>
<div>每点击上行的某项,则该项“不”消失并在下行出现,重复点击无效</div>
<div>每点击下行的某项,则该项“会”消失,但对上行无影响</div>
<div style="margin-bottom:20px;">
<span ng-click="getDataDown()" style="border-radius:5px;padding:2px;background:gray;">点击获取选中数据</span>
<span ng-bind="selectedDatasDown"></span>
</div>
<div style="width:1000px;">
<both-select form-datas-down="formDatasDown" current-datas-down="currentDatasDown" two-label-down="twoLabelDown"></both-select>
</div>
</div>
</body>
</html>
<script>
var app = angular.module('myModel', []);
app.controller('thisCtrl', function ($scope) {
$scope.formDatasUp = [
{
value: '0',
label: '选项一'
},
{
value: '1',
label: '选项二'
},
{
value: '2',
label: '选项三'
},
{
value: '3',
label: '选项四'
},
{
value: '4',
label: '选项五'
}
];
$scope.currentDatasUp = [];
$scope.twoLabelUp = ['上面标签:','下面标签:'];
$scope.getDataUp = function(){
$scope.selectedDatasUp=angular.copy($scope.currentDatasUp);
};
////////////////////////////////////////////////////////////////////////////////////////
$scope.formDatasDown = [
{
value: '0',
label: '选项一'
},
{
value: '1',
label: '选项二'
},
{
value: '2',
label: '选项三'
},
{
value: '3',
label: '选项四'
},
{
value: '4',
label: '选项五'
}
];
$scope.currentDatasDown = [];
$scope.twoLabelDown = ['备选项:','被选项:'];
$scope.getDataDown = function(){
$scope.selectedDatasDown=angular.copy($scope.currentDatasDown);
};
});
app.directive('singleSelect', function () {
var html = `
<div>
<div style="width:100%;height:30px;line-height:30px;border:1px solid green;padding:5px;">
<span ng-bind="twoLabelUp[0]"></span>
<span ng-repeat="upData in upDatas track by $index" ng-bind="upData.label" ng-click="clickUpUp(upData.value)" style="border-radius:5px;display:inline-block;margin-right:10px;padding:2px;background:gray"></span>
</div>
<div style="width:100%;height:30px;line-height:30px;border:1px solid red;padding:5px;">
<span ng-bind="twoLabelUp[1]"></span>
<span ng-repeat="downData in downDatas track by $index" ng-bind="downData.label" ng-click="clickUPDown(downData.value)" style="border-radius:5px;display:inline-block;margin-right:10px;padding:2px;background:gray"></span>
</div>
</div>
`;
return {
restrict: 'E',
template: html,
scope: {
formDatasUp: '=formDatasUp',
currentDatasUp: '=currentDatasUp',
twoLabelUp: '=twoLabelUp',
},
replace: true,
controller: function ($scope) {
$scope.upDatas = angular.copy($scope.formDatasUp);
$scope.downDatas = [];
$scope.clickUpUp = function (valueOuter) {
angular.forEach($scope.upDatas,function(value,index){
if(valueOuter===value.value){
$scope.upDatas.splice(index,1);
$scope.downDatas.push(value);
$scope.currentDatasUp.push(value.value);
console.log($scope.currentDatasUp)
}
})
};
$scope.clickUPDown = function (valueOuter) {
angular.forEach($scope.downDatas,function(value,index){
if(valueOuter===value.value){
$scope.upDatas.push(value);
$scope.downDatas.splice(index,1);
var thisIndex=$scope.currentDatasUp.indexOf(value.value)
if(thisIndex>-1){
$scope.currentDatasUp.splice(thisIndex,1);
}
console.log($scope.currentDatasUp)
}
})
};
},
link: function (scope, ele, attrs) {
}
};
});
app.directive('bothSelect', function () {
var html = `
<div>
<div style="width:100%;height:30px;line-height:30px;border:1px solid green;padding:5px;">
<span ng-bind="twoLabelDown[0]"></span>
<span ng-repeat="upData in upDatasDown track by $index" ng-bind="upData.label" ng-click="clickDownUp(upData)" style="border-radius:5px;display:inline-block;margin-right:10px;padding:2px;background:gray"></span>
</div>
<div style="width:100%;height:30px;line-height:30px;border:1px solid red;padding:5px;">
<span ng-bind="twoLabelDown[1]"></span>
<span ng-repeat="downData in downDatasDown track by $index" ng-bind="downData.label" ng-click="clickDownDown(downData,$index)" style="border-radius:5px;display:inline-block;margin-right:10px;padding:2px;background:gray"></span>
</div>
</div>
`;
return {
restrict: 'E',
template: html,
scope: {
formDatasDown: '=formDatasDown',
currentDatasDown: '=currentDatasDown',
twoLabelDown: '=twoLabelDown',
},
replace: true,
controller: function ($scope) {
$scope.upDatasDown = angular.copy($scope.formDatasDown);
$scope.downDatasDown = [];
$scope.clickDownUp = function (data) {
var thisIndex=$scope.currentDatasDown.indexOf(data.value)
if(thisIndex>-1){
return
}else{
$scope.downDatasDown.push(data);
$scope.currentDatasDown.push(data.value);
}
};
$scope.clickDownDown = function (data,index) {
$scope.downDatasDown.splice(index,1);
var thisIndex=$scope.currentDatasDown.indexOf(data.value)
if(thisIndex>-1){
$scope.currentDatasDown.splice(thisIndex,1);
}
};
},
link: function (scope, ele, attrs) {
}
};
})
</script>
三、on-off组件(真实逻辑,点击后,转圈、向后台发送请求、返回数据、给开关赋值、开关展示状态。点击后,立即改成相反状态,是不对的。)
<html lang="en" ng-app="myModel">
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript" src="https:cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
<style>
table{
border-collapse: collapse;
width:1000px;
}
table td,table th {
padding: 5px;
border: 1px solid #cbcbcb;
}
table thead {
background-color: #e0e0e0;
color: #000;
text-align: left;
}
</style>
</head>
<body>
<div ng-controller="thisCtrl">
<on-off this-switch="parentSwitch">
<span>{{parentSwitch.state?"总开关已开启":"总开关已关闭"}}。总开关关闭时,不能操作分开关!</span>
</on-off>
<table>
<thead>
<tr>
<th>序号</th>
<th>数据1</th>
<th>数据2</th>
<th>数据3</th>
<th>开关1</th>
<th>开关2</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in tableDatas track by $index">
<td ng-bind="$index+1"></td>
<td ng-bind="item.value1"></td>
<td ng-bind="item.value2"></td>
<td ng-bind="item.value3"></td>
<td>
<on-off parent-switch="parentSwitch" this-switch="item.value4"></on-off>
</td>
<td>
<on-off parent-switch="parentSwitch" this-switch="item.value5"></on-off>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
<script>
var app = angular.module('myModel', []);
app.controller('thisCtrl', function ($scope) {
//以下通过向后台请求,获取总开关的数据fromServerParent,经加工后为$scope.parentSwitch
var fromServerParent={
isOn:true
};
$scope.parentSwitch={
state:fromServerParent.isOn,
callback:function(){
this.state=!this.state;
}
};
//以下通过向后台请求,获取分开关的数据fromServerData,经加工后为$scope.tableDatas
var fromServerData=[
{value1:"数据1",value2:"数据2",value3:"数据3",isOn:true,isSwitch:true},
{value1:"数据1",value2:"数据2",value3:"数据3",isOn:true,isSwitch:true},
{value1:"数据1",value2:"数据2",value3:"数据3",isOn:true,isSwitch:true},
{value1:"数据1",value2:"数据2",value3:"数据3",isOn:true,isSwitch:true},
{value1:"数据1",value2:"数据2",value3:"数据3",isOn:true,isSwitch:true},
{value1:"数据1",value2:"数据2",value3:"数据3",isOn:true,isSwitch:true},
{value1:"数据1",value2:"数据2",value3:"数据3",isOn:true,isSwitch:true}
];
var tableDatas=[];
angular.forEach(fromServerData,function(value,key){
var item={};
item.value1=value.value1;
item.value2=value.value2;
item.value3=value.value3;
item.value4={
state:value.isOn,
callback:function(){
this.state=!this.state;
}
};
item.value5={
state:value.isSwitch,
callback:function(){
this.state=!this.state;
}
};
tableDatas.push(item)
})
$scope.tableDatas=tableDatas;
});
app.directive('onOff', function () {//(含插槽)
var html = `
<div style="display:flex;align-content:center;padding-bottom:10px;">
<img ng-src="{{thisSwitch.state?onOff.yes:onOff.no}}" width="65px" height="30px" ng-click="clickSwitch()"/>
<span ng-transclude style="height:30px;line-height:30px;padding-left:10px;"></span>
</div>
`;
return {
restrict: 'E',
template: html,
scope: {
thisSwitch: '=thisSwitch',
parentSwitch: '=parentSwitch',
},
transclude:true,
replace: false,
controller: function ($scope,onOff) {
$scope.onOff = onOff;
$scope.clickSwitch = function(){
//以下总开关关闭时,不能操作分开关!
if($scope.parentSwitch && !$scope.parentSwitch.state){
return '弹窗:父级开关阻止本级开关改变'
}
if($scope.thisSwitch.isForbid ){
return '弹窗:本级数据的其它条件阻止本级开关改变'
}
$scope.thisSwitch.callback()
};
},
link: function (scope, ele, attr) {
}
};
});
app.factory('onOff', function () {
return {
yes: 'data:image/png;base64,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',
no: 'data:image/png;base64,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',
};
})
</script>
四、simple-dialog
1、不可拖拽(含插槽)
<!DOCTYPE html>
<html lang="en" ng-app="myModel">
<head>
<meta charset="UTF-8">
<title>三层不可拖拽弹窗之angular1.6.2版</title>
<script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
<style>
.simpleDialog {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
}
.simpleDialog .mask {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: black;
opacity: 0.5;
}
.simpleDialog .content {
position: fixed;
background: white;
opacity: 1;
display: flex;
flex-direction: column;
}
.simpleDialog .content .title {
display: flex;
background: blue;
color: white;
padding: 10px;
cursor: pointer;
user-select: none;
}
.simpleDialog .content .conform {
display: flex;
justify-content: center;
padding: 10px;
background: blue;
}
</style>
</head>
<body>
<div ng-controller="thisCtrl">
<button ng-click="clickButton()" style="margin-top: 30px; z-index: 250">
点击-出现-弹窗
</button>
<simple-dialog required-data="requiredDataOut">
1111111
<simple-dialog required-data="requiredDataMid">
2222222
<simple-dialog required-data="requiredDataIn">
33333333
</simple-dialog>
</simple-dialog>
</simple-dialog>
</div>
</body>
<script>
var app = angular.module('myModel', []);
app.controller('thisCtrl', function ($scope) {
$scope.clickButton = function () {
$scope.requiredDataOut.isShow = true;
};
$scope.requiredDataOut = {
isShow : false,
width : '900px',
height : '600px',
openFn : function () {
$scope.requiredDataMid.isShow = true;
}
};
$scope.requiredDataMid = {
isShow : false,
width : '600px',
height : '400px',
openFn : function () {
$scope.requiredDataIn.isShow = true;
},
};
$scope.requiredDataIn = {
isShow : false,
width : '300px',
height : '200px',
};
// 一层弹窗可以如下使用
// $scope.requiredDataOut = {
// isShow : false,
// };
// $scope.clickButton = function () {
// $scope.requiredDataOut.isShow = true;
// };
});
app.directive('simpleDialog', function () {
var html = `
<div class="simpleDialog" ng-show="requiredData.isShow">
<div class="mask" ng-show="requiredData.isShow"></div>
<div class="content" ng-show="requiredData.isShow">
<div class="title">
<span>系统消息</span>
</div>
<div ng-transclude ng-style="{width:requiredData.width||'800px',height:requiredData.height||'400px'}"></div>
<div class="conform">
<button ng-click="close()">关闭</button>
<button ng-click="open()">打开</button>
</div>
<div>
</div>
`;
return {
restrict: 'E',
template: html,
transclude: true,
scope: {
requiredData: '='
},
controller: function ($scope) {
$scope.close = function () {
$scope.requiredData.isShow = false;
if($scope.requiredData.closeFn)$scope.requiredData.closeFn();
};
$scope.open = function () {
if($scope.requiredData.openFn)$scope.requiredData.openFn();
};
}
};
});
</script>
</html>
2、可拖拽(含插槽)
<!DOCTYPE html>
<html lang="en" ng-app="myModel">
<head>
<meta charset="UTF-8">
<title>三层可拖拽弹窗之angular1.6.2版</title>
<script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
<style>
.simpleDialog {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
}
.simpleDialog .mask {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: black;
opacity: 0.5;
}
.simpleDialog .content {
position: fixed;
background: white;
opacity: 1;
display: flex;
flex-direction: column;
}
.simpleDialog .content .title {
display: flex;
background: blue;
color: white;
padding: 10px;
cursor: pointer;
user-select: none;
}
.simpleDialog .content .conform {
display: flex;
justify-content: center;
padding: 10px;
background: blue;
}
</style>
</head>
<body>
<div ng-controller="thisCtrl">
<button ng-click="clickButton()" style="margin-top: 30px; z-index: 250">
点击-出现-弹窗
</button>
<simple-dialog required-data="requiredDataOut">
1111111
<simple-dialog required-data="requiredDataMid">
2222222
<simple-dialog required-data="requiredDataIn">
</simple-dialog>
</simple-dialog>
</simple-dialog>
</div>
</body>
<script>
var app = angular.module('myModel', []);
app.controller('thisCtrl', function ($scope, drag) {
$scope.clickButton = function () {
$scope.requiredDataOut.isShow = true;
drag($scope.requiredDataOut.titleId,$scope.requiredDataOut.contentId);
};
$scope.requiredDataOut = {
isShow : false,
width : '900px',
height : '600px',
titleId : "titleId1",
contentId : "contentId1",
openFn : function () {
$scope.requiredDataMid.isShow = true;
drag($scope.requiredDataMid.titleId,$scope.requiredDataMid.contentId);
}
};
$scope.requiredDataMid = {
isShow : false,
width : '600px',
height : '400px',
titleId : "titleId2",
contentId : "contentId2",
openFn : function () {
$scope.requiredDataIn.isShow = true;
drag($scope.requiredDataIn.titleId,$scope.requiredDataIn.contentId);
},
};
$scope.requiredDataIn = {
isShow : false,
width : '300px',
height : '200px',
titleId : "titleId3",
contentId : "contentId3",
};
// 一层弹窗可以如下使用
// $scope.requiredDataOut = {
// isShow : false,
// };
// $scope.clickButton = function () {
// $scope.requiredDataOut.isShow = true,
// drag();
// };
});
app.directive('simpleDialog', function () {
var html = `
<div class="simpleDialog" ng-show="requiredData.isShow">
<div class="mask" ng-show="requiredData.isShow"></div>
<div class="content" ng-show="requiredData.isShow" id="{{requiredData.contentId||'contentId'}}">
<div class="title" id="{{requiredData.titleId||'titleId'}}">
<span>系统消息</span>
</div>
<div ng-transclude ng-style="{width:requiredData.width||'800px',height:requiredData.height||'400px'}">后备内容</div>
<div class="conform">
<button ng-click="close()">关闭</button>
<button ng-click="open()">打开</button>
</div>
<div>
</div>
`;
return {
restrict: 'E',
template: html,
transclude: true,
scope: {
requiredData: '='
},
controller: function ($scope) {
$scope.close = function () {
$scope.requiredData.isShow = false;
angular
.element(
document.getElementById($scope.requiredData.contentId||'contentId')
)
.css({
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
});
if($scope.requiredData.closeFn)$scope.requiredData.closeFn();
};
$scope.open = function () {
if($scope.requiredData.openFn)$scope.requiredData.openFn();
};
}
};
});
app.factory('drag', function () {
return function (wholeTitleId, wholeContentId) {
var wholeTitleId = wholeTitleId||'titleId';
var wholeContentId = wholeContentId||'contentId';
var oDiv = document.getElementById(wholeContentId);
oDiv.onmousedown = down;
function processThis(fn, nowThis) {
return function (event) {
fn.call(nowThis, event);
};
}
function down(event) {
event = event || window.event;
if (event.target.id != wholeTitleId) return;
this.initOffsetLeft = this.offsetLeft;
this.initOffsetTop = this.offsetTop;
this.initClientX = event.clientX;
this.initClientY = event.clientY;
this.maxOffsetWidth =
(document.documentElement.clientWidth || document.body.clientWidth) -
this.offsetWidth;
this.maxOffsetHeight =
(document.documentElement.clientHeight ||
document.body.clientHeight) - this.offsetHeight;
if (this.setCapture) {
this.setCapture();
this.onmousemove = processThis(move, this);
this.onmouseup = processThis(up, this);
} else {
document.onmousemove = processThis(move, this);
document.onmouseup = processThis(up, this);
}
}
function move(event) {
var nowLeft = this.initOffsetLeft + (event.clientX - this.initClientX);
var nowTop = this.initOffsetTop + (event.clientY - this.initClientY);
//此处可以根据实际需要,限制拖动边界
this.style.left = nowLeft + 'px';
this.style.top = nowTop + 'px';
}
function up() {
if (this.releaseCapture) {
this.releaseCapture();
this.onmousemove = null;
this.onmouseup = null;
} else {
document.onmousemove = null;
document.onmouseup = null;
}
}
};
});
</script>
</html>
五、3种searchSelect搜索下拉
1、searchSelectFull
<!DOCTYPE html>
<html lang="en" ng-app="myModel">
<head>
<meta charset="UTF-8">
<title>searchSelectFull</title>
<script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
<style>
.outer-div{
display: flex;
flex-direction: column;
flex-wrap: wrap;
align-items: top;
border: 1px solid #7b8ea5;
border-radius: 6px;
cursor: pointer;
width: 70%;
user-select: none;
}
.select-div{
height: 120px;
padding: 5px 0 0 5px;
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
overflow-y: auto;
}
.item-summary{
border: 1px solid #dddddd;
padding: 5px 10px;
background: #fafafa;
margin-right: 5px;
margin-bottom: 5px;
border-radius: 4px;
height:24px;
color:#000;
}
.option-all{
position: absolute;
border: 2px solid #dddddd;
width:444px;
background:#ffffff;
z-index: 10;
padding:6px
}
.option-div{
padding: 5px 10px;
background: #fafafa;
margin-right: 5px;
margin-bottom: 5px;
border-radius: 4px;
text-align: left;
}
.option-div:hover {
background: #cccccc !important;
}
.yes-selected{
background:#237eff;
border-radius:4px;
color:#ffffff
}
.no-selected{
background:#ffffff;
border-radius:0;
border-bottom:1px solid #e7e7e7;
color:#000000
}
</style>
</head>
<body>
<div ng-controller="thisCtrl">
<button ng-click="getAllSelectedOptions()">点击获取被选中项</button>
<duv>{{result}}</duv>
<search-select-full protocol-type="protocolType"></search-select-full>
</div>
</body>
</html>
<script>
var app = angular.module('myModel', []);
app.controller('thisCtrl', function ($scope) {
$scope.protocolType = {
"yesOptions": [
"c1222",
"cnip",
"dnp3",
"dns",
"doip",
"egd",
"enip",
"enip-io",
"fins",
"ftp",
"ftp-data",
"ge-srtp",
"goose",
"hartip",
"http",
"iec104",
"knxip",
"mms",
"modbus",
"opcda",
"opcua",
"pn-dcp",
"pn-mrrt",
"pn-ptcp",
"pop3",
"rdp",
"s7",
"s7-plus",
"sercos",
"smb",
"smb2",
"smtp",
"sv",
"telnet"
],
"allOptions": [
{
"server": "c1222",
"client": "c1222"
},
{
"server": "cnip",
"client": "cnip"
},
{
"server": "dnp3",
"client": "dnp3"
},
{
"server": "dns",
"client": "dns"
},
{
"server": "doip",
"client": "doip"
},
{
"server": "egd",
"client": "egd"
},
{
"server": "enip",
"client": "enip"
},
{
"server": "enip-io",
"client": "enip-io"
},
{
"server": "fins",
"client": "fins"
},
{
"server": "ftp",
"client": "ftp"
},
{
"server": "ftp-data",
"client": "ftp-data"
},
{
"server": "ge-srtp",
"client": "ge-srtp"
},
{
"server": "goose",
"client": "goose"
},
{
"server": "hartip",
"client": "hartip"
},
{
"server": "http",
"client": "http"
},
{
"server": "iec104",
"client": "iec104"
},
{
"server": "knxip",
"client": "knxip"
},
{
"server": "mms",
"client": "mms"
},
{
"server": "modbus",
"client": "modbus"
},
{
"server": "opcda",
"client": "opcda"
},
{
"server": "opcua",
"client": "opcua"
},
{
"server": "pn-dcp",
"client": "pn-dcp"
},
{
"server": "pn-mrrt",
"client": "pn-mrrt"
},
{
"server": "pn-ptcp",
"client": "pn-ptcp"
},
{
"server": "pop3",
"client": "pop3"
},
{
"server": "rdp",
"client": "rdp"
},
{
"server": "s7",
"client": "s7"
},
{
"server": "s7-plus",
"client": "s7-plus"
},
{
"server": "sercos",
"client": "sercos"
},
{
"server": "smb",
"client": "smb"
},
{
"server": "smb2",
"client": "smb2"
},
{
"server": "smtp",
"client": "smtp"
},
{
"server": "sv",
"client": "sv"
},
{
"server": "telnet",
"client": "telnet"
}
],
"isFull": true
}
$scope.getAllSelectedOptions = function(){
var values = $scope.protocolType.getValues();
$scope.result = JSON.stringify(values)
};
});
app.directive('searchSelectFull', function () {
var html = `
<div class="outer-div"
tabindex="-1"
ng-blur="getBlurOut()">
<!-- 以下是select -->
<div style="display: flex;"
ng-click="getOptions($event)">
<div class="select-div">
<div class="item-summary">已选择{{yesOptions.length}}条</div>
<div ng-repeat="client in yesOptions track by $index" class="item-summary">
<span ng-bind="client" style="position:relative;top:-7px"></span>
<img ng-click="clickFork(yesOptions,client)" class="onlyQC" src="{{checkImg.fork}}" width="24px" height="24px" />
</div>
<div class="item-summary" ng-show="yesOptions.length>20">已选择{{yesOptions.length}}条</div>
</div>
</div>
<!-- 以下是option -->
<div style="position: relative" ng-show="isShowOptions">
<div class="option-all">
<div>
<input type="text"
class="form-control"
style="width:300px;height:44px;border-radius:4px;margin:4px 0"
ng-focus="getFocusInput()"
ng-blur="getBlurInput()"
ng-change="changeInputValue()"
ng-model="selectedOption"/>
<span style="padding-bottom: 5px;display:inline-block;width:112px;" ng-style="{'visibility': isShowSelectAll? 'visible':'hidden'}">
<img ng-click="selectAll()" src="{{isSelectAll?checkImg.yes:checkImg.no}}" width="16px" height="16px"/>
<span>{{textSelectAll}}</span>
</span>
</div>
<div style="max-height: 360px;overflow: auto;padding-left: 6px;">
<div ng-repeat="option in filterOptions track by $index" class="option-div"
ng-click="clickOptions(option)"
ng-class="yesOptions.indexOf(option.client)>-1?'yes-selected':'no-selected'">
<span ng-bind="option.client"
style="height:34px;line-height:34px;display: inline-block;"></span>
</div>
</div>
</div>
</div>
</div>
`;
return {
restrict: 'E',
template: html,
scope: {
protocolType: '=protocolType',//所有选项
},
replace: true,
controller: function ($scope,$timeout,checkImg) {
$scope.checkImg = checkImg;
$scope.isSelectAll = true;
$scope.isShowSelectAll = true;
$scope.isFocusInput = false;
$scope.isShowOptions = false;
$scope.isClickOptions = false;
$scope.isClickSelectAll = false;
$scope.yesOptions = $scope.protocolType.yesOptions;
$scope.allOptions = $scope.protocolType.allOptions;
$scope.filterOptions = angular.copy($scope.allOptions);
$scope.textSelectAll = "全部选择";
if($scope.isSelectAll){
$scope.textSelectAll = "已选择"+$scope.yesOptions.length+"条";
}
$scope.clickFork = function (yesOptions,client) {
angular.forEach(yesOptions,function(option,index){
if(client===option){
yesOptions.splice(index,1);
$scope.textSelectAll = "已选择"+$scope.yesOptions.length+"条";
}
})
};
$scope.selectAll = function () {
$scope.isClickSelectAll = true;
$scope.yesOptions = [];
$scope.isSelectAll = !$scope.isSelectAll;
if($scope.isSelectAll){
angular.forEach($scope.allOptions,function(option){
$scope.yesOptions.push(option.client)
})
$scope.textSelectAll = "已选择"+$scope.yesOptions.length+"条";
}else{
$scope.textSelectAll = "全部选择";
}
$timeout(function(){
$scope.isClickSelectAll = false;
},300)
};
$scope.getOptions = function (event) {
if(event.target.className === "onlyQC")return;
$scope.isShowOptions = !$scope.isShowOptions;
};
//组件失焦时,执行下面函数。想一想,什么时候组件聚焦?
$scope.getBlurOut = function () {
$timeout(function(){
if(!$scope.isFocusInput){
$scope.isShowOptions = false;
}
})
};
$scope.getFocusInput = function () {
$scope.isFocusInput = true;
};
//输入框失焦时,执行下面函数
$scope.getBlurInput = function () {
$scope.isFocusInput = false;
$timeout(function(){
if(!$scope.isClickOptions && !$scope.isClickSelectAll){
$scope.isShowOptions = false;
}
},200)
};
$scope.clickOptions = function (option) {
var flag = true;
$scope.isClickOptions = true;
angular.forEach($scope.yesOptions,function(client){
if(option.client===client){
flag = false;
$scope.yesOptions.splice($scope.yesOptions.indexOf(client),1);
}
});
if(flag){
$scope.yesOptions.push(option.client);
}
$scope.textSelectAll = "已选择"+$scope.yesOptions.length+"条";
$timeout(function(){
$scope.isClickOptions = false;
},300)
};
$scope.changeInputValue = function () {
if($scope.selectedOption){
$scope.isShowSelectAll = false;
$scope.filterOptions = [];
angular.forEach($scope.allOptions,function(option){
if(option.client.indexOf($scope.selectedOption) != -1){
$scope.filterOptions.push(option);
}
});
}else{
$scope.isShowSelectAll = true;
$scope.filterOptions = angular.copy($scope.allOptions);
}
};
$scope.protocolType.getValues = function () {
var client = [];
var server = [];
var clientNo = [];
var serverNo = [];
var obj;
angular.forEach($scope.allOptions,function(option){
if($scope.yesOptions.indexOf(option.client) != -1){
client.push(option.client)
server.push(option.server)
}else{
clientNo.push(option.client)
serverNo.push(option.server)
}
});
if($scope.isSelectAll){
obj = {
is_all:1,
client:clientNo,
server:serverNo,
}
}else{
obj = {
is_all:0,
client:client,
server:server,
}
}
return obj
};
},
link: function (scope, ele, attrs) {
}
};
});
app.factory('checkImg', function () {
return {
fork:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAWCAYAAAArdgcFAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEBSURBVEhL7ZKxSwMxFMb9/5dwS0T0DocbhBbhhg4Hwg0OglC5QpGDDsJZSgsKD26oZPlsaCPpu5dUaF2kw2954f2+kC8Xxhj8FWe5yD+Vr5oxmsW6f0Yt6kkL4nNGUN4+5tBKQSUpHt68AKpxf7mZb870sI4GyPJlhdSKHS7AEzvyJ+rv7wjcnFAP9Z5EJRpXTKyyEjOS9rdE3lwI8DkgtkQLDQbcjA6KLXE5TVFcM7GFlxwgLA+JHb8IkOVdgxEXZwXKO15yimou7O+Q5Z/PyBNP8lMe70CjeGW7HuFnea9wawN6v8IFaAxePrx5n3ihHaH7EuZmDaJjCj0BZ7mAwTfR6GSuYmrILwAAAABJRU5ErkJggg==',
yes:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADqADAAQAAAABAAAADgAAAAC98Dn6AAAA+UlEQVQoFZWSMU4DMRBF/584G7QSRcIxuAZKEykNEiUVHVTQRaKh4AIcgAvQpkukVDlBOAYNSGSlXXuwpViyYYFdS9aMZ/6bsezh5HZ3T2KhqkfosEhWqnjkyd1u3xWKdQMsfaEAB0Zilf8swfdU0w0klmpGpz1BvpbHcklbPf8Okts0CfJtWBTz/Yc++Jc8S3PZVQfKGwiuvMD6XYsMzm1dT/1jXKdQ8E0asHRrAzOzbC6UGINWHPQp1UQ/6wjF2LpmJSKfhti4Bi8+lhWP4I+gAqV1uqSi8j9WRuF3m3eMWVUJBeKxzUoYn7bEX7HDyPmB7QEHbRjyL+/+VnuXDUFOAAAAAElFTkSuQmCC',
no:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADqADAAQAAAABAAAADgAAAAC98Dn6AAAAbklEQVQoFWM8c+ZMLQMDQxUQcwAxMeAHUFEbC5CoYmNj02ZmZn5FjK6/f/+K/fr16ypIIwdIk7a29hdiNF69ehWkjIOJGMXY1IxqxBYqULEhFDiglPMDlIygKQKPryBSILUgPSCNbaC0B6RJSuQAbowizhJuOsAAAAAASUVORK5CYII='
};
});
</script>
2、searchSelectMid
<!DOCTYPE html>
<html lang="en" ng-app="myModel">
<head>
<meta charset="UTF-8">
<title>searchSelectMid</title>
<script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
<style>
.outer-div{
width:210px;
height:36px;
cursor: pointer;
}
.select-div{
border: 1px solid #dddddd;
padding: 5px 10px;
background: #fafafa;
margin-right: 5px;
border-radius: 4px;
height:24px;
color:#000;
}
.option-both{
position: absolute;
border: 2px solid #dddddd;
width:444px;
background:#ffffff;
z-index: 10;
padding:6px
}
.option-filter{
padding: 5px 10px;
background: #fafafa;
margin-right: 5px;
margin-bottom: 5px;
border-radius: 4px;
text-align: left;
}
.option-filter:hover {
background: #cccccc !important;
}
.option-summary{
max-height: 360px;
padding: 4px;
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
overflow-y: auto;
}
.yes-selected{
background:#237eff;
border-radius:4px;
color:#ffffff
}
.no-selected{
background:#ffffff;
border-radius:0;
border-bottom:1px solid #e7e7e7;
color:#000000
}
</style>
</head>
<body>
<div ng-controller="thisCtrl">
<button ng-click="getAllSelectedOptions()">点击获取被选中项</button>
<duv>{{result}}</duv>
<search-select-mid protocol-type="protocolType"></search-select-mid>
</div>
</body>
</html>
<script>
var app = angular.module('myModel', []);
app.controller('thisCtrl', function ($scope) {
$scope.protocolType = {
"yesOptions": [
"c1222",
"cnip",
"dnp3",
"dns",
"doip",
"egd",
"enip",
"enip-io",
"fins",
"ftp",
"ftp-data",
"ge-srtp",
"goose",
"hartip",
"http",
"iec104",
"knxip",
"mms",
"modbus",
"opcda",
"opcua",
"pn-dcp",
"pn-mrrt",
"pn-ptcp",
"pop3",
"rdp",
"s7",
"s7-plus",
"sercos",
"smb",
"smb2",
"smtp",
"sv",
"telnet"
],
"allOptions": [
{
"server": "c1222",
"client": "c1222"
},
{
"server": "cnip",
"client": "cnip"
},
{
"server": "dnp3",
"client": "dnp3"
},
{
"server": "dns",
"client": "dns"
},
{
"server": "doip",
"client": "doip"
},
{
"server": "egd",
"client": "egd"
},
{
"server": "enip",
"client": "enip"
},
{
"server": "enip-io",
"client": "enip-io"
},
{
"server": "fins",
"client": "fins"
},
{
"server": "ftp",
"client": "ftp"
},
{
"server": "ftp-data",
"client": "ftp-data"
},
{
"server": "ge-srtp",
"client": "ge-srtp"
},
{
"server": "goose",
"client": "goose"
},
{
"server": "hartip",
"client": "hartip"
},
{
"server": "http",
"client": "http"
},
{
"server": "iec104",
"client": "iec104"
},
{
"server": "knxip",
"client": "knxip"
},
{
"server": "mms",
"client": "mms"
},
{
"server": "modbus",
"client": "modbus"
},
{
"server": "opcda",
"client": "opcda"
},
{
"server": "opcua",
"client": "opcua"
},
{
"server": "pn-dcp",
"client": "pn-dcp"
},
{
"server": "pn-mrrt",
"client": "pn-mrrt"
},
{
"server": "pn-ptcp",
"client": "pn-ptcp"
},
{
"server": "pop3",
"client": "pop3"
},
{
"server": "rdp",
"client": "rdp"
},
{
"server": "s7",
"client": "s7"
},
{
"server": "s7-plus",
"client": "s7-plus"
},
{
"server": "sercos",
"client": "sercos"
},
{
"server": "smb",
"client": "smb"
},
{
"server": "smb2",
"client": "smb2"
},
{
"server": "smtp",
"client": "smtp"
},
{
"server": "sv",
"client": "sv"
},
{
"server": "telnet",
"client": "telnet"
}
],
"isFull": true
}
$scope.getAllSelectedOptions = function(){
var values = $scope.protocolType.getValues();
$scope.result = JSON.stringify(values)
};
});
app.directive('searchSelectMid', function () {
var html = `
<div
tabindex="-1"
class="outer-div"
ng-blur="outerBlur()"
ng-click="outerFocus()"
ng-mouseleave="outerLeave()"
style="
width:210px;
height:36px;
cursor: pointer;padding:0">
<!-- 以下是select -->
<div ng-mouseenter="enterSelect()" ng-click="clickSelect()" style="padding:4px 0 0 10px;height:34px;" class="select-div">
<span ng-show="yesOptions[0]" ng-bind="yesOptions[0]"></span>
<span ng-show="yesOptions.length>1" ng-bind="' ......'"></span>
<span ng-show="yesOptions.length==0">
<span style="color:#87a6c9;">请单选或多选或滤选</span>
</span>
</div>
<!-- 以下是option -->
<div style="position: relative;" ng-show="isShowOptions||isShowSummary">
<div style="position: absolute;border: 1px solid #818080;width:300px;background:#ffffff;z-index: 10;padding:6px" ng-show="!isShowSummary">
<div>
<input type="text"
style="width:156px;height:32px;border-radius:4px;margin:4px 0;padding-left: 8px;"
placeholder="请输入过滤条件"
ng-change="changeInputValue()"
ng-model="selectedOption" />
<span style="padding-bottom: 5px;display:inline-block;width:112px;" ng-style="{'visibility': isShowSelectAll? 'visible':'hidden'}">
<img ng-click="selectAll()" src="{{isSelectAll?checkImg.yes:checkImg.no}}" width="16px" height="16px"/>
<span>{{textSelectAll}}</span>
</span>
</div>
<div style="max-height: 360px;overflow: auto;" class="youSelect-option">
<div ng-repeat="option in filterOptions track by $index" style="
padding: 2px 8px;
background: #fafafa;
margin-right: 5px;
margin-bottom: 1px;
text-align: left;
"
ng-click="clickOptions(option)"
ng-style="yesOptions.indexOf(option.client)>-1?{'background':'#237eff','color':'#ffffff'}:{'background':'#ffffff','color':'#495057'}">
<span ng-bind="option.client"
style="height:24px;line-height:24px;display: inline-block;"></span>
<i class="fa"
style="float:right;position: relative;top:5px"
ng-show="yesOptions.indexOf(option.client)>-1"></i>
</div>
</div>
</div>
<div style="position: absolute;border: 1px solid #818080;width:300px;background:#ffffff;z-index: 10;padding:6px;box-shadow: 10px 10px 5px #888888;" ng-show="isShowSummary && yesOptions.length>0">
<div style="
max-height: 360px;
padding: 4px;
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
overflow-y: auto;
">
<span style="position:relative;top:4px;">{{'已选'+yesOptions.length+'条 '}}</span>
<span ng-repeat="client in yesOptions track by $index"
style="
padding: 4px;
background: #fafafa;
margin-right: 5px;
"
ng-bind="client" style="color: #000"
></span>
</div>
</div>
</div>
</div>
`;
return {
restrict: 'E',
template: html,
scope: {
protocolType: '=protocolType',//所有选项
},
replace: true,
controller: function ($scope,$timeout,checkImg) {
$scope.checkImg = checkImg;
$scope.isShowSummary = false; //是否显示已选项汇总
$scope.isShowSelectAll = true; //是否显示全选
$scope.isShowOptions = false; //是否显示下拉项
$scope.isFocus = false;
$scope.isSelectAll = $scope.protocolType.isFull||false; //是否全选
$scope.yesOptions = angular.copy($scope.protocolType.yesOptions);
$scope.allOptions = angular.copy($scope.protocolType.allOptions);
$scope.filterOptions = angular.copy($scope.allOptions);
$scope.textSelectAll = "全部选择";
if($scope.isSelectAll){
$scope.textSelectAll = "已选择"+$scope.yesOptions.length+"条";
}
$scope.selectAll = function () {
$scope.yesOptions = [];
$scope.isSelectAll = !$scope.isSelectAll;
if($scope.isSelectAll){
angular.forEach($scope.allOptions,function(option){
$scope.yesOptions.push(option.client)
})
$scope.textSelectAll = "已选择"+$scope.yesOptions.length+"条";
}else{
$scope.textSelectAll = "全部选择";
}
};
$scope.enterSelect = function () {
if($scope.isShowOptions){
$scope.isShowSummary = false;
}else{
$scope.isShowSummary = true;
}
};
$scope.clickSelect = function () {
$scope.isShowSummary = false;
$scope.isShowOptions = !$scope.isShowOptions;
};
$scope.outerLeave = function () {
$scope.isShowSummary = false;
};
$scope.outerFocus = function () {
$scope.isFocus = true;
$timeout(function(){
$scope.isFocus = false;
},200)
};
$scope.outerBlur = function () {
$timeout(function(){
if(!$scope.isFocus){//域外失焦
$scope.isShowSummary = false;
$scope.isShowOptions = false;
}
},150)
};
$scope.clickOptions = function (option) {
var flag = true;
angular.forEach($scope.yesOptions,function(client){
if(option.client===client){
flag = false;
$scope.yesOptions.splice($scope.yesOptions.indexOf(client),1);
}
});
if(flag){
$scope.yesOptions.push(option.client);
}
$scope.textSelectAll = "已选择"+$scope.yesOptions.length+"条";
};
$scope.changeInputValue = function () {
if($scope.selectedOption){
$scope.isShowSelectAll = false;
$scope.filterOptions = [];
angular.forEach($scope.allOptions,function(option){
if(option.client.indexOf($scope.selectedOption) != -1){
$scope.filterOptions.push(option);
}
});
}else{
$scope.isShowSelectAll = true;
$scope.filterOptions = angular.copy($scope.allOptions);
}
};
$scope.protocolType.getValues = function () {
var client = [];
var server = [];
var clientNo = [];
var serverNo = [];
var obj;
angular.forEach($scope.allOptions,function(option){
if($scope.yesOptions.indexOf(option.client) != -1){
client.push(option.client)
server.push(option.server)
}else{
clientNo.push(option.client)
serverNo.push(option.server)
}
});
if($scope.isSelectAll){
obj = {
is_all:1,
client:clientNo,
server:serverNo,
}
}else{
obj = {
is_all:0,
client:client,
server:server,
}
}
return obj
};
$scope.protocolType.empty = function (array) {
$scope.yesOptions = array||[];
$scope.textSelectAll = "全部选择";
$scope.isSelectAll = false;
};
$scope.protocolType.full = function () {
$scope.isSelectAll = true;
$scope.selectedOption = '';
$scope.changeInputValue();
$scope.yesOptions = angular.copy($scope.protocolType.yesOptions);
$scope.textSelectAll = "已选择"+$scope.yesOptions.length+"条";
};
},
link: function (scope, ele, attrs) {
}
};
});
app.factory('checkImg', function () {
return {
yes:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADqADAAQAAAABAAAADgAAAAC98Dn6AAAA+UlEQVQoFZWSMU4DMRBF/584G7QSRcIxuAZKEykNEiUVHVTQRaKh4AIcgAvQpkukVDlBOAYNSGSlXXuwpViyYYFdS9aMZ/6bsezh5HZ3T2KhqkfosEhWqnjkyd1u3xWKdQMsfaEAB0Zilf8swfdU0w0klmpGpz1BvpbHcklbPf8Okts0CfJtWBTz/Yc++Jc8S3PZVQfKGwiuvMD6XYsMzm1dT/1jXKdQ8E0asHRrAzOzbC6UGINWHPQp1UQ/6wjF2LpmJSKfhti4Bi8+lhWP4I+gAqV1uqSi8j9WRuF3m3eMWVUJBeKxzUoYn7bEX7HDyPmB7QEHbRjyL+/+VnuXDUFOAAAAAElFTkSuQmCC',
no:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADqADAAQAAAABAAAADgAAAAC98Dn6AAAAbklEQVQoFWM8c+ZMLQMDQxUQcwAxMeAHUFEbC5CoYmNj02ZmZn5FjK6/f/+K/fr16ypIIwdIk7a29hdiNF69ehWkjIOJGMXY1IxqxBYqULEhFDiglPMDlIygKQKPryBSILUgPSCNbaC0B6RJSuQAbowizhJuOsAAAAAASUVORK5CYII='
};
});
</script>
3、searchSelectEmpty
<!DOCTYPE html>
<html lang="en" ng-app="myModel">
<head>
<meta charset="UTF-8">
<title>searchSelectEmpty</title>
<script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
<style>
.outer-div{
width:300px;
height:36px;
cursor: pointer;
}
.select-div{
border:1px solid #cccccc;
border-radius: 6px;
}
.option-outer{
position: absolute;
border: 2px solid #dddddd;
width:444px;
background:#ffffff;
z-index: 10;
padding:6px
}
.option-inner{
padding: 5px 10px;
background: #fafafa;
margin-right: 5px;
margin-bottom: 5px;
border-radius: 4px;
text-align: left;
border-bottom: 1px solid #e7e7e7;
}
.option-inner:hover {
background: #cccccc !important;
}
.form-input{
width:180px;
height:34px;
border-radius: 16px;
border:1px solid #ffffff;
font-size: 16px;
padding-left: 20px;
}
.form-input:focus {
outline:none
}
</style>
</head>
<body>
<div ng-controller="thisCtrl">
<button ng-click="getAllSelectedOptions()">点击获取被选中项</button>
<duv>{{result}}</duv>
<search-select-empty server-value="serverValue" protocol-type="protocolType"></search-select-empty>
</div>
</body>
</html>
<script>
var app = angular.module('myModel', []);
app.controller('thisCtrl', function ($scope) {
$scope.serverValue = '';
$scope.protocolType = [
{
"server": "c1222",
"client": "c1222"
},
{
"server": "cnip",
"client": "cnip"
},
{
"server": "dnp3",
"client": "dnp3"
},
{
"server": "dns",
"client": "dns"
},
{
"server": "doip",
"client": "doip"
},
{
"server": "egd",
"client": "egd"
},
{
"server": "enip",
"client": "enip"
},
{
"server": "enip-io",
"client": "enip-io"
},
{
"server": "fins",
"client": "fins"
},
{
"server": "ftp",
"client": "ftp"
},
{
"server": "ftp-data",
"client": "ftp-data"
},
{
"server": "ge-srtp",
"client": "ge-srtp"
},
{
"server": "goose",
"client": "goose"
},
{
"server": "hartip",
"client": "hartip"
},
{
"server": "http",
"client": "http"
},
{
"server": "iec104",
"client": "iec104"
},
{
"server": "knxip",
"client": "knxip"
},
{
"server": "mms",
"client": "mms"
},
{
"server": "modbus",
"client": "modbus"
},
{
"server": "opcda",
"client": "opcda"
},
{
"server": "opcua",
"client": "opcua"
},
{
"server": "pn-dcp",
"client": "pn-dcp"
},
{
"server": "pn-mrrt",
"client": "pn-mrrt"
},
{
"server": "pn-ptcp",
"client": "pn-ptcp"
},
{
"server": "pop3",
"client": "pop3"
},
{
"server": "rdp",
"client": "rdp"
},
{
"server": "s7",
"client": "s7"
},
{
"server": "s7-plus",
"client": "s7-plus"
},
{
"server": "sercos",
"client": "sercos"
},
{
"server": "smb",
"client": "smb"
},
{
"server": "smb2",
"client": "smb2"
},
{
"server": "smtp",
"client": "smtp"
},
{
"server": "sv",
"client": "sv"
},
{
"server": "telnet",
"client": "telnet"
}
];
$scope.getAllSelectedOptions = function(){
$scope.result = $scope.serverValue
};
});
app.directive('searchSelectEmpty', function () {
var html = `
<div class="outer-div" >
<!-- 以下是select -->
<div ng-click="clickSelect()" class="select-div">
<input type="text" class="form-input"
ng-blur="getInputBlur()"
ng-change="changeInputValue()"
ng-model="clientValue" />
<img src="{{checkImg.triangle}}"
width="12px" height="6px"
style="float:right;position: relative;top:16px;left:-6px" />
</div>
<!-- 以下是option -->
<div style="position: relative;" ng-show="isShowOptions && filterOptions.length">
<div class="option-outer" >
<div style="max-height: 360px;overflow: auto;padding-left: 6px;">
<div ng-repeat="option in filterOptions track by $index" class="option-inner" ng-click="clickOptions(option)">
<span ng-bind="option.client" style="height:34px;line-height:34px;display: inline-block;"></span>
</div>
</div>
</div>
</div>
</div>
`;
return {
restrict: 'E',
template: html,
scope: {
protocolType: '=protocolType',//所有选项
serverValue: '=serverValue',
},
replace: true,
controller: function ($scope,$timeout,checkImg) {
$scope.checkImg = checkImg;
$scope.isShowOptions = false;
$scope.filterOptions = angular.copy($scope.protocolType);
$scope.copyOptions = angular.copy($scope.protocolType);
$scope.clickSelect = function () {
$scope.isShowOptions = true;
};
$scope.getInputBlur = function () {
$timeout(function(){
$scope.isShowOptions = false;
},150)
};
$scope.clickOptions = function (option) {
$scope.serverValue = option.server;
$scope.clientValue = option.client;
};
$scope.changeInputValue = function () {
if($scope.clientValue){
$scope.filterOptions = [];
angular.forEach($scope.copyOptions,function(option){
if(option.client.indexOf($scope.clientValue) != -1){
$scope.filterOptions.push(option);
}
});
}else{
$scope.filterOptions = angular.copy($scope.copyOptions);
}
};
},
link: function (scope, ele, attrs) {
}
};
});
app.factory('checkImg', function () {
return {
triangle:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAYCAYAAAC8/X7cAAAAAXNSR0IArs4c6QAAAM9JREFUWAnV2MsNwyAMgGFKRmKBToPYImQLGKcDsFNKlUp9BIKNnWBLPvnyf1ffrLWzUso/V9xorZcppfQwxuT4uyRBjg8h+ClHS0O843P7CyAJ8Rn/BZCA+I3/A4yM2IvfBYyIKMUXASMhavFVwAiIo/hDwJWIlvgmwBWI1vhmwJkISDwIcAYCGg8GcCIw8SgABwIbjwZQInriuwAUiN74bkAPgiKeBIBBUMWTASAIynhSQAuCOp4cUENwxLMACggfY1zyTdQ45/z2tmHrXgHsbZxC2VHomgAAAABJRU5ErkJggg==',
};
});
</script>
六、input-select树结构(嵌套结构,不可以拖拽,左侧没有竖线)
1、主要需求描述
(1)页面初始时,前端自己显示一个空下拉框
(2)点击下拉框,前端向后台发送请求,将返回的数据,既渲染成下拉项,又存为备份
(3)点击其中一个下拉项,该下拉项如果没有下一级或下一级只有输入框,那么直接渲染,如果有下一级且下一级有下拉框,那么带上该下拉项数据,向后台发送请求,将返回的数据分别渲染成多个下拉框和输入框,作为下拉框的子级并缩进
(4)下拉框有的可以多选,有的只能单选,有的0选中时可以输入,叉掉选中项时,对应的子级也一起删掉
(5)以后重复(2)(3)(4),只是要携带自身至树根所有层级的label和value...
(6)点击“保存”,将整个树图和层级关系发给后台,后台生成一条数据,前端刷新页面,该条数据出现在表格中
(7)点击该条数据后面的“详情”,前端将该条数据展示成“保存”前的树图,可以继续编辑
(8)另外,点击域内下拉框,下拉框隐现交替,点击域外,域内下拉框隐藏
2、主要需求实现思路
(1)弃用select标签,使用div标签模拟select标签的效果,给div标签添加tabindex属性,使其能够获取和失去焦点
(2)数据和组件都用嵌套结构,见下面“数据示例”和“组件html代码”,以便实现各种展示效果,
(3)点击“保存”时,用递归遍历整个嵌套树,提取出后台所需数据
(4)点击“详情”时,用递归遍历本条详情,逐级逐条生成数据,每条数据的生成,都包含:A、本条数据加工getThisChildren(children,detail1),B、本条选项获取并加工getThisOptions(thisChild,detail2),C、下级数据获取getNextChildren(thisChild,detail3);getNextChildren最后将thisChild.children和detail3传给getThisChildren,开启下一级数据的生成;这样纵使后发出的请求先返回数据,也不会出现混乱。
附、数据示例
var childrenTemp = {//下拉框示例
type: type,//决定渲染成下拉框还是输入框
parentOption: formData.selectFront,//本条父级的前端文字,
selectLabel: key,//标签内容
selectFront: '',//本条的前端文字,传给本条的子级
selectBack: '',//本条的后台数据,传给本条的子级
selectModel: '',//0选中时输入的数据
type_detail: type_detail,//0选中时,是否禁止输入
protocol_type: parentProtocolType,//是否把本条后代的根定在此处
allOptions: [],//本条所有的下拉项,每项包含前后端数据
yesOptions: [],//本条所有的选中项,每项只包含前端数据
selectChainThis: parentProtocolType? [thisParams] : selectChainThis,//自身父级至树根所有层级的{label:value}对象组成的数组
isFinal: isFinal,//本条是否有子级
isArray: isArray,//本条是否可以多选
isShowOptions: false,//是否显示本条的下拉项
children: [],//本条的所有子级
};
var childrenTemp = {//输入框示例
type: type,//决定渲染成下拉框还是输入框
parentOption: formData.selectFront,//本数据与父级select的哪个选中项关联,
inputLabel: key,//标签内容
inputValue: '',//输入的数据
type_detail: type_detail,//是否禁止输入
};
3、实例1,不可预览,供实用参考
(1)组件html代码
<div style="margin-left: 20px"><!-- 子级缩进 -->
<div ng-show="requestParams.which==='addOrEdit'">
<div ng-repeat="formData in formDatas track by $index">
<div ng-if="formData.type==='input'" style="margin: 10px 0">
</div>
<div ng-if="formData.type==='select'">
<input-select
ng-if="formData.children.length>0"
form-datas="formData.children"
request-params="requestParams"
></input-select>
</div>
</div>
</div>
<div ng-show="requestParams.which==='detail'">
<div ng-repeat="formData in formDatas track by $index">
<div ng-if="formData.type==='input'" style="margin: 10px 0">
</div>
<div ng-if="formData.type==='select'">
<input-select
ng-if="formData.children.length>0"
></input-select>
</div>
</div>
</div>
</div>
(2)页面html代码
<input-select
ng-if="formDatas.length>0"
form-datas="formDatas"
request-params="requestParamsAddOrEdit"
>
</input-select>
(3)组件js代码
(function () {
angular.module('common-dir').directive('inputSelect', function () {
return {
restrict: 'E',
templateUrl: 'vendor/common-directive/input-select.html',
scope: {
formDatas: '=formDatas',
requestParams: '=requestParams'
},
replace: true,
controller: function ($scope, auditApi, $timeout) {
$scope.getBlur = function (formData, event) {
$timeout(function () {
formData.isShowOptions = false;
}, 200);
};
//这个函数用于:向后台发送请求,获取select的option
$scope.getOptions = function (formData, event) {
//点击select框,获取下拉项
if (event.target.tagName === 'INPUT') return; //当点击select框内部的input框时,阻止
if (event.target.className === 'onlyQC') return; //当点击select框内部的备选项时,阻止
if (formData.allOptions && formData.allOptions.length > 0) {
if (event.target.parentElement.className === 'isShowOptions') {
formData.isShowOptions = !formData.isShowOptions;
}
return; //当下拉项已存在时,阻止
}
................................
};
//这个函数用于:叉掉已选中项时,除了删掉自身,还删掉下面对应的所有子项
$scope.optionDelete = function (formData, front) {
formData.yesOptions.splice(formData.yesOptions.indexOf(front), 1);
for (var i = 0; i < formData.children.length; i++) {
if (formData.children[i].parentOption == front) {
formData.children.splice(
formData.children.indexOf(formData.children[i]),
1
);
i--;
}
}
};
//这个函数用于:点击下拉项中的某项时
$scope.optionClick = function (formData, option) {
var isTrue =
formData.type_detail === 'protogo' ||
formData.type_detail === 'object' ||
formData.isArray;
if (!isTrue) {
formData.children = [];
}
................................
};
},
link: function (scope, ele, attrs) {}
};
}).factory('inputSelect', function (auditApi) {
return {
/*方法说明
*@method getThisChildren 生成本条的全部子级
*@param{Array}selectChainThis 参数链,向后台发送请求前,应当拼接成字符串
*@param{String}label label标签文字
*@param{String}parent 本条来自于父级的被选中项parent
*@param{Object}valueOut 所有子级的原始数据
*@param{Array}children 所有子级的最终数据
*@param{Object,Array,String}thisDetail 本条数据的详情,随着递归的深入,越来越短
*@param{Boolean}isLeap 是否需要跳转,有时随着递归的深入,下一级回到了第一级(协议名称),这种情况叫跳转
*@return {undefined} undefined,没有返回值
*/
getThisChildren:function (
selectChainThis,
label,
parent,
valueOut,
children,
thisDetail,
isLeap,
request
) {
if (type === 'input') {
thisChild = {};
} else if (type === 'select') {
thisChild = {};
if (isFinal === 1) {
//没有子级
} else if (isFinal === 0) {
//有子级
if (thisDetail.constructor === Object) {
var detailKeysArray = Object.keys(thisDetail);
for (var i = 0; i < detailKeysArray.length; i++) {
var detailKey = detailKeysArray[i];
if (detailKey == label) {
var nextDetail = thisDetail[detailKey];
that.getThisOptions(...);
}
}
} else {
//点击下拉框时,组件向后台发送请求,然后才出现下拉项,不用在这里获取下拉项。
}
}
}
children.push(thisChild);
},
/*方法说明
*@method getThisOptions 生成本子级的所有下拉项
*@param{Object}thisChild 一个子级
*@param{Object,Array,String}nextDetail 本条数据的详情,随着递归的深入,越来越短
*@param{Array}selectChainThis 参数链,向后台发送请求前,应当拼接成字符串
*@param{String}label label标签文字
*@param{Boolean}isLeap 是否需要跳转,有时随着递归的深入,下一级回到了第一级(协议名称),这种情况叫跳转
*@return {undefined} undefined,没有返回值
*/
getThisOptions:function (
thisChild,
nextDetail,
selectChainThis,
label,
isLeap,
request
) {
$http({
method: request.method,
root: request.root,
url: request.url,
params: params
})
.then(function (result) {
allOptions.push(oneOption);
for (var j = 0; j < allOptions.length; j++) {
var optionOne = allOptions[j];
that.getNextChildren(...);
}
});
},
/*方法说明
*@method getNextChildren 获取子级的子级
*@param{Object}optionOne 子级的一个下拉项
*@param{Object,Array,String}nextDetail 本条数据的详情,随着递归的深入,越来越短
*@param{Array}selectChainThis 参数链,向后台发送请求前,应当拼接成字符串
*@param{Object}thisChild 一个子级
*@param{Object}params 请求参数
*@param{Boolean}isLeap 是否需要跳转,有时随着递归的深入,下一级回到了第一级(协议名称),这种情况叫跳转
*@return {undefined} undefined,没有返回值
*/
getNextChildren:function (
optionOne,
nextDetail,
selectChainThis,
thisChild,
params,
isLeap,
request
) {
var that = this;
var isFinal = optionOne.isFinal;
var parent = optionOne.front;
if (that.wholeKeysArray.indexOf(parent) > -1) {//以下是跳转逻辑
$http({
method: request.method,
root: request.root,
url: request.url,
params: {
protocol_type: parent
}
})
.then(function (result) {
var selectChainThis = [{ 协议名称: parent }]; //遇到跳转逻辑时,给选择链重新赋值
for (var label in valueOut) {
that.getThisChildren(...);
}
});
}
} else {//以下是正常逻辑
if (isFinal === 1) {
var resultKey = Object.keys(thisChild.copyOptions)[0];
var valueOut = thisChild.copyOptions[resultKey];
var isObject = nextDetail.constructor === Object;
var detailKeysArray;
if (isObject) {
detailKeysArray = Object.keys(nextDetail);
if (detailKeysArray.indexOf(parent) > -1) {
for (var label in valueOut) {
if (label === parent) {
that.getThisChildren(...);
thisChild.yesOptions.push(parent);
}
}
}
}
} else if (isFinal === 0) {
var isObject = nextDetail.constructor === Object;
if (isObject) {
var detailKeysArray = Object.keys(nextDetail);
if (detailKeysArray.indexOf(parent) > -1) {
$http({
method: request.method,
root: request.root,
url: request.url,
params: thisParams
})
.then(function (result) {
if (detailKeysArray.indexOf(resultKeysArray[0]) > -1) {//父级的parentOption和子级的label一样
for (var label in valueOut) {
that.getThisChildren(...);
}
} else {//如果出现parentOption和它子级的label不一样,那么根据数据结构,逻辑写在此处。
for (var label in valueOut) {
that.getThisChildren(...);
}
}
});
}
}
}
}
},
};
});
})();
(4)页面js代码
$scope.formDatas = [
{
type: 'select',
selectLabel: '协议名称',
selectFront: '',
selectBack: '',
allOptions: [],
yesOptions: [],
selectChainThis: [],
children: [],
isFinal: 0
}
];
4、实例2,可预览,供演示
<!DOCTYPE html>
<html lang="en" ng-app="myModel">
<head>
<meta charset="UTF-8">
<title>input和select</title>
<script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
</head>
<body>
<div ng-controller="thisCtrl">
<div>
<p>=====可预览=====</p>
<p>1、向后台发送请求,获取返回数据,填充$scope.formDatas</p>
<p>2、向后台发送请求,获取返回数据,填充$scope.formDatas</p>
</div>
<input-select ng-if="formDatas.length>0" form-datas="formDatas">
</input-select>
</div>
</body>
</html>
<script>
var app = angular.module('myModel', []);
app.controller('thisCtrl', function ($scope/*,cyRequest*/) {
//此处,向后台发送请求,获取返回数据,填充$scope.formDatas
$scope.formDatas = [
{
type: 'input',
inputLabel: '输入框',
inputValue: '',
},{
type: 'select',
selectLabel: '选择框标签',
selectValue: '后台1',
selectOptions: [
{ back: '后台0', front: '前端0' },
{ back: '后台1', front: '前端1' },
{ back: '后台2', front: '前端2' },
{ back: '后台3', front: '前端3' },
{ back: '后台4', front: '前端4' },
{ back: '后台5', front: '前端5' },
],
selectChainThis: [],//默认值。
selectChainNext: null,
children: [],//子级。
},{
type: 'select',
selectLabel: '选择框标签2222',
selectValue: '后台1',
selectOptions: [
{ back: '后台0', front: '前端0' },
{ back: '后台1', front: '前端1' },
{ back: '后台2', front: '前端2' },
{ back: '后台3', front: '前端3' },
{ back: '后台4', front: '前端4' },
{ back: '后台5', front: '前端5' },
],
selectChainThis: [],//默认值。
selectChainNext: null,
children: [],//子级。
}
];
});
app.directive('inputSelect', function () {
var html = `
<div style="margin-left:10px;">
<div ng-repeat="formData in formDatas track by $index">
<div ng-if="formData.type==='input'" style="margin:10px 0">
<label ng-bind="formData.inputLabel+':'" style="width:200px;text-align:right;display:inline-block"></label>
<input ng-model="formData.inputValue" type="text" style="width:300px;border:1px solid gray;height:26px;border-radius:4px;"/>
</div>
<div ng-if="formData.type==='select'" style="margin:10px 0">
<label ng-bind="formData.selectLabel+':'" style="width:200px;text-align:right;display:inline-block"></label>
<select
ng-model="formData.selectValue"
ng-options="option.back as option.front for option in formData.selectOptions"
ng-change="clickOption(formData)"
style="width:306px;border:1px solid gray;height:26px;border-radius:4px;"
></select>
<input-select ng-if="formData.children.length>0" form-datas="formData.children"></input-select>
</div>
</div>
</div>
`;
return {
restrict: 'E',
template: html,
scope: {
formDatas: '=formDatas'
},
replace: true,
controller: function ($scope) {//此处注入“公共请求服务”
$scope.clickOption = function (formData) {
var objOuter={};
objOuter[formData.selectLabel] = formData.selectValue;
formData.selectChainNext = angular.copy(formData.selectChainThis);
formData.selectChainNext.push(objOuter);
//此处,把formData.selectChainNext作为“公共请求服务”的参数,向后台发送请求,获取返回数据result并加工,然后填充formData.children
var result=[
{
type:'input',
label:'输入框'
},{
type:'select',
label:'选择框标签'
},{
type:'select',
label:'选择框标签2'
}
];
angular.forEach(result,function(value,index){
var objInner = { };
if(value.type==='input'){
objInner.type = value.type;
objInner.inputLabel = value.label;
objInner.inputValue = '';
}else if(value.type==='select'){
objInner.type = value.type;
objInner.selectLabel = value.label;
objInner.selectValue = '后台1';
objInner.selectOptions = [
{ back: '后台0', front: '前端0' },
{ back: '后台1', front: '前端1' },
{ back: '后台2', front: '前端2' },
{ back: '后台3', front: '前端3' },
{ back: '后台4', front: '前端4' },
{ back: '后台5', front: '前端5' },
];
objInner.selectChainThis = angular.copy(formData.selectChainNext);
objInner.selectChainNext = null;
objInner.children = [];
if(index===1){
console.log(objInner.selectChainThis)
}
}
formData.children.push(objInner)
})
};
},
link: function (scope, ele, attrs) {
}
};
})
</script>
七、dir-tree树结构(平行结构,可以拖拽,左侧有竖线)
1、主要需求描述
(1)页面初始时,初始化组件,向后台发送请求,获取嵌套数据,改成平级数据,计算并添加缩进和左侧线,渲染组件
(2)可以编辑本条数据
(3)可以拖拽本条数据至目标数据上,将自身和目标数据发给后台,最后刷新页面,获取新嵌套数据(被拖拽数据及其后代成为目标数据的后代)
2、主要需求实现思路
(1)拖拽效果用插件
(2)递归实现嵌套数据改成平级数据,非如此则拖拽出错
(3)递归实现左侧竖线
附、数据示例
var obj={
id: item.id,//本级id
name: item.name,
puuid: item.puuid,
uuid: item.uuid,
idsArray: item.idsArray,//供判断层级关系时用
isShowSelf: item.collapsed||true,//是否显示自身
//以上为后台返回,以下为前端添加
isShowChild: true,//是否显示后代
leftTime: leftTime,//竖线从左缩进次数
downTime: 0,//竖线向下延伸次数
}
(5)给div标签添加tabindex属性,使其能够获取和失去焦点
3、实例1,不可预览,供实用参考
(1)组件html代码
<div>
<div
style="display:flex; flex-direction: row"
ng-repeat="formData in formDatas track by $index"
>
<div
class="dir-tree-line"
ng-drag="true"
ng-drop="true"
ng-show="formData.isShowSelf"
ng-style="{'margin-left':formData.leftTime*28+'px'}"
ng-drop-success="dropSuccess($index, $data, $event)"
ng-drag-data="formData"
ng-click="clickItem(formData)"
style="position:relative"
>
<div>
<span ng-style="{'background': $index===0? '#ffffff':'rgb(162, 142, 142)'}" style="height:1px;width:16px;display: inline-block;position: relative;top:-4px"></span>
<span
class="dir-tree-label"
ng-class="formData.isHasChild?((selectedItem.showId && selectedItem.showId === formData.uuid)?'dir-tree-haschild-blue':'dir-tree-haschild-black'):((selectedItem.showId && selectedItem.showId === formData.uuid)?'dir-tree-nochild-blue':'dir-tree-nochild-black')"
></span>
<span
ng-bind="formData.name"
ng-class="(selectedItem.showId && selectedItem.showId === formData.uuid)?'dir-tree-label-text-blue':'dir-tree-label-text-black'"
ng-drag="true"
ng-drag-data="formData"
></span>
<span
class="dir-tree-label"
ng-show="formData.isHasChild && !formData.isShowChild"
ng-class="(selectedItem.showId && selectedItem.showId === formData.uuid)?'dir-tree-haschild-tianjia-blue':'dir-tree-haschild-tianjia-black'"
></span>
</div>
<div
style="position:relative;width:1px;background: rgb(162, 142, 142);left:27px;top:-7px;"
ng-style="{'height':((formData.downTime+0.5)*36+8)+'px'}"
ng-show="formData.isShowChild && formData.isHasChild"
></div>
</div>
<div style="flex:1;"></div>
</div>
</div>
(2)页面html代码
<dir-tree request-datas='requestDatas' selected-item='selectedAdd' ng-if="decideOther.isIf"></dir-tree>
(3)组件js代码
(function () {
angular.module('common-dir').directive('dirTree', function () {
return {
restrict: 'E',
templateUrl: 'vendor/common-directive/dir-tree.html',
scope: {
requestDatas: '=requestDatas',//与前后台交互相关的内容
selectedItem: '=selectedItem',//树图的被选中项
decideOther: '=decideOther'//本树是否决定“其它树”的隐现
},
replace: true,
controller: function ($scope, $timeout, auditApi) {
if(!$scope.selectedItem.isAbandonDrag){
$scope.selectedItem.callback = function(name,uuid,puuid){
auditApi
.query({
method: $scope.requestDatas.method||'put',
root: $scope.requestDatas.root||'organization',
url: $scope.requestDatas.url||'/info',
data: {
name:name,//被修改节点的名称
uuid:uuid,//被修改节点的uuid
puuid:puuid,//被修改的节点,应移到哪个节点之下
},
pageLoad: true,
})
.then(function () {
layer.msg('拖拽成功!');
$scope.getDatas();
}).catch(function(){
$scope.getDatas();
});
}
}else{
delete $scope.selectedItem.callback
}
$scope.clickItem = function(formData){
$scope.selectedItem.name = formData.name;
$scope.selectedItem.puuid = formData.puuid;
$scope.selectedItem.uuid = formData.uuid;
$scope.selectedItem.showId = formData.uuid;
var id = formData.id;
formData.isShowChild = !formData.isShowChild
$scope.formDatas.forEach(function(item){
if(item.idsArray.indexOf(id) > -1 && item.idsArray.length > formData.idsArray.length){
item.isShowChild = formData.isShowChild;
item.isShowSelf = formData.isShowChild;
}
})
$scope.addDownTime($scope.formDatas);
}
$scope.dropSuccess = function(index,data,event){
var toItem = $scope.formDatas[index];
if(angular.isFunction($scope.selectedItem.callback)){
$scope.selectedItem.callback(data.name,data.uuid,toItem.uuid);
}
}
$scope.getDatas = $scope.requestDatas.getDatas = function(isNewAdd){
auditApi
.query({
method: $scope.requestDatas.method||'get',
root: $scope.requestDatas.root||'organization',
url: $scope.requestDatas.url||'/info',
pageLoad: true,
})
.then(function (res) {
var list = angular.copy(res.rows);
var lastList = [];
//以下给每条数据添加idsArray属性,供判断层级关系时用
function addEachRowIdsArray(list,fatherId){
list.forEach(function(item) {
item.idsArray = fatherId.concat(item.id);
if(item.children){
addEachRowIdsArray(item.children,item.idsArray)
}
});
}
addEachRowIdsArray(list,[])
//以下把父子嵌套数据改造成兄弟并列数据,不如此则拖拽出错
function getList(list,leftTime){
angular.forEach(list,function(item){
var obj={
id: item.id,
name: item.name,
puuid: item.puuid,
uuid: item.uuid,
idsArray: item.idsArray,
isShowSelf: item.collapsed||true,//是否显示自身
//以上为后台返回,以下为前端添加
isShowChild: true,//是否显示后代
leftTime: leftTime,//竖线从左缩进次数
downTime: 0,//竖线向下延伸次数
}
lastList.push(obj)
if(item.children.length){
obj.isHasChild = true;
getList(item.children,leftTime+1)
}
})
}
getList(list,1);
if(isNewAdd){
$scope.selectedItem.showId = lastList[0].uuid;
$scope.selectedItem.uuid = lastList[0].uuid;
}
$scope.selectedItem.isInit = Boolean(lastList.length);
if($scope.decideOther){//不如此,修改后“其它树”不刷新
$scope.decideOther.isIf = false;
$timeout(function(){
$scope.decideOther.isIf = true;
$scope.formDatas = lastList;
$scope.addDownTime($scope.formDatas)
})
}
$scope.formDatas = lastList;//已经完成加工的树图总数据!
$scope.addDownTime($scope.formDatas)//给总数据的每条数据添加向下延伸次数
});
}
$scope.addDownTime = function(formDatas){
//以下给formDatas的每一项添加downTime
formDatas.forEach(function(item1){
item1.downTime = 0 ;
if(!item1.isShowChild)return;
//以下添加item1的downTime
var id1 = item1.id;
var idsArray = [];
//以下获取本级的所有子代的id,并存储在idsArray中
formDatas.forEach(function(item2){
if(item2.idsArray.indexOf(id1) > -1 && item2.idsArray.length == item1.idsArray.length+1){
idsArray.push(item2.id)
}
})
//以下删除idsArray的最后一项
idsArray.pop();
//以下计算本项的downTime
for(var i = 0; i<idsArray.length; i++){
var id2 = idsArray[i];//第i个子代的id
for(var ii = 0; ii<formDatas.length; ii++){
var item3 = formDatas[ii];
if(item3.idsArray.indexOf(id2)>-1){//从所有数据中,找到第i个子代
item1.downTime++;
if(item3.isHasChild && !item3.isShowChild){//第i个子代有子代且没有展开
var id3 = item3.id;
formDatas.forEach(function(item4){
if(item4.idsArray.indexOf(id3) > -1 && item4.idsArray.length > item3.idsArray.length){
ii++//跳过没展开的子代
}
})
}
}
}
}
})
}
$scope.getDatas()
},
link: function (scope, ele, attrs) {}
};
});
})();
(4)页面js代码
//以下是树图属性传参的数据
$scope.decideOther = {//本树是否决定“其它树”的隐现
isIf: true
};
$scope.requestDatas = { };//与前后台交互相关的内容
$scope.selectedDatas = {//树图的被选中项
name: '',
uuid: '',
puuid: '',
showId: '',
};
4、实例2,可预览,供演示用
<!DOCTYPE html>
<html lang="en" ng-app="myModel">
<head>
<meta charset="UTF-8">
<title>passWord</title>
<style>
.dir-tree-label{
padding-left: 20px;
margin-right: 4px;
height: 36px;
line-height: 36px;
background-position: left 4px;
background-repeat: no-repeat;
cursor:pointer;
user-select:none;
}
.dir-tree-label-text{
cursor: pointer;
user-select: none;
}
.dir-tree-label-black{
color:black
}
.dir-tree-label-red{
color:red
}
</style>
<script type="text/javascript" src="https://cdn.bootcss.com/angular.js/1.6.2/angular.js"></script>
<script src="https://cdn.bootcss.com/ngDraggable/0.1.11/ngDraggable.js"></script>
</head>
<body>
<div ng-controller="thisCtrl">
<dir-tree form-datas="lastList" other-datas='otherDatas'></dir-tree>
</div>
</body>
</html>
<script>
var app = angular.module('myModel', ['ngDraggable']);
app.controller('thisCtrl', function ($scope) {
var res = {
"rows": [{
"children": [{
"children": [{
"children": [{
"children": [],
"isShowSelf": true,
"id": "12",
"name": "裁员部一",
"puuid": "1ec9f238-9aba-484c-b857-d4183200053c",
"uuid": "1e8ae51d-8d2f-486e-b1d1-ff1009f8461e"
},
{
"children": [],
"isShowSelf": true,
"id": "13",
"name": "裁员部二",
"puuid": "1ec9f238-9aba-484c-b857-d4183200053c",
"uuid": "be521aa9-1e8c-4262-a156-2d02e3cb9c51"
}
],
"isShowSelf": true,
"id": "4",
"name": "人力部一",
"puuid": "bcb3e97f-d8ef-4e33-a234-09a3fef8533d",
"uuid": "1ec9f238-9aba-484c-b857-d4183200053c"
},
{
"children": [],
"isShowSelf": true,
"id": "5",
"name": "人力部二",
"puuid": "bcb3e97f-d8ef-4e33-a234-09a3fef8533d",
"uuid": "ba10a598-9af7-47ba-9c34-ee41d85f0525"
},
{
"children": [],
"isShowSelf": true,
"id": "6",
"name": "人力部三",
"puuid": "bcb3e97f-d8ef-4e33-a234-09a3fef8533d",
"uuid": "a880024f-e906-4558-833c-4ec7a20927e3"
},
{
"children": [],
"isShowSelf": true,
"id": "7",
"name": "人力部四",
"puuid": "bcb3e97f-d8ef-4e33-a234-09a3fef8533d",
"uuid": "25de3bf5-4848-48e5-a161-04d1492a4edb"
}
],
"isShowSelf": true,
"id": "2",
"name": "人力部",
"puuid": "f2a8c4ea-e270-44d0-a7bd-18c62fb55358",
"uuid": "bcb3e97f-d8ef-4e33-a234-09a3fef8533d"
},
{
"children": [{
"children": [],
"isShowSelf": true,
"id": "8",
"name": "财务部一",
"puuid": "44667cbb-a1d5-4187-84aa-c3ee347d873e",
"uuid": "c036eab1-0f4d-4732-be3c-8e2da46cb60e"
},
{
"children": [],
"isShowSelf": true,
"id": "9",
"name": "财务部二",
"puuid": "44667cbb-a1d5-4187-84aa-c3ee347d873e",
"uuid": "ee22c501-e797-4190-84d7-41c0523ac784"
},
{
"children": [],
"isShowSelf": true,
"id": "10",
"name": "财务部三",
"puuid": "44667cbb-a1d5-4187-84aa-c3ee347d873e",
"uuid": "60dd79cf-20b7-4d04-92c0-968eb85620c1"
},
{
"children": [],
"isShowSelf": true,
"id": "11",
"name": "财务部四",
"puuid": "44667cbb-a1d5-4187-84aa-c3ee347d873e",
"uuid": "f7a76408-c3ba-4c4b-8f77-d520407beb31"
}
],
"isShowSelf": true,
"id": "3",
"name": "财务部",
"puuid": "f2a8c4ea-e270-44d0-a7bd-18c62fb55358",
"uuid": "44667cbb-a1d5-4187-84aa-c3ee347d873e"
}
],
"isShowSelf": true,
"id": "1",
"name": "中国石油",
"puuid": "",
"uuid": "f2a8c4ea-e270-44d0-a7bd-18c62fb55358"
}]
};
var list = angular.copy(res.rows);
var lastList = [];
//以下解决隐藏和显示问题
function addEachRowIds(list,fatherId){
list.forEach(function(item) {
item.idsArray = fatherId.concat(item.id);
if(item.children){
addEachRowIds(item.children,item.idsArray)
}
});
}
addEachRowIds(list,[])
//以下把父子嵌套数据改造成兄弟并列数据,不如此则拖拽出错,极端重要!!!
function getList(list,left){
angular.forEach(list,function(item){
var obj={
id: item.id,
name: item.name,
puuid: item.puuid,
uuid: item.uuid,
idsArray: item.idsArray,
isShowSelf: item.isShowSelf,
left: left,
}
lastList.push(obj)
if(item.children.length){
obj.isHasChild = true;
getList(item.children,left+1)
}
})
}
getList(list,1);
$scope.lastList = lastList;
$scope.otherDatas = {//页面树图的被选中项
name: '',//被选中项的name
uuid: '',//被选中项的uuid
puuid: '',//被选中项的puuid,即该数据父级的uuid
showId: '',//被选中项的showId,取值为“该条数据”的uuid,在<dir-tree>组件中,showId与每条数据的uuid进行比较,相等的展示为红色,为被选中项本身
callback: function(name,uuid,puuid){//拖动结束后,触发的函数
}
};
});
app.directive('dirTree', function () {
var html = `
<div>
<div
class="dir-tree-label"
ng-drag="true"
ng-drop="true"
ng-show="formData.isShowSelf"
ng-style="{'margin-left':formData.left*20+'px'}"
ng-repeat="formData in formDatas track by $index"
ng-drop-success="dropSuccess($index, $data, $event)"
ng-drag-data="formData"
>
<img ng-src="{{formData.isHasChild? fileImg.havechild : fileImg.nochild}}" ng-click="clickImg(formData)">
<span
class="dir-tree-label-text"
ng-bind="formData.name"
ng-class="(otherDatas.showId && otherDatas.showId === formData.uuid)?'dir-tree-label-red':'dir-tree-label-black'"
ng-click="clickText(formData)"
ng-drag="true"
ng-drag-data="formData"
></span>
</div>
</div>
`;
return {
restrict: 'E',
template: html,
scope: {
formDatas: '=formDatas',
otherDatas: '=otherDatas'//页面树图的被选中项
},
replace: true,
controller: function ($scope,fileImg) {
$scope.fileImg = fileImg;
$scope.clickImg = function(formData){
$scope.clickText(formData);
var thisArrayIds = formData.idsArray;
$scope.formDatas.forEach(function(item){
var flag = true;
var itemArrayIds = item.idsArray;
thisArrayIds.forEach(function(id,index){
if(itemArrayIds.indexOf(id) === -1){
flag = false;
}
})
if(flag){
item.isShowSelf = !item.isShowSelf
}
})
formData.isShowSelf = true
}
$scope.clickText = function(formData){
$scope.otherDatas.name = formData.name;
$scope.otherDatas.puuid = formData.puuid;
$scope.otherDatas.uuid = formData.uuid;
$scope.otherDatas.showId = formData.uuid;
}
$scope.dropSuccess = function(index,data,event){
/**
* 函数说明:拖拽结束时触发的函数
* @index:目标所在位置的序号
* @data:初始位置的数据
* @event:初始位置的鼠标事件
*
*/
var toItem = $scope.formDatas[index];
var fromIndex = $scope.formDatas.findIndex(function(item){
return Object.is(data.uuid ,item.uuid)
})
$scope.formDatas[index] = data;
$scope.formDatas[fromIndex] = toItem;
$scope.otherDatas.callback(data.name,data.uuid,toItem.uuid);
}
},
link: function (scope, ele, attrs) {}
};
});
app.factory('fileImg', function () {
return {
nochild: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAOCAYAAADwikbvAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAOw4AAA7DAZBCL/sAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAHZJREFUOE9j+P//Pxiv7HH8jw/D1CFjOAOqACu4srcGqwFEa8ZmAEma0Q3A1JxbgIrRAH7NBABezT3bF8JxfU08WCwrJwlMgwD1bYbZguxXkBjIVmSboepwaCYA8GoGRQeyvy07S8AYBmhnMyGAUzOxGKLnPwMABE2jDc+cnOsAAAAASUVORK5CYII=',
havechild: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAYAAAAmL5yKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAIhJREFUOE9j+P//P0UYqyApGIVzuN3kPzJGlsOF4Yy1xbpAChVAxVA0oGMwAbbt0TwEvtT8/8GuCjBGdxVYLVYDiAS4DUB2AR48PUoe0wCwX7EoxobRw4U6BszPUAEHHDEYrBbdALC/sCjGhrGGAUgQ7DQsGtAxVgNAGOQ0YjBMPQyjcEjH/xkAhEKsbVNNI1sAAAAASUVORK5CYII=',
};
});
</script>