wl-design-mobile 0.8.3 → 0.8.4

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 (40) hide show
  1. package/doc/file/compoents/lambo-file-row.vue +5 -92
  2. package/doc/file-type/file-type.js +286 -0
  3. package/doc/file-type/icons/icon-file-ai.svg +1 -0
  4. package/doc/file-type/icons/icon-file-avi.svg +1 -0
  5. package/doc/file-type/icons/icon-file-default.svg +1 -0
  6. package/doc/file-type/icons/icon-file-epub.svg +1 -0
  7. package/doc/file-type/icons/icon-file-excel.svg +1 -0
  8. package/doc/file-type/icons/icon-file-flv.svg +1 -0
  9. package/doc/file-type/icons/icon-file-gif.svg +1 -0
  10. package/doc/file-type/icons/icon-file-html.svg +1 -0
  11. package/doc/file-type/icons/icon-file-java.svg +1 -0
  12. package/doc/file-type/icons/icon-file-js.svg +1 -0
  13. package/doc/file-type/icons/icon-file-jsp.svg +1 -0
  14. package/doc/file-type/icons/icon-file-mp3.svg +1 -0
  15. package/doc/file-type/icons/icon-file-msi.svg +1 -0
  16. package/doc/file-type/icons/icon-file-pdf.svg +1 -0
  17. package/doc/file-type/icons/icon-file-png.svg +1 -0
  18. package/doc/file-type/icons/icon-file-ppt.svg +1 -0
  19. package/doc/file-type/icons/icon-file-psd.svg +1 -0
  20. package/doc/file-type/icons/icon-file-rar.svg +1 -0
  21. package/doc/file-type/icons/icon-file-raw.svg +1 -0
  22. package/doc/file-type/icons/icon-file-txt.svg +1 -0
  23. package/doc/file-type/icons/icon-file-word.svg +1 -0
  24. package/flow/approval/flow-approval.vue +550 -0
  25. package/flow/comps/flow-attachment-list.vue +230 -0
  26. package/flow/comps/flow-attachment-modal.vue +132 -0
  27. package/flow/comps/flow-copy-users.vue +358 -0
  28. package/flow/comps/flow-node-card.vue +150 -0
  29. package/flow/comps/flow-node-header.vue +172 -0
  30. package/flow/comps/flow-util.js +156 -0
  31. package/flow/comps/icons/approved.svg +11 -0
  32. package/flow/comps/icons/canceled.svg +11 -0
  33. package/flow/comps/icons/rejected.svg +11 -0
  34. package/flow/comps/icons/underway.svg +11 -0
  35. package/flow/detail/flow-detail.vue +466 -0
  36. package/flow/index.js +51 -0
  37. package/flow/lambo-flow-detail.vue +894 -12
  38. package/flow/lambo-flow-edit.vue +6 -21
  39. package/flow/submit/flow-submit.vue +381 -0
  40. package/package.json +1 -1
