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.
Files changed (137) hide show
  1. package/.browserslistrc +3 -0
  2. package/.editorconfig +7 -0
  3. package/.env.development +1 -0
  4. package/.env.lib +1 -0
  5. package/.eslintrc.js +14 -0
  6. package/.prettierrc +3 -0
  7. package/README.md +53 -0
  8. package/babel.config.js +3 -0
  9. package/docs/.vuepress/config.js +23 -0
  10. package/docs/.vuepress/styles/index.styl +4 -0
  11. package/docs/AdvQuery.md +26 -0
  12. package/docs/CustomQuery.md +21 -0
  13. package/docs/EditArea.md +16 -0
  14. package/docs/FilterDrop.md +29 -0
  15. package/docs/FullTable.md +68 -0
  16. package/docs/Pagination.md +22 -0
  17. package/docs/QueryForm.md +27 -0
  18. package/docs/README.md +17 -0
  19. package/docs/Table.md +58 -0
  20. package/docs/ToolGroup.md +32 -0
  21. package/examples/App.vue +97 -0
  22. package/examples/image/hb-image-viewer/3k.jpg +0 -0
  23. package/examples/image/hb-image-viewer/a.jpg +0 -0
  24. package/examples/image/hb-image-viewer/b.jpg +0 -0
  25. package/examples/main.js +39 -0
  26. package/examples/mock/authority.js +1108 -0
  27. package/examples/mock/dept.js +4001 -0
  28. package/examples/mock/fulltable.js +407 -0
  29. package/examples/mock/organizeData.js +18965 -0
  30. package/examples/router/index.js +111 -0
  31. package/examples/views/ElementCpTest.vue +299 -0
  32. package/examples/views/Fragment.vue +196 -0
  33. package/examples/views/HBAdvQuery.vue +150 -0
  34. package/examples/views/HBAuthority.vue +339 -0
  35. package/examples/views/HBDialog.vue +39 -0
  36. package/examples/views/HBEditarea.vue +350 -0
  37. package/examples/views/HBFilterDrop.vue +72 -0
  38. package/examples/views/HBFloatFooter.vue +42 -0
  39. package/examples/views/HBFullTable.vue +751 -0
  40. package/examples/views/HBImageViewer.vue +57 -0
  41. package/examples/views/HBNavMenu.vue +37 -0
  42. package/examples/views/HBOrganizeDialog.vue +133 -0
  43. package/examples/views/HBPagination.vue +80 -0
  44. package/examples/views/HBSelectVague.vue +57 -0
  45. package/examples/views/HBToolgroup.vue +29 -0
  46. package/examples/views/HBTopBar.vue +16 -0
  47. package/lib/demo.html +10 -0
  48. package/lib/fonts/iconfont.9e99af35.ttf +0 -0
  49. package/lib/hbte-ui.common.js +38291 -0
  50. package/lib/hbte-ui.css +1 -0
  51. package/lib/hbte-ui.umd.js +38301 -0
  52. package/lib/hbte-ui.umd.min.js +21 -0
  53. package/lib/img/nodata.f87d17c1.png +0 -0
  54. package/lib/img/user-logo--default.a53bd193.png +0 -0
  55. package/package.json +40 -0
  56. package/packages/adv-query/index.js +5 -0
  57. package/packages/adv-query/src/main.vue +294 -0
  58. package/packages/area-select/index.js +7 -0
  59. package/packages/area-select/src/main.vue +37 -0
  60. package/packages/authority/index.js +5 -0
  61. package/packages/authority/src/main.vue +606 -0
  62. package/packages/config.js +3 -0
  63. package/packages/custom-query/index.js +5 -0
  64. package/packages/custom-query/src/main.vue +80 -0
  65. package/packages/dialog/index.js +7 -0
  66. package/packages/dialog/src/main.vue +176 -0
  67. package/packages/edit-area/index.js +13 -0
  68. package/packages/edit-area/src/config.js +3 -0
  69. package/packages/edit-area/src/main.vue +304 -0
  70. package/packages/edit-area/src/simulateEvent.js +219 -0
  71. package/packages/filter-drop/index.js +6 -0
  72. package/packages/filter-drop/src/main.vue +107 -0
  73. package/packages/float-footer/index.js +7 -0
  74. package/packages/float-footer/src/main.vue +40 -0
  75. package/packages/fragment/index.js +7 -0
  76. package/packages/fragment/src/main.vue +74 -0
  77. package/packages/fragment/src/old.vue +144 -0
  78. package/packages/full-table/index.js +6 -0
  79. package/packages/full-table/src/config.js +14 -0
  80. package/packages/full-table/src/main.vue +141 -0
  81. package/packages/image-viewer/index.js +8 -0
  82. package/packages/image-viewer/src/main.js +14 -0
  83. package/packages/image-viewer/src/main.vue +138 -0
  84. package/packages/image-viewer/src/svg/cancel.svg +2 -0
  85. package/packages/image-viewer/src/svg/delete.svg +2 -0
  86. package/packages/image-viewer/src/svg/download.svg +2 -0
  87. package/packages/image-viewer/src/svg/rotate.svg +77 -0
  88. package/packages/image-viewer/src/svg/scaledown.svg +71 -0
  89. package/packages/image-viewer/src/svg/scaleup.svg +72 -0
  90. package/packages/index.js +43 -0
  91. package/packages/nav-menu/index.js +7 -0
  92. package/packages/nav-menu/src/main.vue +277 -0
  93. package/packages/organize-dialog/index.js +5 -0
  94. package/packages/organize-dialog/src/main.vue +427 -0
  95. package/packages/pagination/index.js +6 -0
  96. package/packages/pagination/src/main.vue +145 -0
  97. package/packages/query-form/index.js +5 -0
  98. package/packages/query-form/src/main.vue +142 -0
  99. package/packages/select-vague/index.js +5 -0
  100. package/packages/select-vague/src/main.vue +80 -0
  101. package/packages/table/index.js +5 -0
  102. package/packages/table/src/main.vue +265 -0
  103. package/packages/theme-chalk/adv-query.scss +107 -0
  104. package/packages/theme-chalk/authority.scss +103 -0
  105. package/packages/theme-chalk/common/elementCover.scss +722 -0
  106. package/packages/theme-chalk/common/flex.scss +66 -0
  107. package/packages/theme-chalk/common/global.scss +147 -0
  108. package/packages/theme-chalk/common/reset.scss +92 -0
  109. package/packages/theme-chalk/common/var.scss +46 -0
  110. package/packages/theme-chalk/custom-query.scss +10 -0
  111. package/packages/theme-chalk/dialog.scss +9 -0
  112. package/packages/theme-chalk/edit-area.scss +119 -0
  113. package/packages/theme-chalk/filter-drop.scss +81 -0
  114. package/packages/theme-chalk/float-footer.scss +14 -0
  115. package/packages/theme-chalk/fonts/iconfont.ttf +0 -0
  116. package/packages/theme-chalk/fragment.scss +51 -0
  117. package/packages/theme-chalk/full-table.scss +40 -0
  118. package/packages/theme-chalk/icon.scss +96 -0
  119. package/packages/theme-chalk/image-viewer.scss +94 -0
  120. package/packages/theme-chalk/images/company-logo--default.png +0 -0
  121. package/packages/theme-chalk/images/nodata.png +0 -0
  122. package/packages/theme-chalk/images/user-logo--default.png +0 -0
  123. package/packages/theme-chalk/index.scss +21 -0
  124. package/packages/theme-chalk/nav-menu.scss +205 -0
  125. package/packages/theme-chalk/organize-dialog.scss +170 -0
  126. package/packages/theme-chalk/pagination.scss +91 -0
  127. package/packages/theme-chalk/select.scss +8 -0
  128. package/packages/theme-chalk/table.scss +102 -0
  129. package/packages/theme-chalk/tool-group.scss +105 -0
  130. package/packages/theme-chalk/top-bar.scss +195 -0
  131. package/packages/tool-group/index.js +13 -0
  132. package/packages/tool-group/src/config.js +40 -0
  133. package/packages/tool-group/src/main.vue +93 -0
  134. package/packages/top-bar/index.js +7 -0
  135. package/packages/top-bar/src/main.vue +280 -0
  136. package/public/index.html +20 -0
  137. 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,5 @@
1
+ import HbOrganizeDialog from "./src/main.vue";
2
+ HbOrganizeDialog.install = function (Vue) {
3
+ Vue.component(HbOrganizeDialog.name, HbOrganizeDialog);
4
+ };
5
+ export default HbOrganizeDialog;
@@ -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>