rrj-astra-ui 1.0.4 → 1.0.6
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/components/AuiIcon.vue +101 -56
- package/package.json +1 -1
package/components/AuiIcon.vue
CHANGED
|
@@ -5,16 +5,42 @@
|
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
7
|
<script setup>
|
|
8
|
-
import { ref, computed } from 'vue';
|
|
8
|
+
import { ref, computed, onMounted, watch } from 'vue';
|
|
9
9
|
import { camelize } from '@vue/shared';
|
|
10
10
|
import * as svgFiles from '../icons/index.js';
|
|
11
|
-
import { DOMParser, XMLSerializer } from '@xmldom/xmldom';
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
// 核心修复:兼容 xmldom 所有版本的导入逻辑
|
|
13
|
+
let DOMParser, XMLSerializer;
|
|
14
|
+
try {
|
|
15
|
+
// 动态导入避免编译时导出报错,兼容 v0.x(命名导出)和 v1+/v2+(默认导出)
|
|
16
|
+
import('@xmldom/xmldom').then(module => {
|
|
17
|
+
if (module.DOMParser) {
|
|
18
|
+
// v0.x 命名导出
|
|
19
|
+
DOMParser = module.DOMParser;
|
|
20
|
+
XMLSerializer = module.XMLSerializer;
|
|
21
|
+
} else if (module.default) {
|
|
22
|
+
// v1+/v2+ 默认导出
|
|
23
|
+
DOMParser = module.default.DOMParser;
|
|
24
|
+
XMLSerializer = module.default.XMLSerializer;
|
|
25
|
+
}
|
|
26
|
+
// 导入完成后重新加载 SVG
|
|
27
|
+
loadSvg();
|
|
28
|
+
});
|
|
29
|
+
} catch (e) {
|
|
30
|
+
console.error('xmldom 导入失败:', e);
|
|
31
|
+
// 兜底:避免组件崩溃
|
|
32
|
+
DOMParser = function() {
|
|
33
|
+
this.parseFromString = () => ({
|
|
34
|
+
getElementsByTagName: () => [{}]
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
XMLSerializer = function() {
|
|
38
|
+
this.serializeToString = () => '';
|
|
39
|
+
};
|
|
40
|
+
}
|
|
14
41
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
})
|
|
42
|
+
const __name = 'AuiIcon';
|
|
43
|
+
defineOptions({ name: __name });
|
|
18
44
|
|
|
19
45
|
const props = defineProps({
|
|
20
46
|
name: {
|
|
@@ -41,7 +67,6 @@ const props = defineProps({
|
|
|
41
67
|
type: [String, Number],
|
|
42
68
|
default: 0,
|
|
43
69
|
validator: (value) => {
|
|
44
|
-
// 允许数值角度或预设值
|
|
45
70
|
return !isNaN(Number(value)) || ['90', '180', '270', 'clockwise', 'counter-clockwise'].includes(value);
|
|
46
71
|
}
|
|
47
72
|
}
|
|
@@ -49,97 +74,117 @@ const props = defineProps({
|
|
|
49
74
|
|
|
50
75
|
const svgContent = ref('');
|
|
51
76
|
|
|
77
|
+
// 优化:封装 SVG 加载逻辑,支持 props 变化时重新加载
|
|
52
78
|
const loadSvg = () => {
|
|
79
|
+
// 未加载完 xmldom 时跳过
|
|
80
|
+
if (!DOMParser || !XMLSerializer) return;
|
|
81
|
+
|
|
53
82
|
const svg = svgFiles[props.name];
|
|
54
|
-
if (svg) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
83
|
+
if (!svg) {
|
|
84
|
+
console.error(`Icon "${props.name}" 不存在,请检查 icons/index.js 导出`);
|
|
85
|
+
svgContent.value = '';
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const parser = new DOMParser();
|
|
91
|
+
const doc = parser.parseFromString(svg, 'image/svg+xml');
|
|
92
|
+
const svgElement = doc.getElementsByTagName('svg')[0];
|
|
93
|
+
|
|
58
94
|
if (svgElement) {
|
|
59
|
-
//
|
|
95
|
+
// 清除默认尺寸和颜色,统一由 props 控制
|
|
60
96
|
svgElement.removeAttribute('width');
|
|
61
97
|
svgElement.removeAttribute('height');
|
|
98
|
+
svgElement.removeAttribute('fill'); // 补充移除 fill,避免覆盖 color
|
|
62
99
|
svgElement.removeAttribute('stroke');
|
|
63
100
|
svgElement.setAttribute('stroke', props.color);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
101
|
+
svgElement.setAttribute('fill', 'currentColor'); // 统一填充色为继承
|
|
102
|
+
|
|
103
|
+
// 递归设置子元素 stroke
|
|
104
|
+
const setStrokeAttributes = (element) => {
|
|
105
|
+
if (!element || !element.hasAttribute) return;
|
|
67
106
|
if (element.hasAttribute('stroke')) {
|
|
68
107
|
element.removeAttribute('stroke');
|
|
69
108
|
element.setAttribute('stroke', props.color);
|
|
70
109
|
} else {
|
|
71
110
|
element.setAttribute('stroke', 'none');
|
|
72
111
|
}
|
|
73
|
-
const children = element.childNodes;
|
|
112
|
+
const children = element.childNodes || [];
|
|
74
113
|
for (let i = 0; i < children.length; i++) {
|
|
75
114
|
if (children[i].nodeType === 1) {
|
|
76
115
|
setStrokeAttributes(children[i]);
|
|
77
116
|
}
|
|
78
117
|
}
|
|
79
|
-
}
|
|
118
|
+
};
|
|
80
119
|
setStrokeAttributes(svgElement);
|
|
81
120
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
svgContent.value =
|
|
85
|
-
}
|
|
86
|
-
console.error(
|
|
121
|
+
|
|
122
|
+
const serializer = new XMLSerializer();
|
|
123
|
+
svgContent.value = serializer.serializeToString(doc);
|
|
124
|
+
} catch (e) {
|
|
125
|
+
console.error(`处理 Icon "${props.name}" 失败:`, e);
|
|
126
|
+
svgContent.value = '';
|
|
87
127
|
}
|
|
88
128
|
};
|
|
89
129
|
|
|
90
|
-
|
|
130
|
+
// 监听 props 变化,重新加载 SVG(比如动态改 name/color/rotate)
|
|
131
|
+
watch([() => props.name, () => props.color, () => props.rotate], () => {
|
|
132
|
+
loadSvg();
|
|
133
|
+
}, { immediate: true });
|
|
91
134
|
|
|
135
|
+
// 样式计算(保持原有逻辑,优化可读性)
|
|
92
136
|
const style = computed(() => {
|
|
93
|
-
const
|
|
94
|
-
const height = typeof props.size === 'number' ? `${props.size}px` : props.size;
|
|
95
|
-
|
|
96
|
-
// 处理旋转逻辑
|
|
137
|
+
const sizeVal = typeof props.size === 'number' ? `${props.size}px` : props.size;
|
|
97
138
|
let rotation = '0deg';
|
|
98
|
-
|
|
139
|
+
|
|
99
140
|
if (!isNaN(Number(props.rotate))) {
|
|
100
|
-
// 直接使用数值角度
|
|
101
141
|
rotation = `${Number(props.rotate)}deg`;
|
|
102
142
|
} else {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
break;
|
|
112
|
-
case '270':
|
|
113
|
-
case 'counter-clockwise':
|
|
114
|
-
rotation = '270deg';
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
143
|
+
const rotateMap = {
|
|
144
|
+
'90': '90deg',
|
|
145
|
+
'180': '180deg',
|
|
146
|
+
'270': '270deg',
|
|
147
|
+
'clockwise': '90deg',
|
|
148
|
+
'counter-clockwise': '270deg'
|
|
149
|
+
};
|
|
150
|
+
rotation = rotateMap[props.rotate] || '0deg';
|
|
117
151
|
}
|
|
118
|
-
|
|
152
|
+
|
|
119
153
|
return {
|
|
120
|
-
width,
|
|
121
|
-
height,
|
|
154
|
+
width: sizeVal,
|
|
155
|
+
height: sizeVal,
|
|
122
156
|
display: 'inline-block',
|
|
123
157
|
verticalAlign: 'middle',
|
|
124
158
|
transform: `rotate(${rotation})`,
|
|
125
|
-
transition: 'transform 0.3s ease'
|
|
159
|
+
transition: 'transform 0.3s ease',
|
|
160
|
+
color: props.color // 确保 color 生效
|
|
126
161
|
};
|
|
127
162
|
});
|
|
128
163
|
|
|
164
|
+
// 类名计算(优化逻辑,兼容更多 class 类型)
|
|
129
165
|
const classes = computed(() => {
|
|
130
166
|
const baseClass = `icon-${camelize(props.name)}`;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (typeof props.class === 'object') {
|
|
138
|
-
|
|
167
|
+
const classList = [baseClass];
|
|
168
|
+
|
|
169
|
+
if (typeof props.class === 'string' && props.class) {
|
|
170
|
+
classList.push(...props.class.split(' ').filter(Boolean));
|
|
171
|
+
} else if (Array.isArray(props.class)) {
|
|
172
|
+
classList.push(...props.class.filter(Boolean));
|
|
173
|
+
} else if (typeof props.class === 'object' && props.class !== null) {
|
|
174
|
+
Object.entries(props.class).forEach(([key, value]) => {
|
|
175
|
+
if (value) classList.push(key);
|
|
176
|
+
});
|
|
139
177
|
}
|
|
140
|
-
|
|
178
|
+
|
|
179
|
+
return classList;
|
|
141
180
|
});
|
|
142
181
|
</script>
|
|
143
182
|
|
|
144
183
|
<style scoped>
|
|
184
|
+
/* 补充基础样式,避免 SVG 溢出 */
|
|
185
|
+
:deep(svg) {
|
|
186
|
+
width: 100%;
|
|
187
|
+
height: 100%;
|
|
188
|
+
overflow: hidden;
|
|
189
|
+
}
|
|
145
190
|
</style>
|