@@ -0,0 +1,230 @@
1
+ <template>
2
+ <div class="attachment-section" :class="{ 'no-border': !showBorder }">
3
+ <div class="attachment-header" v-if="showTitle || editable">
4
+ <div class="attachment-title" v-if="showTitle">
5
+ <van-icon name="paperclip" size="14" style="margin-right: 4px"></van-icon>
6
+ 附件({{ attachments.length }})
7
+ </div>
8
+ <van-uploader
9
+ v-if="editable"
10
+ :after-read="handleAfterRead"
11
+ :accept="accept"
12
+ :show-preview="false"
13
+ :show-upload-list="false"
14
+ :disabled="uploading"
15
+ >
16
+ <div class="upload-btn" :class="{ uploading }">
17
+ <van-icon :name="uploading ? 'replay' : 'plus'" size="14"></van-icon>
18
+ {{ uploading ? '上传中' : '上传' }}
19
+ </div>
20
+ </van-uploader>
21
+ </div>
22
+ <div class="attachment-list" v-if="attachments && attachments.length > 0">
23
+ <div
24
+ v-for="(file, index) in attachments"
25
+ :key="index"
26
+ class="attachment-item"
27
+ @click="handleClick(file)"
28
+ >
29
+ <van-icon :name="getFileIcon(file.fileName || file.name)" size="14"></van-icon>
30
+ <span class="file-name">{{ file.fileName || file.name }}</span>
31
+ <span class="file-size">{{ formatFileSize(file.fileSize || file.size) }}</span>
32
+ <van-icon
33
+ v-if="editable"
34
+ name="cross"
35
+ class="remove-file"
36
+ @click.stop="handleRemove(index)"
37
+ ></van-icon>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ </template>
42
+
43
+ <script>
44
+ import { Toast } from 'vant'
45
+ import { axios } from '../../util/axios'
46
+ import { fileGetUrl, fileUploadUrl } from '../../api/doc'
47
+ import { getFileIcon, formatFileSize } from './flow-util.js'
48
+
49
+ export default {
50
+ name: 'FlowAttachmentList',
51
+ props: {
52
+ uploadEnabled: {
53
+ type: Boolean,
54
+ default: true
55
+ },
56
+ attachments: {
57
+ type: Array,
58
+ default: () => []
59
+ },
60
+ showTitle: {
61
+ type: Boolean,
62
+ default: true
63
+ },
64
+ showBorder: {
65
+ type: Boolean,
66
+ default: true
67
+ },
68
+ editable: {
69
+ type: Boolean,
70
+ default: false
71
+ },
72
+ accept: {
73
+ type: String,
74
+ default: ''
75
+ }
76
+ },
77
+ data() {
78
+ return {
79
+ uploading: false
80
+ }
81
+ },
82
+ methods: {
83
+ getFileIcon,
84
+ formatFileSize,
85
+ handleClick(file) {
86
+ this.$emit('click', file)
87
+ },
88
+ handleAfterRead(fileObj) {
89
+ this.$emit('after-read', fileObj)
90
+ if (!this.uploadEnabled) return
91
+ const file = fileObj.file
92
+ const formData = new FormData()
93
+ formData.append('file', file)
94
+ this.uploading = true
95
+ axios.post(fileUploadUrl, formData).then(res => {
96
+ if (res && res.code === 1 && res.data && res.data.length > 0 && res.data[0].fileId) {
97
+ const resFile = res.data[0]
98
+ const fileData = {
99
+ fileId: resFile.fileId,
100
+ fileName: resFile.fileName || file.name,
101
+ fileSize: resFile.fileSize || file.size,
102
+ fileUrl: resFile.fileUrl || resFile.url || resFile.downloadUrl || fileGetUrl + resFile.fileId
103
+ }
104
+ Toast.success(fileData.fileName + '上传成功')
105
+ this.$emit('upload-success', fileData)
106
+ return
107
+ }
108
+ Toast.fail('文件' + file.name + '上传失败')
109
+ this.$emit('upload-error', res)
110
+ }).catch(error => {
111
+ Toast.fail('文件' + file.name + '上传异常')
112
+ this.$emit('upload-error', error)
113
+ }).finally(() => {
114
+ this.uploading = false
115
+ })
116
+ },
117
+ handleRemove(index) {
118
+ this.$emit('remove', index)
119
+ }
120
+ }
121
+ }
122
+ </script>
123
+
124
+ <style scoped>
125
+ .attachment-section {
126
+ margin-top: 12px;
127
+ padding-top: 12px;
128
+ border-top: 1px solid #f0f0f0;
129
+ }
130
+
131
+ .attachment-section.no-border {
132
+ margin-top: 0;
133
+ padding-top: 0;
134
+ border-top: none;
135
+ }
136
+
137
+ .attachment-header {
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: space-between;
141
+ margin-bottom: 8px;
142
+ }
143
+
144
+ .attachment-title {
145
+ font-size: 13px;
146
+ font-weight: 500;
147
+ color: #595959;
148
+ display: flex;
149
+ align-items: center;
150
+ }
151
+
152
+ .attachment-list {
153
+ display: flex;
154
+ flex-wrap: wrap;
155
+ gap: 8px;
156
+ }
157
+
158
+ .attachment-item {
159
+ display: inline-flex;
160
+ align-items: center;
161
+ padding: 6px 10px;
162
+ background: #fff;
163
+ border: 1px solid #e8e8e8;
164
+ border-radius: 4px;
165
+ cursor: pointer;
166
+ transition: all 0.2s;
167
+ font-size: 12px;
168
+ gap: 6px;
169
+ color: #595959;
170
+ height: 32px;
171
+ }
172
+
173
+ .attachment-item:hover {
174
+ background: #f0f0f0;
175
+ color: #262626;
176
+ }
177
+
178
+ .attachment-item .van-icon {
179
+ color: #8c8c8c;
180
+ vertical-align: middle;
181
+ flex-shrink: 0;
182
+ }
183
+
184
+ .file-name {
185
+ max-width: 120px;
186
+ overflow: hidden;
187
+ text-overflow: ellipsis;
188
+ white-space: nowrap;
189
+ }
190
+
191
+ .file-size {
192
+ color: #bfbfbf;
193
+ flex-shrink: 0;
194
+ font-size: 11px;
195
+ }
196
+
197
+ .remove-file {
198
+ color: #bfbfbf;
199
+ margin-left: 4px;
200
+ }
201
+
202
+ .remove-file:hover {
203
+ color: #ee0a24;
204
+ }
205
+
206
+ .upload-btn {
207
+ display: inline-flex;
208
+ align-items: center;
209
+ gap: 4px;
210
+ height: 26px;
211
+ padding: 0 10px;
212
+ border: 1px dashed #c8c9cc;
213
+ border-radius: 4px;
214
+ font-size: 12px;
215
+ color: #646566;
216
+ background: #fff;
217
+ cursor: pointer;
218
+ transition: all 0.2s;
219
+ }
220
+
221
+ .upload-btn:hover {
222
+ border-color: #1989fa;
223
+ color: #1989fa;
224
+ }
225
+
226
+ .upload-btn.uploading {
227
+ color: #969799;
228
+ border-color: #dcdee0;
229
+ }
230
+ </style>
@@ -0,0 +1,132 @@
1
+ <template>
2
+ <van-popup :value="visible" position="center" round :style="{ width: modalWidth, maxWidth: '90%' }" @input="handleVisibleChange">
3
+ <div class="modal-header">
4
+ <span class="modal-title">{{ fileName }}</span>
5
+ <van-icon name="cross" class="close-icon" @click="handleClose"></van-icon>
6
+ </div>
7
+ <div class="modal-content" v-show="visible">
8
+ <div class="preview-placeholder">
9
+ <van-icon name="description" size="64" color="#ccc"></van-icon>
10
+ <p>{{ fileName }}</p>
11
+ </div>
12
+ </div>
13
+ <div class="modal-footer">
14
+ <van-button type="default" size="small" @click="handleDownload">下载</van-button>
15
+ <van-button type="primary" size="small" @click="handleClose">关闭</van-button>
16
+ </div>
17
+ </van-popup>
18
+ </template>
19
+
20
+ <script>
21
+ export default {
22
+ name: 'FlowAttachmentModal',
23
+ props: {
24
+ visible: {
25
+ type: Boolean,
26
+ default: false
27
+ },
28
+ fileUrl: {
29
+ type: String,
30
+ default: ''
31
+ },
32
+ fileName: {
33
+ type: String,
34
+ default: ''
35
+ },
36
+ modalWidth: {
37
+ type: String,
38
+ default: '320px'
39
+ }
40
+ },
41
+ computed: {
42
+ fileType() {
43
+ if (!this.fileName) return ''
44
+ return this.fileName.split('.').pop().toLowerCase()
45
+ }
46
+ },
47
+ methods: {
48
+ handleVisibleChange(val) {
49
+ this.$emit('update:visible', val)
50
+ },
51
+ handleClose() {
52
+ this.$emit('update:visible', false)
53
+ },
54
+ handleDownload() {
55
+ if (!this.fileUrl) {
56
+ this.$toast({ message: '文件地址不存在', position: 'middle' })
57
+ return
58
+ }
59
+ let downloadFileName = this.fileName
60
+ if (!downloadFileName) {
61
+ downloadFileName = this.fileUrl.split('/').pop() || 'download'
62
+ }
63
+ const link = document.createElement('a')
64
+ link.href = this.fileUrl
65
+ link.download = downloadFileName
66
+ document.body.appendChild(link)
67
+ link.click()
68
+ document.body.removeChild(link)
69
+ }
70
+ }
71
+ }
72
+ </script>
73
+
74
+ <style scoped>
75
+ .modal-header {
76
+ display: flex;
77
+ align-items: center;
78
+ justify-content: space-between;
79
+ padding: 16px;
80
+ border-bottom: 1px solid #f0f0f0;
81
+ }
82
+
83
+ .modal-title {
84
+ font-size: 16px;
85
+ font-weight: 600;
86
+ color: #323233;
87
+ flex: 1;
88
+ overflow: hidden;
89
+ text-overflow: ellipsis;
90
+ white-space: nowrap;
91
+ }
92
+
93
+ .close-icon {
94
+ font-size: 20px;
95
+ color: #c8c9cc;
96
+ cursor: pointer;
97
+ flex-shrink: 0;
98
+ margin-left: 12px;
99
+ }
100
+
101
+ .close-icon:hover {
102
+ color: #969799;
103
+ }
104
+
105
+ .modal-content {
106
+ padding: 40px 20px;
107
+ min-height: 200px;
108
+ max-height: 70vh;
109
+ overflow-y: auto;
110
+ }
111
+
112
+ .preview-placeholder {
113
+ display: flex;
114
+ flex-direction: column;
115
+ align-items: center;
116
+ justify-content: center;
117
+ color: #969799;
118
+ }
119
+
120
+ .preview-placeholder p {
121
+ margin-top: 12px;
122
+ font-size: 14px;
123
+ }
124
+
125
+ .modal-footer {
126
+ padding: 16px;
127
+ border-top: 1px solid #f0f0f0;
128
+ display: flex;
129
+ justify-content: flex-end;
130
+ gap: 12px;
131
+ }
132
+ </style>
@@ -0,0 +1,358 @@
1
+ <template>
2
+ <div class="copy-users-wrapper">
3
+ <span v-if="showLabel" class="copy-label">抄送人:</span>
4
+ <div class="copy-content">
5
+ <div v-if="editable" class="copy-tags">
6
+ <van-tag v-for="(user, index) in internalCopyUsers" :key="getUserKey(user, index)" closeable @close="removeUser(index)">
7
+ {{ getUserName(user) }}
8
+ </van-tag>
9
+ <div class="add-btn" @click="handleAddClick">
10
+ <van-icon name="plus" size="14"></van-icon>
11
+ 添加
12
+ </div>
13
+ <span v-if="internalCopyUsers.length === 0" class="empty-text">无</span>
14
+ </div>
15
+ <div v-else class="avatar-list">
16
+ <template v-for="(user, index) in displayUsers">
17
+ <van-popup
18
+ :key="'popup-' + index"
19
+ v-model="user.popupVisible"
20
+ position="top"
21
+ :style="{ padding: '12px 16px' }"
22
+ >
23
+ <div class="copy-popup-name">{{ getUserName(user) }}</div>
24
+ <div v-if="getUserDept(user)" class="copy-popup-dept">{{ getUserDept(user) }}</div>
25
+ </van-popup>
26
+ <div
27
+ :key="'avatar-' + index"
28
+ class="copy-avatar"
29
+ :style="getAvatarStyle(index)"
30
+ @click="user.popupVisible = true"
31
+ >
32
+ <img v-if="user.avatar" :src="user.avatar" alt="" />
33
+ <span v-else>{{ getUserName(user) ? getUserName(user).charAt(0) : '' }}</span>
34
+ </div>
35
+ </template>
36
+ <van-popup v-if="internalCopyUsers.length > maxDisplay" v-model="moreCopyPopupVisible" position="top" :style="{ padding: '12px 16px' }">
37
+ <div v-for="(user, index) in remainingUsers" :key="getUserKey(user, index)" class="copy-popup-name">
38
+ {{ getUserName(user) }}
39
+ </div>
40
+ </van-popup>
41
+ <div v-if="internalCopyUsers.length > maxDisplay" class="avatar-excess" @click="moreCopyPopupVisible = true">
42
+ +{{ internalCopyUsers.length - maxDisplay }}
43
+ </div>
44
+ <span v-if="internalCopyUsers.length === 0" class="empty-text">无</span>
45
+ </div>
46
+ </div>
47
+
48
+ <!-- 抄送人选择弹窗 -->
49
+ <van-popup v-model="showSelector" position="bottom" round>
50
+ <div class="user-selector">
51
+ <div class="selector-header">
52
+ <span class="selector-title">选择抄送人</span>
53
+ <van-icon name="cross" class="close-icon" @click="handleSelectorCancel"></van-icon>
54
+ </div>
55
+ <van-checkbox-group v-model="selectedUsers" class="checkbox-group">
56
+ <van-checkbox v-for="(user, index) in availableUsers" :key="getUserKey(user, index)" :name="getUserKey(user, index)" class="checkbox-item">
57
+ {{ getUserName(user) }}
58
+ </van-checkbox>
59
+ </van-checkbox-group>
60
+ <div class="selector-footer">
61
+ <van-button type="primary" block @click="confirmAddUsers">确定</van-button>
62
+ </div>
63
+ </div>
64
+ </van-popup>
65
+ </div>
66
+ </template>
67
+
68
+ <script>
69
+ export default {
70
+ name: 'FlowCopyUsers',
71
+ props: {
72
+ copyUsers: {
73
+ type: Array,
74
+ default: () => []
75
+ },
76
+ availableUsers: {
77
+ type: Array,
78
+ default: () => [
79
+ { id: '1', name: '张三' },
80
+ { id: '2', name: '李四' },
81
+ { id: '3', name: '王五' },
82
+ { id: '4', name: '赵六' },
83
+ { id: '5', name: '钱七' },
84
+ { id: '6', name: '孙八' }
85
+ ]
86
+ },
87
+ maxDisplay: {
88
+ type: Number,
89
+ default: 3
90
+ },
91
+ avatarSize: {
92
+ type: Number,
93
+ default: 24
94
+ },
95
+ editable: {
96
+ type: Boolean,
97
+ default: false
98
+ },
99
+ showLabel: {
100
+ type: Boolean,
101
+ default: true
102
+ },
103
+ rowKey: {
104
+ type: String,
105
+ default: 'id'
106
+ },
107
+ rowName: {
108
+ type: String,
109
+ default: 'name'
110
+ }
111
+ },
112
+ data() {
113
+ return {
114
+ showSelector: false,
115
+ moreCopyPopupVisible: false,
116
+ selectedUsers: [],
117
+ internalCopyUsers: []
118
+ }
119
+ },
120
+ watch: {
121
+ copyUsers: {
122
+ immediate: true,
123
+ deep: true,
124
+ handler(val) {
125
+ this.internalCopyUsers = [...val].map(u => ({ ...u, popupVisible: false }))
126
+ }
127
+ },
128
+ showSelector(val) {
129
+ if (val) {
130
+ this.selectedUsers = this.internalCopyUsers.map((u, index) => this.getUserKey(u, index))
131
+ }
132
+ }
133
+ },
134
+ computed: {
135
+ displayUsers() {
136
+ return this.internalCopyUsers.slice(0, this.maxDisplay)
137
+ },
138
+ remainingUsers() {
139
+ return this.internalCopyUsers.slice(this.maxDisplay)
140
+ }
141
+ },
142
+ methods: {
143
+ getUserKey(user, index = 0) {
144
+ return user[this.rowKey] || user.id || user.loginName || String(index)
145
+ },
146
+ getUserName(user) {
147
+ return user[this.rowName] || user.name || user.displayName || ''
148
+ },
149
+ getUserDept(user) {
150
+ return user.dept || user.orgNodeName || ''
151
+ },
152
+ cleanUsers(users) {
153
+ return users.map(user => {
154
+ const item = { ...user }
155
+ delete item.popupVisible
156
+ return item
157
+ })
158
+ },
159
+ getAvatarStyle(index) {
160
+ if (index === 0) return {}
161
+ return {
162
+ marginLeft: '-10px'
163
+ }
164
+ },
165
+ emitChange() {
166
+ this.$emit('change', this.cleanUsers(this.internalCopyUsers))
167
+ },
168
+ removeUser(index) {
169
+ this.internalCopyUsers.splice(index, 1)
170
+ this.emitChange()
171
+ },
172
+ handleAddClick() {
173
+ this.$emit('add-copy-users')
174
+ this.showSelector = true
175
+ },
176
+ handleSelectorCancel() {
177
+ this.selectedUsers = []
178
+ this.showSelector = false
179
+ },
180
+ confirmAddUsers() {
181
+ this.internalCopyUsers = this.selectedUsers.map(id =>
182
+ this.availableUsers.find((user, index) => this.getUserKey(user, index) === id)
183
+ ).filter(Boolean).map(u => ({ ...u, popupVisible: false }))
184
+ this.emitChange()
185
+ this.selectedUsers = []
186
+ this.showSelector = false
187
+ }
188
+ }
189
+ }
190
+ </script>
191
+
192
+ <style scoped>
193
+ .copy-users-wrapper {
194
+ display: flex;
195
+ align-items: center;
196
+ }
197
+
198
+ .copy-label {
199
+ font-size: 13px;
200
+ color: #8c8c8c;
201
+ flex-shrink: 0;
202
+ }
203
+
204
+ .copy-content {
205
+ flex: 1;
206
+ min-width: 0;
207
+ }
208
+
209
+ .copy-tags {
210
+ display: flex;
211
+ align-items: center;
212
+ flex-wrap: wrap;
213
+ gap: 6px;
214
+ }
215
+
216
+ .add-btn {
217
+ display: inline-flex;
218
+ align-items: center;
219
+ gap: 4px;
220
+ height: 26px;
221
+ padding: 0 10px;
222
+ border: 1px dashed #c8c9cc;
223
+ border-radius: 4px;
224
+ font-size: 12px;
225
+ color: #646566;
226
+ background: #fff;
227
+ cursor: pointer;
228
+ transition: all 0.2s;
229
+ }
230
+
231
+ .add-btn:hover {
232
+ border-color: #1989fa;
233
+ color: #1989fa;
234
+ }
235
+
236
+ .avatar-list {
237
+ display: flex;
238
+ align-items: center;
239
+ }
240
+
241
+ .copy-avatar {
242
+ width: 24px;
243
+ height: 24px;
244
+ border-radius: 50%;
245
+ display: flex;
246
+ align-items: center;
247
+ justify-content: center;
248
+ background: #1989fa;
249
+ color: #fff;
250
+ font-size: 12px;
251
+ border: 2px solid #fff;
252
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
253
+ cursor: pointer;
254
+ transition: transform 0.2s, box-shadow 0.2s;
255
+ overflow: hidden;
256
+ flex-shrink: 0;
257
+ }
258
+
259
+ .copy-avatar img {
260
+ width: 100%;
261
+ height: 100%;
262
+ object-fit: cover;
263
+ }
264
+
265
+ .copy-avatar:hover {
266
+ transform: scale(1.1);
267
+ box-shadow: 0 2px 8px rgba(25, 137, 250, 0.3);
268
+ z-index: 10;
269
+ position: relative;
270
+ }
271
+
272
+ .avatar-excess {
273
+ width: 24px;
274
+ height: 24px;
275
+ line-height: 20px;
276
+ text-align: center;
277
+ border-radius: 50%;
278
+ background: #1989fa;
279
+ color: #fff;
280
+ font-size: 11px;
281
+ margin-left: -10px;
282
+ border: 2px solid #fff;
283
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
284
+ cursor: pointer;
285
+ transition: transform 0.2s, box-shadow 0.2s;
286
+ flex-shrink: 0;
287
+ }
288
+
289
+ .avatar-excess:hover {
290
+ transform: scale(1.1);
291
+ box-shadow: 0 2px 8px rgba(25, 137, 250, 0.3);
292
+ z-index: 10;
293
+ position: relative;
294
+ }
295
+
296
+ .empty-text {
297
+ font-size: 12px;
298
+ color: #bfbfbf;
299
+ }
300
+
301
+ .copy-popup-name {
302
+ font-size: 14px;
303
+ color: #323233;
304
+ line-height: 1.5;
305
+ }
306
+
307
+ .copy-popup-dept {
308
+ font-size: 12px;
309
+ color: #969799;
310
+ line-height: 1.5;
311
+ }
312
+
313
+ .user-selector {
314
+ max-height: 60vh;
315
+ display: flex;
316
+ flex-direction: column;
317
+ }
318
+
319
+ .selector-header {
320
+ display: flex;
321
+ align-items: center;
322
+ justify-content: space-between;
323
+ padding: 16px;
324
+ border-bottom: 1px solid #f0f0f0;
325
+ }
326
+
327
+ .selector-title {
328
+ font-size: 16px;
329
+ font-weight: 600;
330
+ color: #323233;
331
+ }
332
+
333
+ .close-icon {
334
+ font-size: 20px;
335
+ color: #c8c9cc;
336
+ cursor: pointer;
337
+ }
338
+
339
+ .checkbox-group {
340
+ flex: 1;
341
+ overflow-y: auto;
342
+ padding: 12px 16px;
343
+ }
344
+
345
+ .checkbox-item {
346
+ padding: 12px 0;
347
+ border-bottom: 1px solid #f7f8fa;
348
+ }
349
+
350
+ .checkbox-item:last-child {
351
+ border-bottom: none;
352
+ }
353
+
354
+ .selector-footer {
355
+ padding: 12px 16px;
356
+ border-top: 1px solid #f0f0f0;
357
+ }
358
+ </style>