cd-icon-picker 1.2.4 → 1.2.7
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/data/categories.ts +4524 -2925
- package/package.json +1 -1
- package/src/IconPicker.vue +36 -35
package/package.json
CHANGED
package/src/IconPicker.vue
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<span v-else-if="isSvgMode && currentSelect" class="flex items-center justify-center">
|
|
6
6
|
<SvgIcon :name="currentSelect" :prefix="svgPrefix" />
|
|
7
7
|
</span>
|
|
8
|
-
<Icon v-else :icon="currentSelect || defaultIcon" />
|
|
8
|
+
<Icon v-else :icon="currentSelect || defaultIcon" :color="color" />
|
|
9
9
|
</div>
|
|
10
10
|
<t-input v-else :readonly="readonly" :style="{ width }" :placeholder="mode === 'text' ? placeholder : ''" class="cd-icon-picker" v-model="displayValue" @click="handleOpen">
|
|
11
11
|
<template #prefixIcon>
|
|
@@ -13,22 +13,32 @@
|
|
|
13
13
|
<span v-else-if="isSvgMode && currentSelect" class="cursor-pointer px-2 py-1 flex items-center">
|
|
14
14
|
<SvgIcon :name="currentSelect" :prefix="svgPrefix" />
|
|
15
15
|
</span>
|
|
16
|
-
<Icon v-else :icon="currentSelect || defaultIcon" class="cursor-pointer px-2 py-1" />
|
|
16
|
+
<Icon v-else :icon="currentSelect || defaultIcon" :color="color" class="cursor-pointer px-2 py-1" />
|
|
17
17
|
</template>
|
|
18
18
|
</t-input>
|
|
19
19
|
<teleport to="body">
|
|
20
|
-
<t-dialog v-model:visible="visible" width="
|
|
20
|
+
<t-dialog v-model:visible="visible" width="1100px" :close-on-esc-keydown="true" :close-on-overlay-click="true" @confirm="handleConfirm" @cancel="handleCancel">
|
|
21
21
|
<template #header>
|
|
22
22
|
<div class="cd-picker-header">
|
|
23
23
|
<span style="font-size: 14px;font-weight: normal;">当前图标:</span>
|
|
24
|
-
<img v-if="isImageUrl(currentSelect)" :src="currentSelect" :style="{ filter: color ? `drop-shadow(0 0 0 ${color})` : '' }" style="width:
|
|
24
|
+
<img v-if="isImageUrl(currentSelect)" :src="currentSelect" :style="{ filter: color ? `drop-shadow(0 0 0 ${color})` : '' }" style="width: 24px; height: 24px; object-fit: contain;" />
|
|
25
25
|
<span v-else-if="isSvgMode && currentSelect" :style="{ color }">
|
|
26
|
-
<SvgIcon :name="currentSelect" :prefix="svgPrefix" :size="'
|
|
26
|
+
<SvgIcon :name="currentSelect" :prefix="svgPrefix" :size="'24'" />
|
|
27
27
|
</span>
|
|
28
|
-
<Icon v-else :icon="currentSelect || defaultIcon" :size="'
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
<Icon v-else :icon="currentSelect || defaultIcon" :size="'24'" :style="{ color }" />
|
|
29
|
+
<span v-if="currentSelect" style="font-size: 12px; color: #999; margin-left: 8px; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">{{ currentSelect }}</span>
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
32
|
+
<template #footer>
|
|
33
|
+
<div class="cd-picker-footer">
|
|
34
|
+
<div class="cd-picker-footer-colors">
|
|
35
|
+
<input type="color" v-model="color" class="cd-picker-color-input" />
|
|
36
|
+
<span v-for="c in presetColors" :key="c" @click="color = c" :style="{ backgroundColor: c }" :class="['preset-color', color === c && 'preset-color-active']"></span>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="cd-picker-footer-actions">
|
|
39
|
+
<t-button theme="default" @click="handleCancel">取消</t-button>
|
|
40
|
+
<t-button theme="primary" @click="handleConfirm">确定</t-button>
|
|
41
|
+
</div>
|
|
32
42
|
</div>
|
|
33
43
|
</template>
|
|
34
44
|
<div class="cd-picker-body">
|
|
@@ -44,7 +54,6 @@
|
|
|
44
54
|
<div class="cd-picker-section-title">来源</div>
|
|
45
55
|
<ul class="cd-picker-cats">
|
|
46
56
|
<li :class="['cd-picker-cat', sourceType === 'remix' && 'active']" @click="sourceType = 'remix'">通用</li>
|
|
47
|
-
<li :class="['cd-picker-cat', sourceType === 'tdesign' && 'active']" @click="sourceType = 'tdesign'">通用2</li>
|
|
48
57
|
<li :class="['cd-picker-cat', sourceType === 'company' && 'active']" @click="sourceType = 'company'">企业</li>
|
|
49
58
|
<li :class="['cd-picker-cat', sourceType === 'custom' && 'active']" @click="sourceType = 'custom'">自定义</li>
|
|
50
59
|
</ul>
|
|
@@ -106,7 +115,7 @@ const props = defineProps({
|
|
|
106
115
|
remixicon: { type: String, default: '' },
|
|
107
116
|
svg: { type: String, default: '' },
|
|
108
117
|
width: { type: String, default: '100%' },
|
|
109
|
-
pageSize: { type: Number, default:
|
|
118
|
+
pageSize: { type: Number, default: 168 },
|
|
110
119
|
copy: { type: Boolean, default: false },
|
|
111
120
|
diyIcon: { type: [Array, Object] as PropType<string[] | { prefix: string; icons: string[]; categoryMap?: Record<string, string[]> }> },
|
|
112
121
|
placeholder: { type: String, default: '选择图标' },
|
|
@@ -163,7 +172,8 @@ const displayValue = computed({
|
|
|
163
172
|
watch(
|
|
164
173
|
() => props.type,
|
|
165
174
|
v => {
|
|
166
|
-
|
|
175
|
+
// backward compat: treat 'tdesign' as 'remix' since they're merged
|
|
176
|
+
sourceType.value = (v === 'tdesign' ? 'remix' : (v as any)) || 'remix';
|
|
167
177
|
},
|
|
168
178
|
{ immediate: true },
|
|
169
179
|
);
|
|
@@ -175,11 +185,7 @@ watch(
|
|
|
175
185
|
{ immediate: true },
|
|
176
186
|
);
|
|
177
187
|
watchEffect(() => {
|
|
178
|
-
|
|
179
|
-
if (sourceType.value === 'remix' && v && !/-line$|-fill$/.test(v)) {
|
|
180
|
-
v = v + (styleType.value === 'filled' ? '-fill' : '-line');
|
|
181
|
-
}
|
|
182
|
-
currentSelect.value = v;
|
|
188
|
+
currentSelect.value = props.value || '';
|
|
183
189
|
});
|
|
184
190
|
watch([rawIcons, styleType, sourceType], () => {
|
|
185
191
|
page.value = 1;
|
|
@@ -189,10 +195,10 @@ const categoryMap = computed(() => {
|
|
|
189
195
|
if (props.categoryMap) return props.categoryMap as Record<string, string[]>;
|
|
190
196
|
const d = props.diyIcon as any;
|
|
191
197
|
if (!Array.isArray(d) && d?.categoryMap) return d.categoryMap as Record<string, string[]>;
|
|
192
|
-
const map = (categories as any)[sourceType.value] || (categories as any).tdesign;
|
|
198
|
+
const map = (categories as any)[sourceType.value] || (categories as any).tdesign || {};
|
|
193
199
|
return map as Record<string, string[]>;
|
|
194
200
|
});
|
|
195
|
-
const categoryOptions = computed(() => Object.keys(categoryMap.value).filter(k => !k.endsWith('fill')));
|
|
201
|
+
const categoryOptions = computed(() => Object.keys(categoryMap.value || {}).filter(k => !k.endsWith('fill')));
|
|
196
202
|
const filteredIcons = computed(() => {
|
|
197
203
|
if (sourceType.value === 'custom') {
|
|
198
204
|
const list = customIcons.value;
|
|
@@ -225,10 +231,6 @@ const filteredIcons = computed(() => {
|
|
|
225
231
|
} else {
|
|
226
232
|
list = ((useFill ? map[`${categoryType.value}fill`] : map[categoryType.value]) || map[categoryType.value] || []) as string[];
|
|
227
233
|
}
|
|
228
|
-
if (sourceType.value === 'remix') {
|
|
229
|
-
const suffix = useFill ? '-fill' : '-line';
|
|
230
|
-
list = list.map(n => (/-line$|-fill$/.test(n) ? n : `${n}${suffix}`));
|
|
231
|
-
}
|
|
232
234
|
return searchText.value ? list.filter(i => i.includes(searchText.value)) : list;
|
|
233
235
|
});
|
|
234
236
|
const getTotal = computed(() => filteredIcons.value.length);
|
|
@@ -264,20 +266,13 @@ function handleConfirm() {
|
|
|
264
266
|
visible.value = false;
|
|
265
267
|
const icon = currentSelect.value;
|
|
266
268
|
if (!icon) return;
|
|
267
|
-
if (sourceType.value === 'tdesign') emit('update:tdesign', icon);
|
|
268
|
-
else if (sourceType.value === 'remix') emit('update:remixicon', icon);
|
|
269
|
-
else emit('update:svg', icon);
|
|
270
269
|
emit('update:value', icon);
|
|
271
270
|
emit('update:color', color.value);
|
|
272
271
|
emit('change', sourceType.value as any, icon as any, color.value);
|
|
273
272
|
}
|
|
274
273
|
function handleCancel() {
|
|
275
274
|
visible.value = false;
|
|
276
|
-
|
|
277
|
-
if (sourceType.value === 'remix' && v && !/-line$|-fill$/.test(v)) {
|
|
278
|
-
v = v + (styleType.value === 'filled' ? '-fill' : '-line');
|
|
279
|
-
}
|
|
280
|
-
currentSelect.value = v;
|
|
275
|
+
currentSelect.value = props.value || '';
|
|
281
276
|
}
|
|
282
277
|
function handleAddCustomImage() {
|
|
283
278
|
const url = customImageUrl.value.trim();
|
|
@@ -310,16 +305,22 @@ watch(
|
|
|
310
305
|
.cd-picker-cat.active { background: #f2f3f5; color: #0052d9; }
|
|
311
306
|
.cd-icon-only { display: flex; align-items: center; justify-content: center; border: 1px solid #e5e5e5; border-radius: 6px; padding: 6px; cursor: pointer; }
|
|
312
307
|
.cd-icon-only:hover { border-color: var(--td-brand-color, #0052d9); }
|
|
313
|
-
.cd-picker-main {
|
|
308
|
+
.cd-picker-main { flex: 1; }
|
|
314
309
|
.cd-picker-search { margin-bottom: 8px; }
|
|
315
310
|
.cd-picker-content { border-top: 1px solid #eee; }
|
|
316
|
-
.cd-picker-grid { list-style: none; margin: 0; padding: 10px 0; display: grid; grid-template-columns: repeat(
|
|
311
|
+
.cd-picker-grid { list-style: none; margin: 0; padding: 10px 0; display: grid; grid-template-columns: repeat(12, 1fr); gap: 10px; }
|
|
317
312
|
.cd-picker-item { display: flex; width: 30px; align-items: center; justify-content: center; border: 1px solid #e5e5e5; border-radius: 6px; padding: 8px; cursor: pointer; }
|
|
318
313
|
.cd-picker-item.active { border-color: var(--td-brand-color, #0052d9); }
|
|
319
314
|
.cd-picker-item:hover { border-color: var(--td-brand-color, #0052d9); }
|
|
320
315
|
.cd-picker-pagination { display: flex; justify-content: center; padding: 8px; }
|
|
321
316
|
.cd-picker-empty { padding: 24px; }
|
|
322
|
-
.cd-picker-header { display: flex; align-items: center; }
|
|
323
|
-
.
|
|
317
|
+
.cd-picker-header { display: flex; align-items: center; gap: 6px; }
|
|
318
|
+
.cd-picker-footer { display: flex; align-items: center; justify-content: space-between; width: 100%; }
|
|
319
|
+
.cd-picker-footer-colors { display: flex; align-items: center; gap: 6px; }
|
|
320
|
+
.cd-picker-footer-actions { display: flex; align-items: center; gap: 8px; }
|
|
321
|
+
.cd-picker-color-input { cursor: pointer; width: 28px; height: 28px; border: 1px solid #ddd; border-radius: 6px; padding: 1px; background: none; }
|
|
322
|
+
.cd-picker-color-input:hover { border-color: var(--td-brand-color, #0052d9); }
|
|
323
|
+
.preset-color { display: inline-block; width: 28px; height: 28px; border-radius: 6px; cursor: pointer; border: 2px solid transparent; transition: all 0.2s; box-sizing: border-box; }
|
|
324
324
|
.preset-color:hover { border-color: var(--td-brand-color, #0052d9); transform: scale(1.1); }
|
|
325
|
+
.preset-color-active { border-color: var(--td-brand-color, #0052d9); box-shadow: 0 0 0 2px rgba(0, 82, 217, 0.2); }
|
|
325
326
|
</style>
|