hbte-saas-ui 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.browserslistrc +3 -0
- package/.editorconfig +7 -0
- package/.env.development +1 -0
- package/.env.lib +1 -0
- package/.eslintrc.js +14 -0
- package/.prettierrc +3 -0
- package/README.md +53 -0
- package/babel.config.js +3 -0
- package/docs/.vuepress/config.js +23 -0
- package/docs/.vuepress/styles/index.styl +4 -0
- package/docs/AdvQuery.md +26 -0
- package/docs/CustomQuery.md +21 -0
- package/docs/EditArea.md +16 -0
- package/docs/FilterDrop.md +29 -0
- package/docs/FullTable.md +68 -0
- package/docs/Pagination.md +22 -0
- package/docs/QueryForm.md +27 -0
- package/docs/README.md +17 -0
- package/docs/Table.md +58 -0
- package/docs/ToolGroup.md +32 -0
- package/examples/App.vue +97 -0
- package/examples/image/hb-image-viewer/3k.jpg +0 -0
- package/examples/image/hb-image-viewer/a.jpg +0 -0
- package/examples/image/hb-image-viewer/b.jpg +0 -0
- package/examples/main.js +39 -0
- package/examples/mock/authority.js +1108 -0
- package/examples/mock/dept.js +4001 -0
- package/examples/mock/fulltable.js +407 -0
- package/examples/mock/organizeData.js +18965 -0
- package/examples/router/index.js +111 -0
- package/examples/views/ElementCpTest.vue +299 -0
- package/examples/views/Fragment.vue +196 -0
- package/examples/views/HBAdvQuery.vue +150 -0
- package/examples/views/HBAuthority.vue +339 -0
- package/examples/views/HBDialog.vue +39 -0
- package/examples/views/HBEditarea.vue +350 -0
- package/examples/views/HBFilterDrop.vue +72 -0
- package/examples/views/HBFloatFooter.vue +42 -0
- package/examples/views/HBFullTable.vue +751 -0
- package/examples/views/HBImageViewer.vue +57 -0
- package/examples/views/HBNavMenu.vue +37 -0
- package/examples/views/HBOrganizeDialog.vue +133 -0
- package/examples/views/HBPagination.vue +80 -0
- package/examples/views/HBSelectVague.vue +57 -0
- package/examples/views/HBToolgroup.vue +29 -0
- package/examples/views/HBTopBar.vue +16 -0
- package/lib/demo.html +10 -0
- package/lib/fonts/iconfont.9e99af35.ttf +0 -0
- package/lib/hbte-ui.common.js +38291 -0
- package/lib/hbte-ui.css +1 -0
- package/lib/hbte-ui.umd.js +38301 -0
- package/lib/hbte-ui.umd.min.js +21 -0
- package/lib/img/nodata.f87d17c1.png +0 -0
- package/lib/img/user-logo--default.a53bd193.png +0 -0
- package/package.json +40 -0
- package/packages/adv-query/index.js +5 -0
- package/packages/adv-query/src/main.vue +294 -0
- package/packages/area-select/index.js +7 -0
- package/packages/area-select/src/main.vue +37 -0
- package/packages/authority/index.js +5 -0
- package/packages/authority/src/main.vue +606 -0
- package/packages/config.js +3 -0
- package/packages/custom-query/index.js +5 -0
- package/packages/custom-query/src/main.vue +80 -0
- package/packages/dialog/index.js +7 -0
- package/packages/dialog/src/main.vue +176 -0
- package/packages/edit-area/index.js +13 -0
- package/packages/edit-area/src/config.js +3 -0
- package/packages/edit-area/src/main.vue +304 -0
- package/packages/edit-area/src/simulateEvent.js +219 -0
- package/packages/filter-drop/index.js +6 -0
- package/packages/filter-drop/src/main.vue +107 -0
- package/packages/float-footer/index.js +7 -0
- package/packages/float-footer/src/main.vue +40 -0
- package/packages/fragment/index.js +7 -0
- package/packages/fragment/src/main.vue +74 -0
- package/packages/fragment/src/old.vue +144 -0
- package/packages/full-table/index.js +6 -0
- package/packages/full-table/src/config.js +14 -0
- package/packages/full-table/src/main.vue +141 -0
- package/packages/image-viewer/index.js +8 -0
- package/packages/image-viewer/src/main.js +14 -0
- package/packages/image-viewer/src/main.vue +138 -0
- package/packages/image-viewer/src/svg/cancel.svg +2 -0
- package/packages/image-viewer/src/svg/delete.svg +2 -0
- package/packages/image-viewer/src/svg/download.svg +2 -0
- package/packages/image-viewer/src/svg/rotate.svg +77 -0
- package/packages/image-viewer/src/svg/scaledown.svg +71 -0
- package/packages/image-viewer/src/svg/scaleup.svg +72 -0
- package/packages/index.js +43 -0
- package/packages/nav-menu/index.js +7 -0
- package/packages/nav-menu/src/main.vue +277 -0
- package/packages/organize-dialog/index.js +5 -0
- package/packages/organize-dialog/src/main.vue +427 -0
- package/packages/pagination/index.js +6 -0
- package/packages/pagination/src/main.vue +145 -0
- package/packages/query-form/index.js +5 -0
- package/packages/query-form/src/main.vue +142 -0
- package/packages/select-vague/index.js +5 -0
- package/packages/select-vague/src/main.vue +80 -0
- package/packages/table/index.js +5 -0
- package/packages/table/src/main.vue +265 -0
- package/packages/theme-chalk/adv-query.scss +107 -0
- package/packages/theme-chalk/authority.scss +103 -0
- package/packages/theme-chalk/common/elementCover.scss +722 -0
- package/packages/theme-chalk/common/flex.scss +66 -0
- package/packages/theme-chalk/common/global.scss +147 -0
- package/packages/theme-chalk/common/reset.scss +92 -0
- package/packages/theme-chalk/common/var.scss +46 -0
- package/packages/theme-chalk/custom-query.scss +10 -0
- package/packages/theme-chalk/dialog.scss +9 -0
- package/packages/theme-chalk/edit-area.scss +119 -0
- package/packages/theme-chalk/filter-drop.scss +81 -0
- package/packages/theme-chalk/float-footer.scss +14 -0
- package/packages/theme-chalk/fonts/iconfont.ttf +0 -0
- package/packages/theme-chalk/fragment.scss +51 -0
- package/packages/theme-chalk/full-table.scss +40 -0
- package/packages/theme-chalk/icon.scss +96 -0
- package/packages/theme-chalk/image-viewer.scss +94 -0
- package/packages/theme-chalk/images/company-logo--default.png +0 -0
- package/packages/theme-chalk/images/nodata.png +0 -0
- package/packages/theme-chalk/images/user-logo--default.png +0 -0
- package/packages/theme-chalk/index.scss +21 -0
- package/packages/theme-chalk/nav-menu.scss +205 -0
- package/packages/theme-chalk/organize-dialog.scss +170 -0
- package/packages/theme-chalk/pagination.scss +91 -0
- package/packages/theme-chalk/select.scss +8 -0
- package/packages/theme-chalk/table.scss +102 -0
- package/packages/theme-chalk/tool-group.scss +105 -0
- package/packages/theme-chalk/top-bar.scss +195 -0
- package/packages/tool-group/index.js +13 -0
- package/packages/tool-group/src/config.js +40 -0
- package/packages/tool-group/src/main.vue +93 -0
- package/packages/top-bar/index.js +7 -0
- package/packages/top-bar/src/main.vue +280 -0
- package/public/index.html +20 -0
- package/vue.config.js +6 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="hb-navmenu"
|
|
4
|
+
:class="[
|
|
5
|
+
isAbsolute ? (isCollapse ? 'hb-navmenu--open' : '') : '',
|
|
6
|
+
isTransition ? 'hb-navmenu--transition' : '',
|
|
7
|
+
]"
|
|
8
|
+
>
|
|
9
|
+
<nav class="hb-navmenu-main">
|
|
10
|
+
<slot name="logo"></slot>
|
|
11
|
+
<div
|
|
12
|
+
:class="
|
|
13
|
+
isActive(index)
|
|
14
|
+
? 'hb-navmenu-main__item hb-navmenu-main__item--isactive'
|
|
15
|
+
: 'hb-navmenu-main__item'
|
|
16
|
+
"
|
|
17
|
+
v-for="(item, index) in data"
|
|
18
|
+
:key="index"
|
|
19
|
+
@click="navigatToPath(item)"
|
|
20
|
+
>
|
|
21
|
+
<i :class="item[props.icon]"></i>
|
|
22
|
+
<p class="hb-navmenu-main__explain">
|
|
23
|
+
{{ item[props.label] }}
|
|
24
|
+
</p>
|
|
25
|
+
</div>
|
|
26
|
+
</nav>
|
|
27
|
+
<div
|
|
28
|
+
:class="[
|
|
29
|
+
isCollapse ? 'hb-navmenu-sub' : 'hb-navmenu-sub hb-navmenu-sub--closed',
|
|
30
|
+
isTransition ? 'hb-navmenu-sub--transition' : '',
|
|
31
|
+
]"
|
|
32
|
+
>
|
|
33
|
+
<i
|
|
34
|
+
class="hbui-shouqi hb-navmenu--collapse"
|
|
35
|
+
v-if="isCollapseShowIcon"
|
|
36
|
+
@click="changecollapse"
|
|
37
|
+
></i>
|
|
38
|
+
<i
|
|
39
|
+
class="hbui-zhankai hb-navmenu--collapse-closed"
|
|
40
|
+
v-else
|
|
41
|
+
@click="changecollapse"
|
|
42
|
+
></i>
|
|
43
|
+
<div class="hb-navmenu-sub-hide">
|
|
44
|
+
<div class="hb-navmenu-sub-content">
|
|
45
|
+
<p class="hb-navmenu-sub-content__title">
|
|
46
|
+
{{ navMenuTitle }}
|
|
47
|
+
</p>
|
|
48
|
+
<div
|
|
49
|
+
:class="
|
|
50
|
+
subIsActive(index)
|
|
51
|
+
? 'hb-navmenu-sub-content--active hb-navmenu-sub-item'
|
|
52
|
+
: 'hb-navmenu-sub-item'
|
|
53
|
+
"
|
|
54
|
+
v-for="(item, index) in subNnavMenuList"
|
|
55
|
+
:key="index"
|
|
56
|
+
@click="navigatToPath(item, false)"
|
|
57
|
+
>
|
|
58
|
+
<i :class="item[props.icon]"></i>
|
|
59
|
+
{{ item[props.label] }}
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</template>
|
|
66
|
+
|
|
67
|
+
<script>
|
|
68
|
+
//用props传数据!!!!
|
|
69
|
+
export default {
|
|
70
|
+
name: "HbNavmenu",
|
|
71
|
+
componentName: "HbNavmenu",
|
|
72
|
+
props: {
|
|
73
|
+
data: {
|
|
74
|
+
type: Array,
|
|
75
|
+
default: () => [],
|
|
76
|
+
},
|
|
77
|
+
/**
|
|
78
|
+
* 菜单id用于当前选中高亮,不传时采用path字段来判断,需要每个path都存在且不重复
|
|
79
|
+
*/
|
|
80
|
+
props: {
|
|
81
|
+
type: Object,
|
|
82
|
+
default: () => ({
|
|
83
|
+
icon: "icon",
|
|
84
|
+
label: "label",
|
|
85
|
+
path: "path",
|
|
86
|
+
children: "children",
|
|
87
|
+
id: "",
|
|
88
|
+
}),
|
|
89
|
+
},
|
|
90
|
+
//路由跳转方式path或name
|
|
91
|
+
pushWay: {
|
|
92
|
+
type: String,
|
|
93
|
+
default: "path",
|
|
94
|
+
},
|
|
95
|
+
// 路由base
|
|
96
|
+
baseUrl: {
|
|
97
|
+
type: String,
|
|
98
|
+
},
|
|
99
|
+
//子菜单是否占用组件宽度
|
|
100
|
+
isAbsolute: {
|
|
101
|
+
type: Boolean,
|
|
102
|
+
default: true,
|
|
103
|
+
},
|
|
104
|
+
isTransition: {
|
|
105
|
+
type: Boolean,
|
|
106
|
+
default: false,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
data() {
|
|
110
|
+
return {
|
|
111
|
+
isCollapse: true,
|
|
112
|
+
isCollapseShowIcon: true,
|
|
113
|
+
navMenuTitle: "",
|
|
114
|
+
subNnavMenuList: [],
|
|
115
|
+
checked: [],
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
created() {
|
|
119
|
+
this.selectPath();
|
|
120
|
+
},
|
|
121
|
+
watch: {
|
|
122
|
+
data: {
|
|
123
|
+
deep: true,
|
|
124
|
+
immediate: true,
|
|
125
|
+
handler(newRouter, oldRouter) {
|
|
126
|
+
if (newRouter != oldRouter) {
|
|
127
|
+
this.selectPath();
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
currentPath: {
|
|
132
|
+
immediate: true,
|
|
133
|
+
handler(newRouter, oldRouter) {
|
|
134
|
+
if (newRouter != oldRouter) {
|
|
135
|
+
this.selectPath();
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
//抛出菜单宽度更改
|
|
140
|
+
isCollapse(newV) {
|
|
141
|
+
this.$emit("changeWidth", newV);
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
computed: {
|
|
145
|
+
// 当前系统路由跳转Key
|
|
146
|
+
routerKey() {
|
|
147
|
+
return this.props.path;
|
|
148
|
+
},
|
|
149
|
+
currentPath() {
|
|
150
|
+
if (this.baseUrl) {
|
|
151
|
+
return (this.$route[this.pushWay] || "").split(this.baseUrl)[1] || "/";
|
|
152
|
+
} else {
|
|
153
|
+
// 适应于不属于微服务的情况下 处理高亮
|
|
154
|
+
return this.$route[this.pushWay];
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
methods: {
|
|
159
|
+
isActive(index) {
|
|
160
|
+
return index == this.checked[0];
|
|
161
|
+
},
|
|
162
|
+
subIsActive(index) {
|
|
163
|
+
return this.checked[1] == index;
|
|
164
|
+
},
|
|
165
|
+
// 导航栏展开与关闭
|
|
166
|
+
changecollapse() {
|
|
167
|
+
this.isCollapse = !this.isCollapse;
|
|
168
|
+
if (!this.isTransition) {
|
|
169
|
+
this.isCollapseShowIcon = this.isCollapse;
|
|
170
|
+
} else {
|
|
171
|
+
setTimeout(() => {
|
|
172
|
+
this.isCollapseShowIcon = this.isCollapse;
|
|
173
|
+
}, 100);
|
|
174
|
+
}
|
|
175
|
+
this.$emit("changeCollapse", this.isCollapse);
|
|
176
|
+
},
|
|
177
|
+
// 获取路由地址,设置一二级导航高亮
|
|
178
|
+
selectPath() {
|
|
179
|
+
if (!this.data.length) return;
|
|
180
|
+
let Fn = (list, parent = [], checked = []) => {
|
|
181
|
+
for (let index = 0; index < list.length; index++) {
|
|
182
|
+
const current = list[index];
|
|
183
|
+
let Path = current[this.routerKey];
|
|
184
|
+
let match = Path?.match(/\/:(.)*/g);
|
|
185
|
+
let newPath = Path?.replace(/\/:(.)*/g, "");
|
|
186
|
+
if (match?.length && this.currentPath.indexOf(newPath) == 0) {
|
|
187
|
+
return {
|
|
188
|
+
parent: [...parent, current],
|
|
189
|
+
checked: [...checked, index],
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
if (Path == this.currentPath) {
|
|
193
|
+
return {
|
|
194
|
+
parent: [...parent, current],
|
|
195
|
+
checked: [...checked, index],
|
|
196
|
+
};
|
|
197
|
+
} else {
|
|
198
|
+
let result = Fn(
|
|
199
|
+
current[this.props.children] || [],
|
|
200
|
+
[...parent, current],
|
|
201
|
+
[...checked, index]
|
|
202
|
+
);
|
|
203
|
+
if (result) {
|
|
204
|
+
return result;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
const routeObj = Fn(this.data);
|
|
210
|
+
if (routeObj) {
|
|
211
|
+
this.navMenuTitle = routeObj.parent[0][this.props.label];
|
|
212
|
+
this.subNnavMenuList = routeObj.parent[0][this.props.children] || [];
|
|
213
|
+
//取消三级高亮判断
|
|
214
|
+
this.checked = routeObj.checked;
|
|
215
|
+
this.changecollapseCreated();
|
|
216
|
+
} else {
|
|
217
|
+
this.navMenuTitle = "";
|
|
218
|
+
this.checked = [];
|
|
219
|
+
this.subNnavMenuList = [];
|
|
220
|
+
this.changecollapseCreated();
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
// 侧边导航栏路由跳转
|
|
224
|
+
navigatToPath(item, flag = true) {
|
|
225
|
+
let addBase = this.pushWay != "name";
|
|
226
|
+
if (flag) {
|
|
227
|
+
this.navMenuTitle = item[this.props.label];
|
|
228
|
+
this.subNnavMenuList = item[this.props.children] || [];
|
|
229
|
+
if (!this.subNnavMenuList.length) {
|
|
230
|
+
this.$router.push({
|
|
231
|
+
[this.pushWay]: `${addBase ? this.baseUrl : ""}${
|
|
232
|
+
item[this.routerKey]
|
|
233
|
+
}`,
|
|
234
|
+
});
|
|
235
|
+
} else {
|
|
236
|
+
this.$router.push({
|
|
237
|
+
[this.pushWay]: `${addBase ? this.baseUrl : ""}${
|
|
238
|
+
this.subNnavMenuList[0][this.routerKey]
|
|
239
|
+
}`,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
this.checked = [];
|
|
243
|
+
} else {
|
|
244
|
+
if (this.currentPath === item[this.routerKey]) {
|
|
245
|
+
this.selectPath();
|
|
246
|
+
}
|
|
247
|
+
this.$router.push({
|
|
248
|
+
[this.pushWay]: `${addBase ? this.baseUrl : ""}${
|
|
249
|
+
item[this.routerKey]
|
|
250
|
+
}`,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
this.changecollapseCreated();
|
|
254
|
+
// 抛出事件传递一个布尔值,false表示子导航收起,true表示子导航展开
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
// 判断子导航展开与否
|
|
258
|
+
changecollapseCreated() {
|
|
259
|
+
if (this.subNnavMenuList.length <= 0) {
|
|
260
|
+
this.isCollapse = false;
|
|
261
|
+
} else {
|
|
262
|
+
this.isCollapse = true;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (!this.isTransition) {
|
|
266
|
+
this.isCollapseShowIcon = this.isCollapse;
|
|
267
|
+
} else {
|
|
268
|
+
setTimeout(() => {
|
|
269
|
+
this.isCollapseShowIcon = this.isCollapse;
|
|
270
|
+
}, 100);
|
|
271
|
+
}
|
|
272
|
+
this.$emit("changeCollapse", this.isCollapse);
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
};
|
|
276
|
+
</script>
|
|
277
|
+
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="hb-organizedialog">
|
|
3
|
+
<el-dialog
|
|
4
|
+
:title="deptOnly ? '选择部门' : '选择部门和人员'"
|
|
5
|
+
v-bind="$attrs"
|
|
6
|
+
:visible.sync="visible"
|
|
7
|
+
:close-on-click-modal="false"
|
|
8
|
+
:close-on-press-escape="false"
|
|
9
|
+
:destroy-on-close="true"
|
|
10
|
+
width="700px"
|
|
11
|
+
@closed="clear"
|
|
12
|
+
>
|
|
13
|
+
<div v-if="loading" class="hb-organizedialog__loading">数据加载中...</div>
|
|
14
|
+
<div v-else class="content hb-organizedialog__wrapper">
|
|
15
|
+
<!-- 左侧已选择 -->
|
|
16
|
+
<div class="hb-organizedialog__part hb-organizedialog__choosed">
|
|
17
|
+
<div class="hb-organizedialog__choosed-wrapper">
|
|
18
|
+
<div class="hb-organizedialog__choosed-list">
|
|
19
|
+
<!-- 远程已选择 -->
|
|
20
|
+
<div
|
|
21
|
+
class="hb-organizedialog__choosed-list--origin item"
|
|
22
|
+
:class="{ allowed: cancelAble }"
|
|
23
|
+
v-for="item in checkedData.originChecked"
|
|
24
|
+
:key="item.id + item.isDept"
|
|
25
|
+
>
|
|
26
|
+
{{ item.name }}
|
|
27
|
+
<i
|
|
28
|
+
class="el-icon-close"
|
|
29
|
+
v-if="cancelAble"
|
|
30
|
+
@click="removecheckedData(item)"
|
|
31
|
+
></i>
|
|
32
|
+
</div>
|
|
33
|
+
<!-- 本地已选择 -->
|
|
34
|
+
<div
|
|
35
|
+
class="hb-organizedialog__choosed-list--local item"
|
|
36
|
+
v-for="item in checkedData.localChecked"
|
|
37
|
+
:key="item.id + item.isDept"
|
|
38
|
+
>
|
|
39
|
+
{{ item.name }}
|
|
40
|
+
<i class="el-icon-close" @click="removecheckedData(item)"></i>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
<!-- 搜索 -->
|
|
45
|
+
<div class="hb-organizedialog__search">
|
|
46
|
+
<el-input
|
|
47
|
+
size="small"
|
|
48
|
+
@focus="keywordsChange($event.target.value)"
|
|
49
|
+
@input="keywordsChange"
|
|
50
|
+
v-model="keyWords"
|
|
51
|
+
placeholder="支持全拼或首拼检索"
|
|
52
|
+
/>
|
|
53
|
+
<el-dropdown
|
|
54
|
+
trigger="click"
|
|
55
|
+
ref="searchBox"
|
|
56
|
+
@command="chooseSearchResult"
|
|
57
|
+
>
|
|
58
|
+
<span></span>
|
|
59
|
+
<el-dropdown-menu
|
|
60
|
+
slot="dropdown"
|
|
61
|
+
class="hb-organizedialog__organization"
|
|
62
|
+
>
|
|
63
|
+
<template v-if="searchData.length">
|
|
64
|
+
<el-dropdown-item
|
|
65
|
+
v-for="item in searchData"
|
|
66
|
+
:key="item.id"
|
|
67
|
+
:command="item"
|
|
68
|
+
:disabled="item.checked"
|
|
69
|
+
>
|
|
70
|
+
<span><img class="avatar" :src="item.logo" alt="" /></span>
|
|
71
|
+
<span v-html="item.html"></span>
|
|
72
|
+
</el-dropdown-item>
|
|
73
|
+
</template>
|
|
74
|
+
<template v-else>
|
|
75
|
+
<el-dropdown-item
|
|
76
|
+
><span>没有搜到任何结果</span></el-dropdown-item
|
|
77
|
+
>
|
|
78
|
+
</template>
|
|
79
|
+
</el-dropdown-menu>
|
|
80
|
+
</el-dropdown>
|
|
81
|
+
</div>
|
|
82
|
+
<!-- 底部按钮 -->
|
|
83
|
+
<div class="hb-organizedialog__foot">
|
|
84
|
+
<el-button
|
|
85
|
+
type="primary"
|
|
86
|
+
@click="confirm"
|
|
87
|
+
:disabled="!checkedNodesNumber"
|
|
88
|
+
size="small"
|
|
89
|
+
>
|
|
90
|
+
确定
|
|
91
|
+
<span v-if="!deptOnly">
|
|
92
|
+
{{
|
|
93
|
+
"( " +
|
|
94
|
+
checkedNodesNumber +
|
|
95
|
+
" / " +
|
|
96
|
+
countMembers(fullTreeData) +
|
|
97
|
+
" )"
|
|
98
|
+
}}
|
|
99
|
+
</span>
|
|
100
|
+
</el-button>
|
|
101
|
+
<el-button size="small" @click="close">取消</el-button>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<!-- 右侧tree -->
|
|
106
|
+
<div class="hb-organizedialog__part hb-organizedialog__tree">
|
|
107
|
+
<!-- 公司 -->
|
|
108
|
+
<div class="hb-organizedialog__tree-company">{{ company }}</div>
|
|
109
|
+
<!-- 组织路径 -->
|
|
110
|
+
<div class="hb-organizedialog__nav">
|
|
111
|
+
<div
|
|
112
|
+
class="hb-organizedialog__nav-item"
|
|
113
|
+
v-for="(item, index) in navPathData"
|
|
114
|
+
:key="item.id"
|
|
115
|
+
:class="{ 'not-allow': item.active }"
|
|
116
|
+
@click="positionDept(index)"
|
|
117
|
+
>
|
|
118
|
+
{{ item.name }}
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
<!-- 组织树 -->
|
|
122
|
+
<div class="hb-organizedialog__tree-wrapper">
|
|
123
|
+
<el-checkbox v-model="allChecked" @change="chooseAll"
|
|
124
|
+
>全选</el-checkbox
|
|
125
|
+
>
|
|
126
|
+
<div
|
|
127
|
+
class="hb-organizedialog__tree-item"
|
|
128
|
+
v-for="item in renderData"
|
|
129
|
+
:key="item.id + item.isDept"
|
|
130
|
+
>
|
|
131
|
+
<el-checkbox
|
|
132
|
+
v-model="item.checked"
|
|
133
|
+
:disabled="item.disabled && !cancelAble"
|
|
134
|
+
@change="
|
|
135
|
+
(boolean) => {
|
|
136
|
+
chooseMember(item, fullTreeData, boolean);
|
|
137
|
+
isAllChecked();
|
|
138
|
+
}
|
|
139
|
+
"
|
|
140
|
+
>
|
|
141
|
+
<!-- 头像 -->
|
|
142
|
+
<span v-if="item.isDept === '0'"
|
|
143
|
+
><img :src="item.logo" class="avatar" alt=""
|
|
144
|
+
/></span>
|
|
145
|
+
<!-- 名称 -->
|
|
146
|
+
<span>{{ item.name }}</span>
|
|
147
|
+
<!-- 人数 -->
|
|
148
|
+
<span v-if="item.isDept === '1' && !deptOnly">{{
|
|
149
|
+
" (" + item.members + ") "
|
|
150
|
+
}}</span>
|
|
151
|
+
</el-checkbox>
|
|
152
|
+
<el-button
|
|
153
|
+
class="subordinate-btn"
|
|
154
|
+
v-if="item.isDept === '1' && item.children.length"
|
|
155
|
+
@click="unfold(item)"
|
|
156
|
+
:disabled="item.checked"
|
|
157
|
+
:class="{ 'not-allow': item.checked }"
|
|
158
|
+
>
|
|
159
|
+
下级
|
|
160
|
+
</el-button>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
</el-dialog>
|
|
166
|
+
</div>
|
|
167
|
+
</template>
|
|
168
|
+
<script>
|
|
169
|
+
import { cloneDeep } from "lodash";
|
|
170
|
+
import pinyinMatch from "pinyin-match";
|
|
171
|
+
export default {
|
|
172
|
+
name: "HbOrganizeDialog",
|
|
173
|
+
componentName: "HbOrganizeDialog",
|
|
174
|
+
data() {
|
|
175
|
+
return {
|
|
176
|
+
visible: false,
|
|
177
|
+
loading: true,
|
|
178
|
+
company: "",
|
|
179
|
+
fullTreeData: [], //完整的已加工的树形数据
|
|
180
|
+
disableData: [], //远程已选中的项(禁用状态)
|
|
181
|
+
renderData: [], //当前渲染的项
|
|
182
|
+
navPathData: [], //组织树路径
|
|
183
|
+
searchData: [], //搜索结果项集合
|
|
184
|
+
keyWords: "",
|
|
185
|
+
allChecked: false,
|
|
186
|
+
timer: null, //模糊搜索节流
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
computed: {
|
|
190
|
+
checkedData() {
|
|
191
|
+
let originChecked = [];
|
|
192
|
+
let localChecked = [];
|
|
193
|
+
chooseNodes(this.fullTreeData, this);
|
|
194
|
+
function chooseNodes(nodes, _this) {
|
|
195
|
+
nodes.forEach((node) => {
|
|
196
|
+
if (node.checked && node.disabled) {
|
|
197
|
+
let isSameNode = originChecked.some((i) =>
|
|
198
|
+
_this.isSameNode(i, node)
|
|
199
|
+
);
|
|
200
|
+
!isSameNode && originChecked.push(node);
|
|
201
|
+
} else if (node.checked && !node.disabled) {
|
|
202
|
+
let isSameNode = localChecked.some((i) =>
|
|
203
|
+
_this.isSameNode(i, node)
|
|
204
|
+
);
|
|
205
|
+
!isSameNode && localChecked.push(node);
|
|
206
|
+
}
|
|
207
|
+
chooseNodes(node.children, _this);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
originChecked,
|
|
212
|
+
localChecked,
|
|
213
|
+
};
|
|
214
|
+
},
|
|
215
|
+
checkedNodesNumber() {
|
|
216
|
+
return (
|
|
217
|
+
this.checkedData.localChecked.length +
|
|
218
|
+
(this.cancelAble ? this.checkedData.originChecked.length : 0)
|
|
219
|
+
);
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
methods: {
|
|
223
|
+
open() {
|
|
224
|
+
this.visible = true;
|
|
225
|
+
},
|
|
226
|
+
close() {
|
|
227
|
+
this.visible = false;
|
|
228
|
+
},
|
|
229
|
+
clear() {
|
|
230
|
+
this.loading = true;
|
|
231
|
+
this.company = "";
|
|
232
|
+
this.disableData = [];
|
|
233
|
+
this.renderData = [];
|
|
234
|
+
this.navPathData = [];
|
|
235
|
+
this.searchData = [];
|
|
236
|
+
this.keyWords = "";
|
|
237
|
+
this.allChecked = false;
|
|
238
|
+
this.timer = null;
|
|
239
|
+
},
|
|
240
|
+
//初始化界面
|
|
241
|
+
init(originData = {}, disableData = []) {
|
|
242
|
+
if (!originData.children) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
// 初始化公司名
|
|
246
|
+
this.company = originData.name;
|
|
247
|
+
// 初始化组织树数据
|
|
248
|
+
this.disableData = disableData;
|
|
249
|
+
this.fullTreeData = this.factoryTreeData(cloneDeep(originData.children));
|
|
250
|
+
// 初始化组织路径
|
|
251
|
+
this.navPathData.push({
|
|
252
|
+
id: originData.id,
|
|
253
|
+
name: originData.name,
|
|
254
|
+
children: this.fullTreeData,
|
|
255
|
+
active: true,
|
|
256
|
+
});
|
|
257
|
+
this.renderTree();
|
|
258
|
+
// 组织树数据初始化完成
|
|
259
|
+
this.loading = false;
|
|
260
|
+
},
|
|
261
|
+
//check节点时,因可能存在相同的节点,需要全部选中(人员出现在多个部门,部门出现在不同分组中)
|
|
262
|
+
chooseMember(target, nodes, boolean) {
|
|
263
|
+
nodes.forEach((node) => {
|
|
264
|
+
this.isSameNode(target, node) && (node.checked = boolean);
|
|
265
|
+
this.chooseMember(target, node.children, boolean);
|
|
266
|
+
});
|
|
267
|
+
},
|
|
268
|
+
factoryTreeData(originData, parentName = this.company) {
|
|
269
|
+
originData.forEach((node) => {
|
|
270
|
+
//是否远程已选
|
|
271
|
+
let target = this.disableData.find((i) => this.isSameNode(i, node));
|
|
272
|
+
//将远程已选数据的额外字段合并到treeNode数据上
|
|
273
|
+
target && Object.assign(node, target);
|
|
274
|
+
node.checked = node.disabled = Boolean(target);
|
|
275
|
+
node.members = this.deptOnly ? 0 : this.countMembers(node.children);
|
|
276
|
+
node.parentName = parentName;
|
|
277
|
+
this.factoryTreeData(node.children, node.name);
|
|
278
|
+
});
|
|
279
|
+
return originData;
|
|
280
|
+
},
|
|
281
|
+
//初始化组织树数据
|
|
282
|
+
renderTree(nodes = this.fullTreeData) {
|
|
283
|
+
this.renderData = nodes;
|
|
284
|
+
this.isAllChecked();
|
|
285
|
+
},
|
|
286
|
+
//全选
|
|
287
|
+
chooseAll(boolean) {
|
|
288
|
+
this.renderData.forEach((node) => {
|
|
289
|
+
if (this.cancelAble) {
|
|
290
|
+
node.checked = boolean;
|
|
291
|
+
this.chooseMember(node, this.fullTreeData, boolean);
|
|
292
|
+
} else {
|
|
293
|
+
if (!node.disabled) {
|
|
294
|
+
node.checked = boolean;
|
|
295
|
+
this.chooseMember(node, this.fullTreeData, boolean);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
this.isAllChecked();
|
|
300
|
+
},
|
|
301
|
+
//移除已选择的选项
|
|
302
|
+
removecheckedData(node) {
|
|
303
|
+
this.chooseMember(node, this.fullTreeData, false);
|
|
304
|
+
this.isAllChecked();
|
|
305
|
+
},
|
|
306
|
+
//判断是否形成全选,并处理全选按钮
|
|
307
|
+
isAllChecked() {
|
|
308
|
+
this.allChecked = this.renderData.every((node) => node.checked);
|
|
309
|
+
},
|
|
310
|
+
//判断是不是同一个节点
|
|
311
|
+
isSameNode(reference, target) {
|
|
312
|
+
return reference.id === target.id && reference.isDept === target.isDept;
|
|
313
|
+
},
|
|
314
|
+
//计算原始数据成员数
|
|
315
|
+
countMembers(subNodes, prevnum = []) {
|
|
316
|
+
subNodes.forEach((item) => {
|
|
317
|
+
item.isDept === "0" && prevnum.push(item.id);
|
|
318
|
+
});
|
|
319
|
+
subNodes.forEach((item) => {
|
|
320
|
+
if (item.isDept === "1" && item.children.length) {
|
|
321
|
+
this.countMembers(item.children, prevnum);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
return [...new Set(prevnum)].length;
|
|
325
|
+
},
|
|
326
|
+
//展开此组织层级
|
|
327
|
+
unfold(node) {
|
|
328
|
+
//更新组织树路径
|
|
329
|
+
this.navPathData.forEach((item) => (item.active = false));
|
|
330
|
+
this.navPathData.push({
|
|
331
|
+
id: node.id,
|
|
332
|
+
name: node.name,
|
|
333
|
+
active: true,
|
|
334
|
+
children: node.children,
|
|
335
|
+
});
|
|
336
|
+
//渲染新的组织树节点
|
|
337
|
+
this.renderTree(node.children);
|
|
338
|
+
//判断是否全选
|
|
339
|
+
this.isAllChecked();
|
|
340
|
+
},
|
|
341
|
+
//切换到指定组织
|
|
342
|
+
positionDept(num) {
|
|
343
|
+
// 修改组织路径
|
|
344
|
+
this.navPathData = this.navPathData.filter((item, index) => index <= num);
|
|
345
|
+
this.navPathData.forEach((item, index) => {
|
|
346
|
+
item.active = num === index;
|
|
347
|
+
});
|
|
348
|
+
// 初始化组织树数据
|
|
349
|
+
this.renderTree(this.navPathData[num].children);
|
|
350
|
+
//判断是否全选
|
|
351
|
+
this.isAllChecked();
|
|
352
|
+
},
|
|
353
|
+
confirm() {
|
|
354
|
+
this.$emit("confirm", this.checkedData);
|
|
355
|
+
},
|
|
356
|
+
//输入搜索关键字(节流)
|
|
357
|
+
keywordsChange(value) {
|
|
358
|
+
if (this.timer) {
|
|
359
|
+
clearTimeout(this.timer);
|
|
360
|
+
}
|
|
361
|
+
this.timer = setTimeout(() => {
|
|
362
|
+
value = value.trim();
|
|
363
|
+
if (value) {
|
|
364
|
+
this.$refs.searchBox.show();
|
|
365
|
+
this.searchData = [];
|
|
366
|
+
this.vagueSearch(value);
|
|
367
|
+
//如果有结果,高亮关键字
|
|
368
|
+
this.highlightWords();
|
|
369
|
+
this.searchData.sort((a, b) => a.isDept - b.isDept);
|
|
370
|
+
} else {
|
|
371
|
+
this.$refs.searchBox.hide();
|
|
372
|
+
}
|
|
373
|
+
}, 500);
|
|
374
|
+
},
|
|
375
|
+
//从搜索结果中选取节点
|
|
376
|
+
chooseSearchResult(node) {
|
|
377
|
+
this.keyWords = "";
|
|
378
|
+
this.chooseMember(node, this.fullTreeData, true);
|
|
379
|
+
this.isAllChecked();
|
|
380
|
+
},
|
|
381
|
+
//模糊搜索(支持拼音)
|
|
382
|
+
vagueSearch(value, originData = this.fullTreeData) {
|
|
383
|
+
originData.forEach((node) => {
|
|
384
|
+
let matched = pinyinMatch.match(node.name, value);
|
|
385
|
+
if (matched) {
|
|
386
|
+
//去重
|
|
387
|
+
let isInCludes = this.searchData.some((i) =>
|
|
388
|
+
this.isSameNode(i, node)
|
|
389
|
+
);
|
|
390
|
+
if (!isInCludes) {
|
|
391
|
+
this.searchData.push(node);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (node.children.length) {
|
|
395
|
+
this.vagueSearch(value, node.children);
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
},
|
|
399
|
+
//高亮匹配的搜索关键字
|
|
400
|
+
highlightWords() {
|
|
401
|
+
if (this.searchData.length) {
|
|
402
|
+
let keyWordsArr = this.keyWords.split("");
|
|
403
|
+
this.searchData.forEach((item) => {
|
|
404
|
+
let targetArr = item.name.split("");
|
|
405
|
+
targetArr.forEach((word, index, self) => {
|
|
406
|
+
let include = keyWordsArr.includes(word);
|
|
407
|
+
self[index] = include ? `<i>${word}</i>` : word;
|
|
408
|
+
});
|
|
409
|
+
item.html =
|
|
410
|
+
(item.isDept === "1" ? `${item.parentName} / ` : "") +
|
|
411
|
+
targetArr.join("");
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
props: {
|
|
417
|
+
deptOnly: {
|
|
418
|
+
type: Boolean,
|
|
419
|
+
default: false,
|
|
420
|
+
},
|
|
421
|
+
cancelAble: {
|
|
422
|
+
type: Boolean,
|
|
423
|
+
default: false,
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
};
|
|
427
|
+
</script>
|