imeik-bizui 1.7.7 → 1.7.9

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.
@@ -11,3 +11,15 @@ export function departmentSelector(params) {
11
11
  params
12
12
  })
13
13
  }
14
+
15
+ /**
16
+ * 部门列表,包含虚拟节点
17
+ * /permissioncenter-admin/department/listDepartmentWithVirtual
18
+ */
19
+ export function listDepartmentWithVirtual(params) {
20
+ return request({
21
+ url: '/permissioncenter-admin/department/listDepartmentWithVirtual',
22
+ method: 'get',
23
+ params
24
+ })
25
+ }
@@ -0,0 +1,326 @@
1
+ <template>
2
+ <div :class="!isView ? 'w-full' : 'inline'">
3
+ <template v-if="isView">
4
+ <div v-if="isWxWorkMobile && needShowMore" class="mobile-text-container">
5
+ <span v-if="!plantText">-</span>
6
+ <div v-else-if="showAll" :id="`plantText-${random}`" class="show-all-text">
7
+ {{ plantText }}
8
+ <div class="btn-text" @click="showMore">
9
+ 收起
10
+ <img class="icon-retract" src="https://imeikud.oss-cn-beijing.aliyuncs.com/pcUploads/1724846415100/icon-retract.png" />
11
+ </div>
12
+ </div>
13
+ <div v-else class="show-limit-text">
14
+ {{ plantText }}
15
+ <div class="btn-text show-more" @click="showMore">
16
+ 展开
17
+ <img class="icon-show-more" src="https://imeikud.oss-cn-beijing.aliyuncs.com/pcUploads/1724846420511/icon-show-more.png" />
18
+ </div>
19
+ </div>
20
+ </div>
21
+ <template v-else-if="plantText">
22
+ <span v-if="showAll" class="viewText showAll">{{ plantText }}</span>
23
+ <el-popover v-else trigger="hover">
24
+ <div class="departmentPopover">{{ plantText }}</div>
25
+ <span slot="reference" class="viewText">{{ plantText }}</span>
26
+ </el-popover>
27
+ </template>
28
+ <span v-else>-</span>
29
+ </template>
30
+
31
+ <el-cascader
32
+ v-else
33
+ ref="select"
34
+ v-model="myValue"
35
+ popper-class="loading"
36
+ class="w-full"
37
+ v-bind="$attrs"
38
+ :props="cascaderProps"
39
+ :options="options"
40
+ :placeholder="$attrs.placeholder || (optionsLoading ? '加载中...' : '请选择部门')"
41
+ :collapse-tags="$attrs.collapseTags || collapseTags"
42
+ :clearable="$attrs.clearable !== false"
43
+ :filterable="$attrs.filterable !== false"
44
+ @change="handleChange"
45
+ >
46
+ </el-cascader>
47
+ </div>
48
+ </template>
49
+
50
+ <script>
51
+ import emitter from 'element-ui/src/mixins/emitter'
52
+ import { listDepartmentWithVirtual } from '../../api/permission'
53
+ import { tree2Array, getLabelByValue } from '../../utils/index'
54
+
55
+ export default {
56
+ name: 'CommonDepartmentCascader',
57
+ mixins: [emitter],
58
+ props: {
59
+ value: {
60
+ type: [String, Number, Array],
61
+ default: undefined
62
+ },
63
+ multiple: {
64
+ type: Boolean,
65
+ default: false
66
+ },
67
+ collapseTags: {
68
+ type: Boolean,
69
+ default: false
70
+ },
71
+ emitPath: {
72
+ type: Boolean,
73
+ default: false
74
+ },
75
+ propValue: {
76
+ type: String,
77
+ default: 'departmentCode'
78
+ },
79
+ expandTrigger: {
80
+ type: String,
81
+ default: 'click'
82
+ },
83
+ checkStrictly: {
84
+ type: Boolean,
85
+ default: false
86
+ },
87
+ extra: {
88
+ type: Object,
89
+ default() {
90
+ return {}
91
+ }
92
+ },
93
+ // 是否仅展示文字
94
+ isView: {
95
+ type: Boolean,
96
+ default: false
97
+ },
98
+ // 查看的时候是否展示全部
99
+ showAll: {
100
+ type: Boolean,
101
+ default: true
102
+ },
103
+ // 是否仅展示主公司
104
+ onlyMainCompany: {
105
+ type: Boolean,
106
+ default: true
107
+ }
108
+ },
109
+ data() {
110
+ return {
111
+ myValue: undefined,
112
+ options: [],
113
+ optionsLoading: false,
114
+ childHasUnChecked: false,
115
+ showAll: true,
116
+ needShowMore: true,
117
+ random: Math.random() * 100
118
+ }
119
+ },
120
+ computed: {
121
+ /**
122
+ * 判断当前环境是否为企业微信移动端,企微移动端禁止了浏览器的下载功能
123
+ * 这时候不展示下载按钮即可
124
+ */
125
+ isWxWorkMobile() {
126
+ const userAgent = window.navigator.userAgent.toLowerCase()
127
+ const result = /wxwork/i.test(userAgent) && /iphone|android/i.test(userAgent)
128
+ // 企业微信移动端
129
+ return result
130
+ },
131
+ cascaderProps() {
132
+ return {
133
+ multiple: this.multiple,
134
+ label: 'departmentName',
135
+ value: this.propValue,
136
+ children: 'childDepartments',
137
+ emitPath: this.emitPath,
138
+ expandTrigger: this.expandTrigger,
139
+ checkStrictly: this.checkStrictly
140
+ }
141
+ },
142
+ plantText() {
143
+ const arr = tree2Array(this.options, { key: 'childDepartments' })
144
+ const vals = [].concat(this.myValue) // 单选的时候也转成数组
145
+ const list = []
146
+
147
+ vals.map((i) => {
148
+ list.push(getLabelByValue(arr, i, { labelKey: 'departmentName', valueKey: this.propValue }))
149
+ return i
150
+ })
151
+
152
+ return list.join(', ')
153
+ }
154
+ },
155
+ watch: {
156
+ value: {
157
+ immediate: true,
158
+ handler() {
159
+ this.setMyValue()
160
+ }
161
+ },
162
+ plantText: {
163
+ immediate: true,
164
+ handler(val) {
165
+ this.$nextTick(() => {
166
+ const divElement = document.getElementById(`plantText-${this.random}`)
167
+ if (divElement) {
168
+ const style = window.getComputedStyle(divElement, null)
169
+ const height = style.height.replace('px', '')
170
+ const lineHeight = style.lineHeight.replace('px', '')
171
+ if (height / lineHeight >= 2) {
172
+ this.needShowMore = true
173
+ this.showAll = false
174
+ } else {
175
+ this.needShowMore = false
176
+ this.showAll = true
177
+ }
178
+ }
179
+ })
180
+ }
181
+ }
182
+ },
183
+ created() {
184
+ this.getOptions()
185
+ },
186
+ methods: {
187
+ showMore() {
188
+ this.showAll = !this.showAll
189
+ this.$emit('showMore', this.showAll)
190
+ },
191
+
192
+ setMyValue() {
193
+ try {
194
+ this.myValue = JSON.parse(JSON.stringify(this.value))
195
+ } catch (error) {
196
+ this.myValue = this.value
197
+ }
198
+ },
199
+
200
+ getOptions() {
201
+ const params = {
202
+ ...this.extra
203
+ }
204
+ this.optionsLoading = true
205
+ listDepartmentWithVirtual(params)
206
+ .then((res) => {
207
+ if (res.code === 200) {
208
+ if (!res.data) {
209
+ return
210
+ }
211
+ let resData = res.data || []
212
+ if (this.onlyMainCompany) {
213
+ resData = resData.filter(item => {
214
+ return item.orgCode === 'virtual-201'
215
+ })
216
+ }
217
+ this.options = this.dealList(resData || [])
218
+ }
219
+ })
220
+ .finally(() => {
221
+ this.optionsLoading = false
222
+ })
223
+ },
224
+
225
+ dealList(arr) {
226
+ return arr.map((i) => {
227
+ if (i.childDepartments && i.childDepartments.length) {
228
+ this.dealList(i.childDepartments)
229
+ } else {
230
+ delete i.childDepartments
231
+ }
232
+ return i
233
+ })
234
+ },
235
+
236
+ handleChange() {
237
+ this.$emit('input', this.myValue)
238
+ this.$emit('change', this.myValue)
239
+ this.dispatch('ElFormItem', 'el.form.change', this.myValue)
240
+ }
241
+ }
242
+ }
243
+ </script>
244
+ <style lang="scss" scoped>
245
+ .mobile-text-container {
246
+ position: relative;
247
+ .show-more {
248
+ position: absolute;
249
+ bottom: 0 !important;
250
+ right: 0;
251
+ &::before {
252
+ content: '';
253
+ position: absolute;
254
+ bottom: 0;
255
+ right: 48px;
256
+ background: linear-gradient( 270deg, #FFFFFF 0%, rgba(255,255,255,0) 100%);
257
+ width: 60px;
258
+ height: 31px;
259
+ }
260
+ }
261
+ .btn-text {
262
+ position: absolute;
263
+ bottom: -28px;
264
+ right: 0;
265
+ display: flex;
266
+ align-items: center;
267
+ background: #fff;
268
+ font-family: PingFangSC, PingFang SC;
269
+ font-weight: 500;
270
+ font-size: 14px;
271
+ color: #3285F6;
272
+ // line-height: 31px;
273
+ .icon-retract, .icon-show-more {
274
+ width: 16px;
275
+ height: 16px;
276
+ margin-left: 4px;
277
+ }
278
+ }
279
+ }
280
+ .show-limit-text {
281
+ white-space: nowrap;
282
+ overflow: hidden;
283
+ text-overflow: ellipsis;
284
+ }
285
+ .viewText {
286
+ max-width: max-content;
287
+ overflow: hidden;
288
+ display: block;
289
+ white-space: nowrap;
290
+ text-overflow: ellipsis;
291
+ cursor: pointer;
292
+
293
+ &.showAll {
294
+ text-overflow: unset;
295
+ overflow: visible;
296
+ white-space: break-spaces;
297
+ }
298
+ }
299
+
300
+ .labelItem {
301
+ display: flex;
302
+ align-items: center;
303
+ gap: 8px;
304
+ transform: translate(6px, 0px);
305
+ justify-content: space-between;
306
+
307
+ .label {
308
+ width: max-content;
309
+ text-overflow: ellipsis;
310
+ white-space: nowrap;
311
+ }
312
+ .selecter {
313
+ font-size: 14px;
314
+ cursor: pointer;
315
+ }
316
+ }
317
+
318
+ .departmentPopover {
319
+ max-width: 300px;
320
+ max-height: 200px;
321
+ overflow: auto;
322
+ padding-right: 12px;
323
+ line-height: 24px;
324
+ cursor: pointer;
325
+ }
326
+ </style>
@@ -0,0 +1,110 @@
1
+ <template>
2
+ <label :style="{ width: `${selectWidth}px` }" class="shape-check-box el-checkbox" :class="{'is-checked': checked}">
3
+ <span v-if="multiple" class="el-checkbox__input" :class="{'is-checked': checked}">
4
+ <span class="el-checkbox__inner"></span>
5
+ </span>
6
+
7
+ <span class="el-checkbox__label">
8
+ <slot></slot>
9
+ </span>
10
+ </label>
11
+
12
+ <!-- <span v-else :style="{ width: `${selectWidth}px` }" class="el-checkbox__label">
13
+ <slot></slot>
14
+ </span> -->
15
+ </template>
16
+
17
+ <script>
18
+ // 模拟cascader,改变下拉组件多选时的展示样式 - 在每行前面加checkbox
19
+ export default {
20
+ props: {
21
+ // 是否多选
22
+ multiple: {
23
+ type: Boolean,
24
+ default: false
25
+ },
26
+ // 是否 checked
27
+ checked: {
28
+ type: Boolean,
29
+ default: false
30
+ },
31
+ // 下拉框宽度
32
+ selectWidth: {
33
+ type: Number,
34
+ default: undefined
35
+ }
36
+ }
37
+ }
38
+ </script>
39
+
40
+ <style lang="scss">
41
+ .el-select-dropdown.is-multiple {
42
+ .el-select-dropdown__item {
43
+ padding-right: 20px;
44
+
45
+ // &:last-of-type {
46
+ // margin-bottom: 4px;
47
+ // text-overflow: inherit;
48
+ // }
49
+
50
+ .el-checkbox__input {
51
+ transform: translate(0px, 1px);
52
+ }
53
+
54
+ &.selected {
55
+ .shape-check-box.is-checked {
56
+ width: 100%;
57
+ background: #ECF2FE;
58
+ transform: translate(-8px, 0px);
59
+ padding: 0 8px;
60
+ }
61
+
62
+ &:has(.shape-check-box)::after {
63
+ display: none;
64
+ }
65
+
66
+ &.hover {
67
+ background-color: #fff;
68
+ }
69
+ }
70
+
71
+ &.is-disabled {
72
+ .shape-check-box {
73
+ cursor: not-allowed;
74
+ color: #c0c4cc;
75
+ }
76
+ .el-checkbox__inner {
77
+ background-color: #f5f7fa;
78
+ border-color: #d1dbe5;
79
+ }
80
+ }
81
+
82
+ &.hover {
83
+ background-color: #fff;
84
+ }
85
+ }
86
+ }
87
+
88
+ .el-select-dropdown {
89
+ .el-select-dropdown__item {
90
+ &:not(:last-of-type) {
91
+ margin-bottom: 2px;
92
+ }
93
+ }
94
+
95
+ .shape-check-box {
96
+ display: flex;
97
+ align-items: center;
98
+ height: 34px;
99
+
100
+ .el-checkbox__label {
101
+ // width: 100%;
102
+ flex: 1;
103
+ display: inline-block;
104
+ overflow: hidden;
105
+ text-overflow: ellipsis;
106
+ white-space: nowrap;
107
+ }
108
+ }
109
+ }
110
+ </style>
@@ -0,0 +1,174 @@
1
+ <template>
2
+ <div :class="!isView ? 'w-full' : 'inline'">
3
+ <ImOneLineDot v-if="isView" :text="plantText || '-'"></ImOneLineDot>
4
+ <el-select
5
+ v-else
6
+ ref="select"
7
+ v-model="myValue"
8
+ class="w-full"
9
+ :placeholder="placeholder"
10
+ :multiple="multiple"
11
+ :filterable="filterable"
12
+ :allow-create="allowCreate"
13
+ reserve-keyword
14
+ clearable
15
+ remote
16
+ :remote-method="remoteMethod"
17
+ :loading="loading"
18
+ popper-class="select-class"
19
+ :collapse-tags="collapseTags"
20
+ v-bind="$attrs"
21
+ @change="onChange"
22
+ @visible-change="visibleChange"
23
+ >
24
+ <el-option v-for="item in options" :key="item[prop.value]" :label="item.name || item.employeeNo" :value="item[prop.value]">
25
+ <slot name="item" :item="item">
26
+ <SelectShape :multiple="multiple" :checked="Array.isArray(myValue) && myValue.includes(item[prop.value])">
27
+ <span>{{ item.name || item.employeeNo }}<span v-if="item.orgName">({{ item.orgName }})</span></span>
28
+ </SelectShape>
29
+ </slot>
30
+ </el-option>
31
+ </el-select>
32
+ </div>
33
+ </template>
34
+
35
+ <script>
36
+ import { employeeSelectOption } from '../../api/user'
37
+ import { getLabelByValue } from '../../utils/index'
38
+ import SelectShape from './SelectShape.vue'
39
+ export default {
40
+ name: 'CommonEmployeeSelect',
41
+ components: { SelectShape },
42
+ props: {
43
+ prop: {
44
+ type: Object,
45
+ default() {
46
+ return {
47
+ value: 'objectCode'
48
+ }
49
+ }
50
+ },
51
+ value: {
52
+ type: [Number, String, Array],
53
+ default: undefined
54
+ },
55
+ placeholder: {
56
+ type: String,
57
+ default: '请选择'
58
+ },
59
+ multiple: {
60
+ type: Boolean,
61
+ default: false
62
+ },
63
+ filterable: {
64
+ type: Boolean,
65
+ default: true
66
+ },
67
+ allowCreate: {
68
+ type: Boolean,
69
+ default: false
70
+ },
71
+ collapseTags: {
72
+ type: Boolean,
73
+ default: false
74
+ },
75
+ extra: {
76
+ type: Object,
77
+ default() {
78
+ return {}
79
+ }
80
+ },
81
+ // 是否仅展示文字
82
+ isView: {
83
+ type: Boolean,
84
+ default: false
85
+ }
86
+ },
87
+ data() {
88
+ return {
89
+ myValue: undefined,
90
+ loading: true,
91
+ selectName: undefined,
92
+ options: []
93
+ }
94
+ },
95
+ computed: {
96
+ plantText() {
97
+ const list = []
98
+ const vals = [].concat(this.myValue) // 单选的时候也转成数组
99
+ vals.map(i => {
100
+ list.push(getLabelByValue(this.options, i, { labelKey: 'name', valueKey: 'objectCode' }))
101
+ return i
102
+ })
103
+ return list.join(',')
104
+ }
105
+ },
106
+ watch: {
107
+ value: {
108
+ immediate: true,
109
+ handler() {
110
+ if (JSON.stringify(this.value) !== JSON.stringify(this.myValue)) {
111
+ this.getOptions()
112
+ }
113
+ this.setMyValue()
114
+ }
115
+ }
116
+ },
117
+ created() {
118
+ this.getOptions()
119
+ },
120
+ methods: {
121
+ maskPhoneNumber(phoneNumber) {
122
+ // 假设手机号格式为11位数字
123
+ const maskedNumber = phoneNumber.substring(0, 3) + '****' + phoneNumber.substring(7)
124
+ return maskedNumber
125
+ },
126
+
127
+ setMyValue() {
128
+ try {
129
+ this.myValue = JSON.parse(JSON.stringify(this.value))
130
+ } catch (error) {
131
+ this.myValue = undefined
132
+ }
133
+ },
134
+
135
+ remoteMethod(query) {
136
+ this.loading = true
137
+ this.selectName = query
138
+ this.getOptions()
139
+ },
140
+
141
+ visibleChange(val) {
142
+ if (!val) {
143
+ this.selectName = ''
144
+ this.getOptions()
145
+ }
146
+ },
147
+ getOptions() {
148
+ const params = {
149
+ name: this.selectName,
150
+ ...this.extra
151
+ }
152
+ if (!this.selectName && this.value) {
153
+ params.objectCodeList = Array.isArray(this.value) ? this.value : [this.value]
154
+ params.selectStatus = !this.value || !this.value.length ? '0' : '1'
155
+ }
156
+ this.loading = true
157
+ employeeSelectOption(params).then((res) => {
158
+ if (res.code === 200) {
159
+ this.options = res.data || []
160
+ } else {
161
+ this.$message.error(res.message)
162
+ }
163
+ this.loading = false
164
+ })
165
+ },
166
+
167
+ onChange() {
168
+ console.log('onChange', this.myValue, this.plantText)
169
+ this.$emit('input', this.myValue, this.plantText)
170
+ this.$emit('change', this.myValue, this.plantText)
171
+ }
172
+ }
173
+ }
174
+ </script>
@@ -0,0 +1,63 @@
1
+ <template>
2
+ <span class="enum-show">{{ enumLabel }}</span>
3
+ </template>
4
+ <script>
5
+ export default {
6
+ name: 'CommonEnumShow',
7
+ props: {
8
+ enumKey: {
9
+ required: true,
10
+ type: String,
11
+ default: ''
12
+ },
13
+ enumValue: {
14
+ required: true,
15
+ type: [String, Array, Number],
16
+ default: ''
17
+ }
18
+ },
19
+ computed: {
20
+ enums() {
21
+ return this.$bizui.enums
22
+ },
23
+ enumLabel() {
24
+ if (!this.enumValue) {
25
+ return '-'
26
+ }
27
+ if (Array.isArray(this.enumValue)) {
28
+ const lables = []
29
+ for (let i = 0; i < this.enumValue.length; i++) {
30
+ const value = this.enumValue[i]
31
+ const label = this.getLabelByValue(this.enumKey, value)
32
+ lables.push(label)
33
+ }
34
+ return lables.join(',') || '-'
35
+ } else {
36
+ return this.getLabelByValue(this.enumKey, this.enumValue)
37
+ }
38
+ }
39
+ },
40
+ methods: {
41
+ getLabelByValue(key, value) {
42
+ const item = this.getOptionsItemByValue(key, value)
43
+ return item.label || '-'
44
+ },
45
+
46
+ getOptionsItemByValue(optionKey, value) {
47
+ const enums = this.enums
48
+ let result = {}
49
+ const options = enums[optionKey] || []
50
+ if (!options || options.length === 0) {
51
+ return result
52
+ }
53
+ for (let i = 0; i < options.length; i++) {
54
+ const item = options[i]
55
+ if (item.value === value + '') {
56
+ result = item
57
+ }
58
+ }
59
+ return result
60
+ }
61
+ }
62
+ }
63
+ </script>
@@ -41,3 +41,20 @@ export function getLabelByValue(options = [], value, opt = {}) {
41
41
 
42
42
  return (el && el[opt.labelKey || 'label']) || ''
43
43
  }
44
+
45
+ // tree 转成数组
46
+ export function tree2Array(tree, opt = {}) {
47
+ if (!Array.isArray(tree) || !tree.length) {
48
+ return []
49
+ }
50
+
51
+ const res = []
52
+ tree.forEach(v => {
53
+ res.push(v)
54
+ if (v[opt.key || 'children']) {
55
+ res.push(...tree2Array(v[opt.key || 'children'], opt))
56
+ }
57
+ })
58
+
59
+ return res
60
+ }