widget.qw 1.0.83 → 1.0.85
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/.env.development +10 -10
- package/.env.production +8 -8
- package/README.md +75 -75
- package/build/style.css +521 -471
- package/build/widget.qw.es.js +253 -84
- package/build/widget.qw.umd.js +252 -83
- package/index.html +17 -17
- package/package.json +47 -47
- package/src/App.vue +26 -26
- package/src/api/index.js +173 -165
- package/src/components/AuditBar.vue +396 -396
- package/src/components/BillCard.vue +164 -164
- package/src/components/CascaderPicker.vue +132 -132
- package/src/components/CascaderPop.vue +375 -371
- package/src/components/CheckGroup.vue +113 -113
- package/src/components/DatetimeDropdown.vue +62 -0
- package/src/components/{DatetimePicker/index.vue → DatetimePicker.vue} +124 -124
- package/src/components/DayDropdown.vue +56 -56
- package/src/components/FilePicker.vue +169 -169
- package/src/components/HeadBar.vue +130 -0
- package/src/components/Input.vue +80 -80
- package/src/components/MonthDropdown.vue +51 -51
- package/src/components/MultiPicker.vue +123 -123
- package/src/components/ObjsEditor.vue +334 -334
- package/src/components/SecretNotify.js +5 -5
- package/src/components/Sheet.vue +92 -92
- package/src/components/SingleApiPicker.vue +108 -108
- package/src/components/SinglePicker.vue +102 -102
- package/src/components/SingleUserSelector.vue +425 -425
- package/src/components/Switch.vue +64 -64
- package/src/components/TreePicker.vue +113 -113
- package/src/components/UserPicker.vue +106 -106
- package/src/components/UserProfile.vue +129 -129
- package/src/components/UsersPicker.vue +118 -118
- package/src/components/YearDropdown.vue +59 -59
- package/src/components/data_selector.vue +303 -303
- package/src/components/image_picker.vue +123 -123
- package/src/components/images_picker.vue +195 -195
- package/src/components/index.js +172 -162
- package/src/components/mult_list_selector.vue +155 -155
- package/src/components/subdepartment_selector.vue +481 -481
- package/src/components/user_selector.vue +639 -639
- package/src/components/widget/DataPop.vue +95 -95
- package/src/components/{DatetimePicker → widget}/DatetimePop.vue +342 -342
- package/src/components/widget/TreePop.vue +88 -88
- package/src/components/widget/UserPop.vue +347 -347
- package/src/main.js +117 -117
- package/src/router/index.ts +183 -173
- package/src/util/array_util.js +32 -32
- package/src/util/auth_util.js +72 -72
- package/src/util/bool_util.js +5 -5
- package/src/util/bus.js +1 -1
- package/src/util/cache_util.js +18 -18
- package/src/util/errer_code.js +6 -6
- package/src/util/eval_util.js +19 -19
- package/src/util/icon_util.js +36 -36
- package/src/util/image_util.js +27 -27
- package/src/util/index.js +57 -57
- package/src/util/num_util.js +70 -70
- package/src/util/obj_util.js +28 -28
- package/src/util/request.js +73 -73
- package/src/util/request_json.js +71 -71
- package/src/util/request_json_mute.js +65 -65
- package/src/util/request_upload.js +79 -79
- package/src/util/route_util.js +31 -31
- package/src/util/str_util.js +143 -143
- package/src/util/time_util.js +406 -406
- package/src/util/toast_util.js +24 -24
- package/src/util/tree_util.js +153 -153
- package/src/util/uuid_util.js +9 -9
- package/src/util/validate.js +182 -182
- package/src/util/vue_filter.js +223 -223
- package/src/views/auditbar/index.vue +78 -78
- package/src/views/billcard/index.vue +48 -48
- package/src/views/cascaderpicker/index.vue +0 -0
- package/src/views/cascaderpicker/userpicker.vue +183 -0
- package/src/views/cascaderpop/index.vue +90 -90
- package/src/views/checkgroup/index.vue +35 -35
- package/src/views/dataSelector/index.vue +48 -48
- package/src/views/datetimedropdown/index.vue +34 -0
- package/src/views/datetimepicker/index.vue +34 -34
- package/src/views/daydropdown/index.vue +31 -31
- package/src/views/filepicker/index.vue +32 -32
- package/src/views/headbar/index.vue +28 -0
- package/src/views/home/index.vue +0 -0
- package/src/views/imagepicker/index.vue +31 -31
- package/src/views/imagespicker/index.vue +37 -37
- package/src/views/input/index.vue +35 -35
- package/src/views/monthdropdown/index.vue +31 -31
- package/src/views/multListSelector/index.vue +61 -61
- package/src/views/multipicker/index.vue +36 -36
- package/src/views/objseditor/index.vue +277 -277
- package/src/views/productSelector/index.vue +35 -35
- package/src/views/projectpicker/index.vue +41 -41
- package/src/views/secretnotify/index.vue +27 -27
- package/src/views/sheet/index.vue +45 -45
- package/src/views/singlepicker/index.vue +35 -35
- package/src/views/subdepartmentSelector/index.vue +40 -40
- package/src/views/switch/index.vue +34 -34
- package/src/views/treepicker/index.vue +41 -41
- package/src/views/userSelector/index.vue +54 -54
- package/src/views/userSelectorNew/index.vue +45 -45
- package/src/views/userpicker/index.vue +43 -43
- package/src/views/userprofile/index.vue +30 -30
- package/src/views/userspicker/index.vue +43 -43
- package/src/views/yeardropdown/index.vue +32 -32
- package/src/vm/index.js +1 -1
- package/vite.config.ts +122 -122
|
@@ -1,481 +1,481 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="department-selector">
|
|
3
|
-
<van-field v-if="!isGone" :model-value="selectedDepartmentPath" readonly clickable :label="props.label"
|
|
4
|
-
:placeholder="props.placeholder" @click="onShow" :right-icon="isReadonly ? '' : 'arrow'"
|
|
5
|
-
:required="isRequired" :rules="props.rules" type="textarea" autosize="true" rows="1"/>
|
|
6
|
-
|
|
7
|
-
<van-popup v-model:show="showPicker" position="bottom" class="department-popup">
|
|
8
|
-
<div class="popup-container">
|
|
9
|
-
<div class="picker-header">
|
|
10
|
-
<van-button v-if="currentLevel > 0" @click="goBack" icon="arrow-left" size="small"
|
|
11
|
-
class="back-button" />
|
|
12
|
-
<div class="current-path">{{ currentPath }}</div>
|
|
13
|
-
<div class="action-buttons">
|
|
14
|
-
<van-button size="small" @click="handleCancel" class="cancel-btn">
|
|
15
|
-
取消
|
|
16
|
-
</van-button>
|
|
17
|
-
<van-button type="primary" size="small" @click="confirmSelection"
|
|
18
|
-
:disabled="!tempSelectedDepartment" class="confirm-btn">
|
|
19
|
-
确定
|
|
20
|
-
</van-button>
|
|
21
|
-
</div>
|
|
22
|
-
</div>
|
|
23
|
-
|
|
24
|
-
<van-field left-icon="search" v-model.trim="searchKeyword" clearable placeholder="请输入部门名称搜索"
|
|
25
|
-
class="search-field" />
|
|
26
|
-
<div class="scroll-container">
|
|
27
|
-
<van-cell-group>
|
|
28
|
-
<van-cell v-for="(item, index) in filteredDepartments" :key="index" clickable
|
|
29
|
-
@click="handleCellClick(item)">
|
|
30
|
-
<template #title>
|
|
31
|
-
<div class="cell-content">
|
|
32
|
-
<div class="circle-selector"
|
|
33
|
-
:class="{ 'selected': tempSelectedDepartment?.id === item.id }"
|
|
34
|
-
@click.stop="toggleSelection(item)">
|
|
35
|
-
<van-icon v-if="tempSelectedDepartment?.id === item.id" name="success" />
|
|
36
|
-
</div>
|
|
37
|
-
<span class="name">{{ item.fullPath || item.name }}</span>
|
|
38
|
-
</div>
|
|
39
|
-
</template>
|
|
40
|
-
<template #right-icon>
|
|
41
|
-
<van-icon v-if="item.subDepartments?.length" name="arrow" />
|
|
42
|
-
</template>
|
|
43
|
-
</van-cell>
|
|
44
|
-
</van-cell-group>
|
|
45
|
-
</div>
|
|
46
|
-
</div>
|
|
47
|
-
</van-popup>
|
|
48
|
-
</div>
|
|
49
|
-
</template>
|
|
50
|
-
|
|
51
|
-
<script setup>
|
|
52
|
-
import { ref, computed, onMounted, watch } from 'vue'
|
|
53
|
-
import util from '../util'
|
|
54
|
-
|
|
55
|
-
const props = defineProps({
|
|
56
|
-
modelValue: [String, Number],
|
|
57
|
-
label: {
|
|
58
|
-
type: String,
|
|
59
|
-
default: '选择部门'
|
|
60
|
-
},
|
|
61
|
-
placeholder: {
|
|
62
|
-
type: String,
|
|
63
|
-
default: '请选择部门'
|
|
64
|
-
},
|
|
65
|
-
required: {
|
|
66
|
-
type: Boolean,
|
|
67
|
-
default: true
|
|
68
|
-
},
|
|
69
|
-
readonly: {
|
|
70
|
-
type: Boolean,
|
|
71
|
-
default: false
|
|
72
|
-
},
|
|
73
|
-
rules: {
|
|
74
|
-
type: Array,
|
|
75
|
-
default: () => []
|
|
76
|
-
},
|
|
77
|
-
auth: {
|
|
78
|
-
type: String,
|
|
79
|
-
default: ''
|
|
80
|
-
},
|
|
81
|
-
// 查询多少层组织机构
|
|
82
|
-
level: {
|
|
83
|
-
type: Number,
|
|
84
|
-
// 默认查所有
|
|
85
|
-
default: 999
|
|
86
|
-
},
|
|
87
|
-
// 二级部门取值范围字典id
|
|
88
|
-
// 取dictionary库.dict的配置id
|
|
89
|
-
deptRangeId: {
|
|
90
|
-
type: String,
|
|
91
|
-
default: ''
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
const emit = defineEmits(['update:modelValue', 'select'])
|
|
96
|
-
const { isRequired, isGone, isDisabled, isReadonly } = util.props2auth(props)
|
|
97
|
-
|
|
98
|
-
// 部门数据相关状态
|
|
99
|
-
const departmentTree = ref([])
|
|
100
|
-
const currentDepartments = ref([])
|
|
101
|
-
const selectedDepartmentPath = ref('')
|
|
102
|
-
const selectionPath = ref([])
|
|
103
|
-
const showPicker = ref(false)
|
|
104
|
-
const tempSelectedDepartment = ref(null)
|
|
105
|
-
|
|
106
|
-
// 在脚本部分添加搜索相关逻辑(约第60行)
|
|
107
|
-
// 添加搜索关键词响应式变量
|
|
108
|
-
const searchKeyword = ref('')
|
|
109
|
-
|
|
110
|
-
// 计算属性
|
|
111
|
-
const currentPath = computed(() => {
|
|
112
|
-
return selectionPath.value
|
|
113
|
-
.filter(d => d.id !== '1') // 过滤一级部门
|
|
114
|
-
.map(d => d.name)
|
|
115
|
-
.join(' / ') || '请选择部门'
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
const currentLevel = computed(() => selectionPath.value.length)
|
|
119
|
-
|
|
120
|
-
// 部门数据缓存
|
|
121
|
-
const departmentCache = {
|
|
122
|
-
data: ref([]),
|
|
123
|
-
loading: ref(false),
|
|
124
|
-
error: ref(null)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// 修改过滤后的部门列表计算属性
|
|
128
|
-
// 修改过滤逻辑(约第80行)
|
|
129
|
-
// 修改搜索结果的路径生成逻辑(约第89行)
|
|
130
|
-
const filteredDepartments = computed(() => {
|
|
131
|
-
if (!searchKeyword.value) return currentDepartments.value;
|
|
132
|
-
|
|
133
|
-
// 递归搜索整个部门树
|
|
134
|
-
const searchTree = (depts, keyword, path = []) => {
|
|
135
|
-
return depts.flatMap(dept => {
|
|
136
|
-
const currentPath = [...path, dept];
|
|
137
|
-
const matches = [];
|
|
138
|
-
|
|
139
|
-
if (dept.name.toLowerCase().includes(keyword.toLowerCase())) {
|
|
140
|
-
matches.push({
|
|
141
|
-
...dept,
|
|
142
|
-
// 过滤掉一级部门(假设一级部门id为1)
|
|
143
|
-
fullPath: currentPath
|
|
144
|
-
.filter(d => d.id !== '1')
|
|
145
|
-
.map(d => d.name)
|
|
146
|
-
.join(' / ')
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (dept.subDepartments?.length) {
|
|
151
|
-
matches.push(...searchTree(dept.subDepartments, keyword, currentPath));
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return matches;
|
|
155
|
-
});
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
return searchTree(departmentTree.value, searchKeyword.value);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
const onShow = () => {
|
|
162
|
-
if (isDisabled.value)
|
|
163
|
-
return
|
|
164
|
-
|
|
165
|
-
showPicker.value = true
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// 修改单元格点击处理
|
|
169
|
-
const handleCellClick = (department) => {
|
|
170
|
-
// 如果是叶子节点直接选中
|
|
171
|
-
if (!department.subDepartments?.length) {
|
|
172
|
-
toggleSelection(department)
|
|
173
|
-
return
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// 非叶子节点才展开层级
|
|
177
|
-
selectionPath.value.push(department)
|
|
178
|
-
currentDepartments.value = department.subDepartments
|
|
179
|
-
tempSelectedDepartment.value = null
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// 监听搜索关键词变化
|
|
183
|
-
watch(searchKeyword, (newVal) => {
|
|
184
|
-
if (newVal) {
|
|
185
|
-
// 可根据需要添加搜索建议逻辑
|
|
186
|
-
}
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
// 在工具函数部分新增以下方法
|
|
190
|
-
const findDepartmentById = (departments, targetId) => {
|
|
191
|
-
for (const dept of departments) {
|
|
192
|
-
if (dept.id === targetId) return dept
|
|
193
|
-
if (dept.subDepartments?.length) {
|
|
194
|
-
const found = findDepartmentById(dept.subDepartments, targetId)
|
|
195
|
-
if (found) return found
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
return null
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// 修改路径构建方法
|
|
202
|
-
const buildDepartmentPath = (department) => {
|
|
203
|
-
const path = []
|
|
204
|
-
let current = department.parent
|
|
205
|
-
while (current) {
|
|
206
|
-
if (current.id !== '1') { // 不插入一级部门
|
|
207
|
-
path.unshift(current.name)
|
|
208
|
-
}
|
|
209
|
-
current = current.parent
|
|
210
|
-
}
|
|
211
|
-
// 添加当前选中部门(最后一级)
|
|
212
|
-
path.push(department.name)
|
|
213
|
-
return path.join(' / ')
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// 修改部门数据加载方法
|
|
217
|
-
const loadDepartments = async () => {
|
|
218
|
-
try {
|
|
219
|
-
if (departmentCache.data.value.length > 0) return
|
|
220
|
-
|
|
221
|
-
departmentCache.loading.value = true
|
|
222
|
-
const response = await util.get_subdepartments({ level: props.level })
|
|
223
|
-
console.log('-------subdepartment---------', response)
|
|
224
|
-
if (response.code === 200) {
|
|
225
|
-
// 添加parent引用
|
|
226
|
-
const addParentRef = (depts, parent) => {
|
|
227
|
-
depts.forEach(d => {
|
|
228
|
-
d.parent = parent
|
|
229
|
-
if (d.subDepartments) addParentRef(d.subDepartments, d)
|
|
230
|
-
})
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// 取配置的二级部门取值范围
|
|
234
|
-
let level2DeptIds = []
|
|
235
|
-
//只显示指定范围内的二级部门
|
|
236
|
-
if (props.deptRangeId) {
|
|
237
|
-
let res = await util.dictionary_dict({ id: props.deptRangeId })
|
|
238
|
-
if (res.data.parseValue && res.data.parseValue.length > 0) {
|
|
239
|
-
level2DeptIds = res.data.parseValue
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// 只保留指定二级部门列表
|
|
244
|
-
if (props.deptRangeId) {
|
|
245
|
-
if (level2DeptIds && level2DeptIds.length > 0) {
|
|
246
|
-
response.data.subDepartments = response.data.subDepartments.filter(item => {
|
|
247
|
-
return level2DeptIds.includes(item.id)
|
|
248
|
-
})
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
departmentCache.data.value = [response.data]
|
|
253
|
-
addParentRef(departmentCache.data.value, null)
|
|
254
|
-
departmentTree.value = departmentCache.data.value
|
|
255
|
-
currentDepartments.value = response.data.subDepartments || []
|
|
256
|
-
// 存储到sessionStorage中
|
|
257
|
-
// sessionStorage.setItem('departmentTree', JSON.stringify(departmentTree.value))
|
|
258
|
-
// sessionStorage.setItem('currentDepartments', JSON.stringify(currentDepartments.value))
|
|
259
|
-
}
|
|
260
|
-
} catch (error) {
|
|
261
|
-
console.error('加载部门数据失败:', error)
|
|
262
|
-
} finally {
|
|
263
|
-
departmentCache.loading.value = false
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// const handleCellClick = (department) => {
|
|
268
|
-
// if (department.subDepartments?.length) {
|
|
269
|
-
// selectionPath.value.push(department)
|
|
270
|
-
// currentDepartments.value = department.subDepartments
|
|
271
|
-
// tempSelectedDepartment.value = null
|
|
272
|
-
// } else {
|
|
273
|
-
// toggleSelection(department)
|
|
274
|
-
// }
|
|
275
|
-
// }
|
|
276
|
-
|
|
277
|
-
const toggleSelection = (department) => {
|
|
278
|
-
tempSelectedDepartment.value =
|
|
279
|
-
tempSelectedDepartment.value?.id === department.id
|
|
280
|
-
? null
|
|
281
|
-
: department
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const confirmSelection = () => {
|
|
285
|
-
if (!tempSelectedDepartment.value) return
|
|
286
|
-
|
|
287
|
-
const selectedPath = [...selectionPath.value, tempSelectedDepartment.value]
|
|
288
|
-
selectedDepartmentPath.value = selectedPath.map(d => d.name).join(' / ')
|
|
289
|
-
emit('update:modelValue', tempSelectedDepartment.value.id)
|
|
290
|
-
emit('select', tempSelectedDepartment.value)
|
|
291
|
-
showPicker.value = false
|
|
292
|
-
resetSelectionState()
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const handleCancel = () => {
|
|
296
|
-
// emit('update:modelValue', null)
|
|
297
|
-
// selectedDepartmentPath.value = ''
|
|
298
|
-
showPicker.value = false
|
|
299
|
-
// resetSelectionState()
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const resetSelectionState = () => {
|
|
303
|
-
selectionPath.value = []
|
|
304
|
-
currentDepartments.value = departmentTree.value[0]?.subDepartments || []
|
|
305
|
-
tempSelectedDepartment.value = null
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const goBack = () => {
|
|
309
|
-
selectionPath.value.pop()
|
|
310
|
-
currentDepartments.value = selectionPath.value.length
|
|
311
|
-
? selectionPath.value[selectionPath.value.length - 1].subDepartments
|
|
312
|
-
: departmentTree.value[0]?.subDepartments || []
|
|
313
|
-
tempSelectedDepartment.value = null
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
watch(showPicker, (newVal) => {
|
|
317
|
-
if (!newVal) {
|
|
318
|
-
resetSelectionState()
|
|
319
|
-
}
|
|
320
|
-
})
|
|
321
|
-
|
|
322
|
-
onMounted(async () => {
|
|
323
|
-
if (departmentCache.data.value.length === 0) {
|
|
324
|
-
await loadDepartments()
|
|
325
|
-
} else {
|
|
326
|
-
departmentTree.value = departmentCache.data.value
|
|
327
|
-
currentDepartments.value = departmentCache.data.value[0]?.subDepartments || []
|
|
328
|
-
}
|
|
329
|
-
})
|
|
330
|
-
|
|
331
|
-
// 修改后的watch监听(补充路径空值处理)
|
|
332
|
-
watch(() => props.modelValue, async (newVal) => {
|
|
333
|
-
if (newVal) {
|
|
334
|
-
await loadDepartments()
|
|
335
|
-
const targetDept = findDepartmentById(departmentCache.data.value, newVal)
|
|
336
|
-
if (targetDept) {
|
|
337
|
-
// 当部门为一级时显示空路径
|
|
338
|
-
selectedDepartmentPath.value = targetDept.parent
|
|
339
|
-
? buildDepartmentPath(targetDept)
|
|
340
|
-
: ''
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}, { immediate: true })
|
|
344
|
-
|
|
345
|
-
// 方法定义
|
|
346
|
-
// 在watch部分新增以下代码
|
|
347
|
-
watch(() => props.modelValue, async (newVal) => {
|
|
348
|
-
if (newVal) {
|
|
349
|
-
await loadDepartments()
|
|
350
|
-
const targetDept = findDepartmentById(departmentCache.data.value, newVal)
|
|
351
|
-
if (targetDept) {
|
|
352
|
-
selectedDepartmentPath.value = buildDepartmentPath(targetDept)
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}, { immediate: true })
|
|
356
|
-
</script>
|
|
357
|
-
|
|
358
|
-
<style scoped>
|
|
359
|
-
.department-popup {
|
|
360
|
-
width: 100%;
|
|
361
|
-
height: 60vh;
|
|
362
|
-
border-radius: 16px 16px 0 0;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
.popup-container {
|
|
366
|
-
display: flex;
|
|
367
|
-
flex-direction: column;
|
|
368
|
-
flex: 1;
|
|
369
|
-
height: 100%;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
.picker-header {
|
|
373
|
-
display: flex;
|
|
374
|
-
align-items: center;
|
|
375
|
-
padding: 12px;
|
|
376
|
-
border-bottom: 1px solid #ebedf0;
|
|
377
|
-
gap: 8px;
|
|
378
|
-
background: #fff;
|
|
379
|
-
/* flex-shrink: 0; */
|
|
380
|
-
position: sticky;
|
|
381
|
-
top: 0;
|
|
382
|
-
z-index: 999;
|
|
383
|
-
height: 56px;
|
|
384
|
-
/* 固定头部高度 */
|
|
385
|
-
flex-shrink: 0;
|
|
386
|
-
/* 防止头部被压缩 */
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
.scroll-container {
|
|
390
|
-
flex: 1;
|
|
391
|
-
height: 0;
|
|
392
|
-
overflow-y: auto;
|
|
393
|
-
-webkit-overflow-scrolling: touch;
|
|
394
|
-
/* height: calc(80vh - 170px); */
|
|
395
|
-
/* 计算方式:总高度(80vh) - 头部高度(56px) - 搜索框高度(52px) */
|
|
396
|
-
position: relative;
|
|
397
|
-
top: -1px;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
.back-button {
|
|
401
|
-
margin-right: 8px;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
.current-path {
|
|
405
|
-
flex: 1;
|
|
406
|
-
font-size: 14px;
|
|
407
|
-
color: #323233;
|
|
408
|
-
white-space: nowrap;
|
|
409
|
-
overflow: hidden;
|
|
410
|
-
text-overflow: ellipsis;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
.action-buttons {
|
|
414
|
-
display: flex;
|
|
415
|
-
gap: 8px;
|
|
416
|
-
margin-left: auto;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
/* .scroll-container {
|
|
420
|
-
flex: 1;
|
|
421
|
-
overflow-y: auto;
|
|
422
|
-
-webkit-overflow-scrolling: touch;
|
|
423
|
-
} */
|
|
424
|
-
|
|
425
|
-
.cell-content {
|
|
426
|
-
display: flex;
|
|
427
|
-
align-items: center;
|
|
428
|
-
gap: 12px;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
.circle-selector {
|
|
432
|
-
width: 20px;
|
|
433
|
-
height: 20px;
|
|
434
|
-
border: 1px solid #ebedf0;
|
|
435
|
-
border-radius: 50%;
|
|
436
|
-
display: flex;
|
|
437
|
-
align-items: center;
|
|
438
|
-
justify-content: center;
|
|
439
|
-
transition: all 0.2s;
|
|
440
|
-
flex-shrink: 0;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
.circle-selector.selected {
|
|
444
|
-
background-color: #1989fa;
|
|
445
|
-
border-color: #1989fa;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
.circle-selector.selected .van-icon {
|
|
449
|
-
color: white;
|
|
450
|
-
font-size: 14px;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
.van-cell__right-icon {
|
|
454
|
-
color: #969799;
|
|
455
|
-
font-size: 16px;
|
|
456
|
-
margin-left: 8px;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
.name {
|
|
460
|
-
flex: 1;
|
|
461
|
-
overflow: hidden;
|
|
462
|
-
text-overflow: ellipsis;
|
|
463
|
-
white-space: nowrap;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
.cancel-btn {
|
|
467
|
-
color: #969799;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
.search-field {
|
|
471
|
-
background: #fff;
|
|
472
|
-
padding: 10px 16px;
|
|
473
|
-
border-bottom: 1px solid #ebedf0;
|
|
474
|
-
position: sticky;
|
|
475
|
-
top: 0;
|
|
476
|
-
z-index: 999;
|
|
477
|
-
margin-bottom: 10px;
|
|
478
|
-
flex-shrink: 0;
|
|
479
|
-
/* 防止压缩 */
|
|
480
|
-
}
|
|
481
|
-
</style>
|
|
1
|
+
<template>
|
|
2
|
+
<div class="department-selector">
|
|
3
|
+
<van-field v-if="!isGone" :model-value="selectedDepartmentPath" readonly clickable :label="props.label"
|
|
4
|
+
:placeholder="props.placeholder" @click="onShow" :right-icon="isReadonly ? '' : 'arrow'"
|
|
5
|
+
:required="isRequired" :rules="props.rules" type="textarea" autosize="true" rows="1"/>
|
|
6
|
+
|
|
7
|
+
<van-popup v-model:show="showPicker" position="bottom" class="department-popup">
|
|
8
|
+
<div class="popup-container">
|
|
9
|
+
<div class="picker-header">
|
|
10
|
+
<van-button v-if="currentLevel > 0" @click="goBack" icon="arrow-left" size="small"
|
|
11
|
+
class="back-button" />
|
|
12
|
+
<div class="current-path">{{ currentPath }}</div>
|
|
13
|
+
<div class="action-buttons">
|
|
14
|
+
<van-button size="small" @click="handleCancel" class="cancel-btn">
|
|
15
|
+
取消
|
|
16
|
+
</van-button>
|
|
17
|
+
<van-button type="primary" size="small" @click="confirmSelection"
|
|
18
|
+
:disabled="!tempSelectedDepartment" class="confirm-btn">
|
|
19
|
+
确定
|
|
20
|
+
</van-button>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<van-field left-icon="search" v-model.trim="searchKeyword" clearable placeholder="请输入部门名称搜索"
|
|
25
|
+
class="search-field" />
|
|
26
|
+
<div class="scroll-container">
|
|
27
|
+
<van-cell-group>
|
|
28
|
+
<van-cell v-for="(item, index) in filteredDepartments" :key="index" clickable
|
|
29
|
+
@click="handleCellClick(item)">
|
|
30
|
+
<template #title>
|
|
31
|
+
<div class="cell-content">
|
|
32
|
+
<div class="circle-selector"
|
|
33
|
+
:class="{ 'selected': tempSelectedDepartment?.id === item.id }"
|
|
34
|
+
@click.stop="toggleSelection(item)">
|
|
35
|
+
<van-icon v-if="tempSelectedDepartment?.id === item.id" name="success" />
|
|
36
|
+
</div>
|
|
37
|
+
<span class="name">{{ item.fullPath || item.name }}</span>
|
|
38
|
+
</div>
|
|
39
|
+
</template>
|
|
40
|
+
<template #right-icon>
|
|
41
|
+
<van-icon v-if="item.subDepartments?.length" name="arrow" />
|
|
42
|
+
</template>
|
|
43
|
+
</van-cell>
|
|
44
|
+
</van-cell-group>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</van-popup>
|
|
48
|
+
</div>
|
|
49
|
+
</template>
|
|
50
|
+
|
|
51
|
+
<script setup>
|
|
52
|
+
import { ref, computed, onMounted, watch } from 'vue'
|
|
53
|
+
import util from '../util'
|
|
54
|
+
|
|
55
|
+
const props = defineProps({
|
|
56
|
+
modelValue: [String, Number],
|
|
57
|
+
label: {
|
|
58
|
+
type: String,
|
|
59
|
+
default: '选择部门'
|
|
60
|
+
},
|
|
61
|
+
placeholder: {
|
|
62
|
+
type: String,
|
|
63
|
+
default: '请选择部门'
|
|
64
|
+
},
|
|
65
|
+
required: {
|
|
66
|
+
type: Boolean,
|
|
67
|
+
default: true
|
|
68
|
+
},
|
|
69
|
+
readonly: {
|
|
70
|
+
type: Boolean,
|
|
71
|
+
default: false
|
|
72
|
+
},
|
|
73
|
+
rules: {
|
|
74
|
+
type: Array,
|
|
75
|
+
default: () => []
|
|
76
|
+
},
|
|
77
|
+
auth: {
|
|
78
|
+
type: String,
|
|
79
|
+
default: ''
|
|
80
|
+
},
|
|
81
|
+
// 查询多少层组织机构
|
|
82
|
+
level: {
|
|
83
|
+
type: Number,
|
|
84
|
+
// 默认查所有
|
|
85
|
+
default: 999
|
|
86
|
+
},
|
|
87
|
+
// 二级部门取值范围字典id
|
|
88
|
+
// 取dictionary库.dict的配置id
|
|
89
|
+
deptRangeId: {
|
|
90
|
+
type: String,
|
|
91
|
+
default: ''
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const emit = defineEmits(['update:modelValue', 'select'])
|
|
96
|
+
const { isRequired, isGone, isDisabled, isReadonly } = util.props2auth(props)
|
|
97
|
+
|
|
98
|
+
// 部门数据相关状态
|
|
99
|
+
const departmentTree = ref([])
|
|
100
|
+
const currentDepartments = ref([])
|
|
101
|
+
const selectedDepartmentPath = ref('')
|
|
102
|
+
const selectionPath = ref([])
|
|
103
|
+
const showPicker = ref(false)
|
|
104
|
+
const tempSelectedDepartment = ref(null)
|
|
105
|
+
|
|
106
|
+
// 在脚本部分添加搜索相关逻辑(约第60行)
|
|
107
|
+
// 添加搜索关键词响应式变量
|
|
108
|
+
const searchKeyword = ref('')
|
|
109
|
+
|
|
110
|
+
// 计算属性
|
|
111
|
+
const currentPath = computed(() => {
|
|
112
|
+
return selectionPath.value
|
|
113
|
+
.filter(d => d.id !== '1') // 过滤一级部门
|
|
114
|
+
.map(d => d.name)
|
|
115
|
+
.join(' / ') || '请选择部门'
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
const currentLevel = computed(() => selectionPath.value.length)
|
|
119
|
+
|
|
120
|
+
// 部门数据缓存
|
|
121
|
+
const departmentCache = {
|
|
122
|
+
data: ref([]),
|
|
123
|
+
loading: ref(false),
|
|
124
|
+
error: ref(null)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 修改过滤后的部门列表计算属性
|
|
128
|
+
// 修改过滤逻辑(约第80行)
|
|
129
|
+
// 修改搜索结果的路径生成逻辑(约第89行)
|
|
130
|
+
const filteredDepartments = computed(() => {
|
|
131
|
+
if (!searchKeyword.value) return currentDepartments.value;
|
|
132
|
+
|
|
133
|
+
// 递归搜索整个部门树
|
|
134
|
+
const searchTree = (depts, keyword, path = []) => {
|
|
135
|
+
return depts.flatMap(dept => {
|
|
136
|
+
const currentPath = [...path, dept];
|
|
137
|
+
const matches = [];
|
|
138
|
+
|
|
139
|
+
if (dept.name.toLowerCase().includes(keyword.toLowerCase())) {
|
|
140
|
+
matches.push({
|
|
141
|
+
...dept,
|
|
142
|
+
// 过滤掉一级部门(假设一级部门id为1)
|
|
143
|
+
fullPath: currentPath
|
|
144
|
+
.filter(d => d.id !== '1')
|
|
145
|
+
.map(d => d.name)
|
|
146
|
+
.join(' / ')
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (dept.subDepartments?.length) {
|
|
151
|
+
matches.push(...searchTree(dept.subDepartments, keyword, currentPath));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return matches;
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return searchTree(departmentTree.value, searchKeyword.value);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const onShow = () => {
|
|
162
|
+
if (isDisabled.value)
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
showPicker.value = true
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 修改单元格点击处理
|
|
169
|
+
const handleCellClick = (department) => {
|
|
170
|
+
// 如果是叶子节点直接选中
|
|
171
|
+
if (!department.subDepartments?.length) {
|
|
172
|
+
toggleSelection(department)
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// 非叶子节点才展开层级
|
|
177
|
+
selectionPath.value.push(department)
|
|
178
|
+
currentDepartments.value = department.subDepartments
|
|
179
|
+
tempSelectedDepartment.value = null
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// 监听搜索关键词变化
|
|
183
|
+
watch(searchKeyword, (newVal) => {
|
|
184
|
+
if (newVal) {
|
|
185
|
+
// 可根据需要添加搜索建议逻辑
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
// 在工具函数部分新增以下方法
|
|
190
|
+
const findDepartmentById = (departments, targetId) => {
|
|
191
|
+
for (const dept of departments) {
|
|
192
|
+
if (dept.id === targetId) return dept
|
|
193
|
+
if (dept.subDepartments?.length) {
|
|
194
|
+
const found = findDepartmentById(dept.subDepartments, targetId)
|
|
195
|
+
if (found) return found
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return null
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 修改路径构建方法
|
|
202
|
+
const buildDepartmentPath = (department) => {
|
|
203
|
+
const path = []
|
|
204
|
+
let current = department.parent
|
|
205
|
+
while (current) {
|
|
206
|
+
if (current.id !== '1') { // 不插入一级部门
|
|
207
|
+
path.unshift(current.name)
|
|
208
|
+
}
|
|
209
|
+
current = current.parent
|
|
210
|
+
}
|
|
211
|
+
// 添加当前选中部门(最后一级)
|
|
212
|
+
path.push(department.name)
|
|
213
|
+
return path.join(' / ')
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 修改部门数据加载方法
|
|
217
|
+
const loadDepartments = async () => {
|
|
218
|
+
try {
|
|
219
|
+
if (departmentCache.data.value.length > 0) return
|
|
220
|
+
|
|
221
|
+
departmentCache.loading.value = true
|
|
222
|
+
const response = await util.get_subdepartments({ level: props.level })
|
|
223
|
+
console.log('-------subdepartment---------', response)
|
|
224
|
+
if (response.code === 200) {
|
|
225
|
+
// 添加parent引用
|
|
226
|
+
const addParentRef = (depts, parent) => {
|
|
227
|
+
depts.forEach(d => {
|
|
228
|
+
d.parent = parent
|
|
229
|
+
if (d.subDepartments) addParentRef(d.subDepartments, d)
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// 取配置的二级部门取值范围
|
|
234
|
+
let level2DeptIds = []
|
|
235
|
+
//只显示指定范围内的二级部门
|
|
236
|
+
if (props.deptRangeId) {
|
|
237
|
+
let res = await util.dictionary_dict({ id: props.deptRangeId })
|
|
238
|
+
if (res.data.parseValue && res.data.parseValue.length > 0) {
|
|
239
|
+
level2DeptIds = res.data.parseValue
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 只保留指定二级部门列表
|
|
244
|
+
if (props.deptRangeId) {
|
|
245
|
+
if (level2DeptIds && level2DeptIds.length > 0) {
|
|
246
|
+
response.data.subDepartments = response.data.subDepartments.filter(item => {
|
|
247
|
+
return level2DeptIds.includes(item.id)
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
departmentCache.data.value = [response.data]
|
|
253
|
+
addParentRef(departmentCache.data.value, null)
|
|
254
|
+
departmentTree.value = departmentCache.data.value
|
|
255
|
+
currentDepartments.value = response.data.subDepartments || []
|
|
256
|
+
// 存储到sessionStorage中
|
|
257
|
+
// sessionStorage.setItem('departmentTree', JSON.stringify(departmentTree.value))
|
|
258
|
+
// sessionStorage.setItem('currentDepartments', JSON.stringify(currentDepartments.value))
|
|
259
|
+
}
|
|
260
|
+
} catch (error) {
|
|
261
|
+
console.error('加载部门数据失败:', error)
|
|
262
|
+
} finally {
|
|
263
|
+
departmentCache.loading.value = false
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// const handleCellClick = (department) => {
|
|
268
|
+
// if (department.subDepartments?.length) {
|
|
269
|
+
// selectionPath.value.push(department)
|
|
270
|
+
// currentDepartments.value = department.subDepartments
|
|
271
|
+
// tempSelectedDepartment.value = null
|
|
272
|
+
// } else {
|
|
273
|
+
// toggleSelection(department)
|
|
274
|
+
// }
|
|
275
|
+
// }
|
|
276
|
+
|
|
277
|
+
const toggleSelection = (department) => {
|
|
278
|
+
tempSelectedDepartment.value =
|
|
279
|
+
tempSelectedDepartment.value?.id === department.id
|
|
280
|
+
? null
|
|
281
|
+
: department
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const confirmSelection = () => {
|
|
285
|
+
if (!tempSelectedDepartment.value) return
|
|
286
|
+
|
|
287
|
+
const selectedPath = [...selectionPath.value, tempSelectedDepartment.value]
|
|
288
|
+
selectedDepartmentPath.value = selectedPath.map(d => d.name).join(' / ')
|
|
289
|
+
emit('update:modelValue', tempSelectedDepartment.value.id)
|
|
290
|
+
emit('select', tempSelectedDepartment.value)
|
|
291
|
+
showPicker.value = false
|
|
292
|
+
resetSelectionState()
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const handleCancel = () => {
|
|
296
|
+
// emit('update:modelValue', null)
|
|
297
|
+
// selectedDepartmentPath.value = ''
|
|
298
|
+
showPicker.value = false
|
|
299
|
+
// resetSelectionState()
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const resetSelectionState = () => {
|
|
303
|
+
selectionPath.value = []
|
|
304
|
+
currentDepartments.value = departmentTree.value[0]?.subDepartments || []
|
|
305
|
+
tempSelectedDepartment.value = null
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const goBack = () => {
|
|
309
|
+
selectionPath.value.pop()
|
|
310
|
+
currentDepartments.value = selectionPath.value.length
|
|
311
|
+
? selectionPath.value[selectionPath.value.length - 1].subDepartments
|
|
312
|
+
: departmentTree.value[0]?.subDepartments || []
|
|
313
|
+
tempSelectedDepartment.value = null
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
watch(showPicker, (newVal) => {
|
|
317
|
+
if (!newVal) {
|
|
318
|
+
resetSelectionState()
|
|
319
|
+
}
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
onMounted(async () => {
|
|
323
|
+
if (departmentCache.data.value.length === 0) {
|
|
324
|
+
await loadDepartments()
|
|
325
|
+
} else {
|
|
326
|
+
departmentTree.value = departmentCache.data.value
|
|
327
|
+
currentDepartments.value = departmentCache.data.value[0]?.subDepartments || []
|
|
328
|
+
}
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
// 修改后的watch监听(补充路径空值处理)
|
|
332
|
+
watch(() => props.modelValue, async (newVal) => {
|
|
333
|
+
if (newVal) {
|
|
334
|
+
await loadDepartments()
|
|
335
|
+
const targetDept = findDepartmentById(departmentCache.data.value, newVal)
|
|
336
|
+
if (targetDept) {
|
|
337
|
+
// 当部门为一级时显示空路径
|
|
338
|
+
selectedDepartmentPath.value = targetDept.parent
|
|
339
|
+
? buildDepartmentPath(targetDept)
|
|
340
|
+
: ''
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}, { immediate: true })
|
|
344
|
+
|
|
345
|
+
// 方法定义
|
|
346
|
+
// 在watch部分新增以下代码
|
|
347
|
+
watch(() => props.modelValue, async (newVal) => {
|
|
348
|
+
if (newVal) {
|
|
349
|
+
await loadDepartments()
|
|
350
|
+
const targetDept = findDepartmentById(departmentCache.data.value, newVal)
|
|
351
|
+
if (targetDept) {
|
|
352
|
+
selectedDepartmentPath.value = buildDepartmentPath(targetDept)
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}, { immediate: true })
|
|
356
|
+
</script>
|
|
357
|
+
|
|
358
|
+
<style scoped>
|
|
359
|
+
.department-popup {
|
|
360
|
+
width: 100%;
|
|
361
|
+
height: 60vh;
|
|
362
|
+
border-radius: 16px 16px 0 0;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.popup-container {
|
|
366
|
+
display: flex;
|
|
367
|
+
flex-direction: column;
|
|
368
|
+
flex: 1;
|
|
369
|
+
height: 100%;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.picker-header {
|
|
373
|
+
display: flex;
|
|
374
|
+
align-items: center;
|
|
375
|
+
padding: 12px;
|
|
376
|
+
border-bottom: 1px solid #ebedf0;
|
|
377
|
+
gap: 8px;
|
|
378
|
+
background: #fff;
|
|
379
|
+
/* flex-shrink: 0; */
|
|
380
|
+
position: sticky;
|
|
381
|
+
top: 0;
|
|
382
|
+
z-index: 999;
|
|
383
|
+
height: 56px;
|
|
384
|
+
/* 固定头部高度 */
|
|
385
|
+
flex-shrink: 0;
|
|
386
|
+
/* 防止头部被压缩 */
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.scroll-container {
|
|
390
|
+
flex: 1;
|
|
391
|
+
height: 0;
|
|
392
|
+
overflow-y: auto;
|
|
393
|
+
-webkit-overflow-scrolling: touch;
|
|
394
|
+
/* height: calc(80vh - 170px); */
|
|
395
|
+
/* 计算方式:总高度(80vh) - 头部高度(56px) - 搜索框高度(52px) */
|
|
396
|
+
position: relative;
|
|
397
|
+
top: -1px;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.back-button {
|
|
401
|
+
margin-right: 8px;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.current-path {
|
|
405
|
+
flex: 1;
|
|
406
|
+
font-size: 14px;
|
|
407
|
+
color: #323233;
|
|
408
|
+
white-space: nowrap;
|
|
409
|
+
overflow: hidden;
|
|
410
|
+
text-overflow: ellipsis;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.action-buttons {
|
|
414
|
+
display: flex;
|
|
415
|
+
gap: 8px;
|
|
416
|
+
margin-left: auto;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/* .scroll-container {
|
|
420
|
+
flex: 1;
|
|
421
|
+
overflow-y: auto;
|
|
422
|
+
-webkit-overflow-scrolling: touch;
|
|
423
|
+
} */
|
|
424
|
+
|
|
425
|
+
.cell-content {
|
|
426
|
+
display: flex;
|
|
427
|
+
align-items: center;
|
|
428
|
+
gap: 12px;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.circle-selector {
|
|
432
|
+
width: 20px;
|
|
433
|
+
height: 20px;
|
|
434
|
+
border: 1px solid #ebedf0;
|
|
435
|
+
border-radius: 50%;
|
|
436
|
+
display: flex;
|
|
437
|
+
align-items: center;
|
|
438
|
+
justify-content: center;
|
|
439
|
+
transition: all 0.2s;
|
|
440
|
+
flex-shrink: 0;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.circle-selector.selected {
|
|
444
|
+
background-color: #1989fa;
|
|
445
|
+
border-color: #1989fa;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.circle-selector.selected .van-icon {
|
|
449
|
+
color: white;
|
|
450
|
+
font-size: 14px;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.van-cell__right-icon {
|
|
454
|
+
color: #969799;
|
|
455
|
+
font-size: 16px;
|
|
456
|
+
margin-left: 8px;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.name {
|
|
460
|
+
flex: 1;
|
|
461
|
+
overflow: hidden;
|
|
462
|
+
text-overflow: ellipsis;
|
|
463
|
+
white-space: nowrap;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
.cancel-btn {
|
|
467
|
+
color: #969799;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.search-field {
|
|
471
|
+
background: #fff;
|
|
472
|
+
padding: 10px 16px;
|
|
473
|
+
border-bottom: 1px solid #ebedf0;
|
|
474
|
+
position: sticky;
|
|
475
|
+
top: 0;
|
|
476
|
+
z-index: 999;
|
|
477
|
+
margin-bottom: 10px;
|
|
478
|
+
flex-shrink: 0;
|
|
479
|
+
/* 防止压缩 */
|
|
480
|
+
}
|
|
481
|
+
</style>
|