@pubinfo/core 2.0.0-rc.3 → 2.0.0-rc.5
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/dist/{AppSetting-3wJKvibc.js → AppSetting-DqVYDIHj.js} +15 -15
- package/dist/{HCheckList.vue_vue_type_script_setup_true_lang-17EywJvs.js → HCheckList.vue_vue_type_script_setup_true_lang-SrNklW3P.js} +1 -1
- package/dist/{HToggle-B-ZjSh6S.js → HToggle-DGTP9jYA.js} +1 -1
- package/dist/{PreferencesContent-xT4paU7N.js → PreferencesContent-5NtwK9RQ.js} +4 -4
- package/dist/{SettingBreadcrumb-CYnO51Ek.js → SettingBreadcrumb-BudqQsuJ.js} +3 -3
- package/dist/{SettingCopyright-FOW5ObHK.js → SettingCopyright-VUberG4R.js} +2 -2
- package/dist/{SettingEnableTransition-Q5cvubmF.js → SettingEnableTransition-C6NYf021.js} +2 -2
- package/dist/SettingHome-BTaeKgwN.js +46 -0
- package/dist/{SettingMenu-BNAJ3el9.js → SettingMenu-D9Aon2LP.js} +3 -3
- package/dist/{SettingMode-LzlRsBL9.js → SettingMode-DaqVd9Mq.js} +1 -1
- package/dist/{SettingNavSearch-BA08vYuw.js → SettingNavSearch-N4JIheIk.js} +2 -2
- package/dist/{SettingOther-BE8dDCYD.js → SettingOther-tLulcors.js} +2 -2
- package/dist/{SettingPage-D061yXCv.js → SettingPage-CEjWB45R.js} +2 -2
- package/dist/{SettingTabbar-COwdPPKy.js → SettingTabbar-DyeLhcCT.js} +3 -3
- package/dist/{SettingThemes-BHaYERNp.js → SettingThemes-C2M3tsVl.js} +1 -1
- package/dist/{SettingToolbar-fSuzu6ND.js → SettingToolbar-DI7de6i0.js} +24 -31
- package/dist/{SettingTopbar-D7GgP0KB.js → SettingTopbar-BgIoXeAq.js} +3 -3
- package/dist/{SettingWidthMode-CNjzChe1.js → SettingWidthMode-DIAU4s5e.js} +1 -1
- package/dist/{TopThinMode-B-28sBJD.js → TopThinMode-JNUHrJI2.js} +1 -1
- package/dist/built-in/authentication/alova/helper.d.ts +34 -0
- package/dist/built-in/authentication/alova/token.d.ts +16 -0
- package/dist/built-in/authentication/helper.d.ts +10 -1
- package/dist/built-in/index.d.ts +1 -1
- package/dist/built-in/layout-component/components/Tools/SearchPanel.vue.d.ts +7 -2
- package/dist/built-in/layout-component/components/ui/HSlideover.vue.d.ts +1 -1
- package/dist/built-in/layout-component/composables/useContext.d.ts +1 -1
- package/dist/built-in/layout-component/composables/useGlobalSearch.d.ts +7 -0
- package/dist/built-in/layout-component/composables/useTitle.d.ts +1 -1
- package/dist/built-in/system-info/components/SystemInfo.vue.d.ts +2 -0
- package/dist/built-in/system-info/index.d.ts +5 -0
- package/dist/{colors-BIQSd520.js → colors-DxWfHM_v.js} +1 -1
- package/dist/core/interface.d.ts +14 -5
- package/dist/core/request.d.ts +2 -8
- package/dist/features/api/modules/auth/renzhengfuwu.d.ts +8 -8
- package/dist/features/api/modules/configData/heibaimingdanfuwu.d.ts +5 -5
- package/dist/features/api/modules/configData/xitongpeizhifuwu.d.ts +14 -14
- package/dist/features/api/modules/configData/zidifuwu.d.ts +10 -10
- package/dist/features/api/modules/rbac/gangweijiekou.d.ts +6 -6
- package/dist/features/api/modules/rbac/jiaosejiekou.d.ts +7 -7
- package/dist/features/api/modules/rbac/pubJiaosezukongzhiqi.d.ts +7 -7
- package/dist/features/api/modules/rbac/shujuquanxianzhubiaokongzhiqi.d.ts +9 -9
- package/dist/features/api/modules/rbac/yonghujiekou.d.ts +15 -15
- package/dist/features/api/modules/rbac/yonghushoucangbiaojiekou.d.ts +5 -5
- package/dist/features/api/modules/rbac/yonghuzuijinchangyongbiaojiekou.d.ts +4 -4
- package/dist/features/api/modules/rbac/ziyuanjiekou.d.ts +13 -13
- package/dist/features/api/modules/rbac/zuhuguanlijiekou.d.ts +5 -5
- package/dist/features/api/modules/rbac/zuzhijiaosebiaokongzhiqi.d.ts +4 -4
- package/dist/features/api/modules/rbac/zuzhijiekou.d.ts +9 -9
- package/dist/features/api/system/user.d.ts +4 -4
- package/dist/features/components/PubinfoIcon/PrismBox.vue.d.ts +21 -0
- package/dist/features/components/PubinfoIcon/SquareBox.vue.d.ts +17 -0
- package/dist/features/components/PubinfoIcon/index.vue.d.ts +13 -9
- package/dist/features/components/PubinfoIcon/props.d.ts +58 -0
- package/dist/features/components/index.d.ts +2 -0
- package/dist/{index-Dlf6GQBd.js → index-5fRpGyLW.js} +4 -4
- package/dist/{index-DNdH93AP.js → index-BFRIv97x.js} +2 -2
- package/dist/{index-WubcSL0v.js → index-BH-vHGvk.js} +1 -1
- package/dist/{index-YSjb6X1D.js → index-C7xIGcDc.js} +4 -7
- package/dist/{index-DYMBkmAp.js → index-CNVn3Ubv.js} +2 -2
- package/dist/{index-CPRiufg0.js → index-Cf-u1Zqh.js} +1 -1
- package/dist/{index-wxEEuQXu.js → index-D4v4g8FJ.js} +111 -97
- package/dist/{index-IscoZG-Y.js → index-DQGnbEGS.js} +2 -2
- package/dist/{index-Beb7_0-E.js → index-Dv7UUFkD.js} +24237 -23807
- package/dist/index.d.ts +1 -1
- package/dist/index.js +55 -48
- package/dist/{pick-D_XPbQHB.js → pick-VFuUwFn-.js} +1 -1
- package/dist/style.css +1 -1
- package/dist/utils/global.d.ts +33 -0
- package/dist/utils/index.d.ts +2 -1
- package/package.json +10 -7
- package/src/built-in/authentication/alova/helper.ts +158 -0
- package/src/built-in/authentication/alova/token.ts +122 -0
- package/src/built-in/authentication/helper.ts +7 -3
- package/src/built-in/authentication/index.ts +6 -20
- package/src/built-in/index.ts +1 -1
- package/src/built-in/layout-component/components/Header/TopMode/index.vue +30 -9
- package/src/built-in/layout-component/components/Menu/item.vue +44 -9
- package/src/built-in/layout-component/components/SettingBar/components/SettingHome.vue +1 -4
- package/src/built-in/layout-component/components/SettingBar/components/SettingToolbar.vue +0 -6
- package/src/built-in/layout-component/components/Sidebar/MainSidebar.vue +3 -3
- package/src/built-in/layout-component/components/Tools/SearchBar.vue +1 -3
- package/src/built-in/layout-component/components/Tools/SearchPanel.vue +125 -57
- package/src/built-in/layout-component/components/Tools/index.vue +9 -12
- package/src/built-in/layout-component/components/Topbar/Tabbar/MoreAction.vue +64 -11
- package/src/built-in/layout-component/components/Topbar/Tabbar/index.vue +73 -18
- package/src/built-in/layout-component/components/Topbar/Toolbar/Favorites.vue +4 -7
- package/src/built-in/layout-component/components/Topbar/Toolbar/index.vue +6 -6
- package/src/built-in/layout-component/composables/useContext.ts +1 -1
- package/src/built-in/layout-component/composables/useGlobalSearch.ts +40 -1
- package/src/built-in/layout-component/composables/useTitle.ts +8 -14
- package/src/built-in/layout-component/provider.ts +2 -2
- package/src/built-in/system-info/components/SystemInfo.vue +53 -0
- package/src/built-in/system-info/index.ts +16 -0
- package/src/core/ctx.ts +7 -1
- package/src/core/interface.ts +18 -5
- package/src/core/request.ts +35 -15
- package/src/core/resolver/icon.ts +9 -5
- package/src/features/components/PubinfoIcon/PrismBox.vue +203 -0
- package/src/features/components/PubinfoIcon/SquareBox.vue +59 -0
- package/src/features/components/PubinfoIcon/index.vue +128 -37
- package/src/features/components/PubinfoIcon/props.ts +54 -0
- package/src/features/components/index.ts +4 -1
- package/src/features/context/index.ts +1 -16
- package/src/features/router/systemRoutes.ts +0 -1
- package/src/features/settings/index.ts +0 -1
- package/src/features/stores/modules/favorites.ts +0 -1
- package/src/features/stores/modules/route.ts +2 -9
- package/src/features/stores/modules/tabbar.ts +0 -3
- package/src/features/stores/utils/routerHelper.ts +35 -4
- package/src/index.ts +7 -2
- package/src/utils/global.ts +161 -0
- package/src/utils/index.ts +2 -1
- package/src/utils/proxy.ts +7 -8
- package/types/global.d.ts +7 -0
- package/types/menu.d.ts +10 -0
- package/types/settings.d.ts +0 -7
- package/types/vue-router.d.ts +0 -3
- package/dist/SettingHome-Df7-AlWB.js +0 -55
- package/dist/built-in/locales/helpler.d.ts +0 -594
- package/dist/built-in/locales/index.d.ts +0 -5
- package/dist/built-in/locales/lang/en.json.d.ts +0 -99
- package/dist/built-in/locales/lang/zh-cn.json.d.ts +0 -100
- package/dist/built-in/locales/lang/zh-tw.json.d.ts +0 -100
- package/dist/built-in/locales/ui.d.ts +0 -3
- package/src/built-in/locales/helpler.ts +0 -76
- package/src/built-in/locales/index.ts +0 -20
- package/src/built-in/locales/lang/en.json +0 -96
- package/src/built-in/locales/lang/zh-cn.json +0 -97
- package/src/built-in/locales/lang/zh-tw.json +0 -97
- package/src/built-in/locales/ui.ts +0 -3
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* PrismBox(六边形容器)
|
|
6
|
+
*
|
|
7
|
+
* 目标:
|
|
8
|
+
* - 默认使用纯 CSS clip-path 生成六边形
|
|
9
|
+
* - 当检测到 CSS 变量 `--pubinfo-box-radius` > 0 且容器为“像素正方形”时,
|
|
10
|
+
* 通过运行时按需生成圆角六边形 SVG mask(mask-image / -webkit-mask-image),
|
|
11
|
+
* 兼容浏览器差异,保持与 SquareBox 一致的主题变量接口。
|
|
12
|
+
*
|
|
13
|
+
* 注:圆角六边形目前纯 CSS 难以稳定跨浏览器实现(clip-path 不支持圆角),
|
|
14
|
+
* 因此采用极薄的运行时以 CSS 变量为输入生成 mask;未满足条件时退回纯 CSS 六边形。
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const el = ref<HTMLElement | null>(null);
|
|
18
|
+
let ro: ResizeObserver | null = null;
|
|
19
|
+
let mo: MutationObserver | null = null;
|
|
20
|
+
let pending = false;
|
|
21
|
+
|
|
22
|
+
function scheduleApply() {
|
|
23
|
+
if (pending) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
pending = true;
|
|
27
|
+
requestAnimationFrame(() => {
|
|
28
|
+
pending = false;
|
|
29
|
+
applyMask();
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function parsePx(input: string | null | undefined): number | null {
|
|
34
|
+
if (!input) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const n = Number.parseFloat(input);
|
|
38
|
+
return Number.isFinite(n) ? n : null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function genRoundedHexMask(size: number, rPx: number): string | null {
|
|
42
|
+
if (!(size > 0 && rPx > 0)) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
const maxR = size * 0.35;
|
|
46
|
+
const R = Math.min(rPx, maxR);
|
|
47
|
+
const rp = (R / size) * 100;
|
|
48
|
+
// 移除衰减系数以提高圆角的可见度
|
|
49
|
+
const pts = [
|
|
50
|
+
{ x: 50, y: 0 },
|
|
51
|
+
{ x: 93, y: 25 },
|
|
52
|
+
{ x: 93, y: 75 },
|
|
53
|
+
{ x: 50, y: 100 },
|
|
54
|
+
{ x: 7, y: 75 },
|
|
55
|
+
{ x: 7, y: 25 },
|
|
56
|
+
];
|
|
57
|
+
const normEdge = 43;
|
|
58
|
+
const t = Math.min(rp / normEdge, 0.5);
|
|
59
|
+
const lerp = (a: any, b: any, tt: number) => ({ x: a.x + (b.x - a.x) * tt, y: a.y + (b.y - a.y) * tt });
|
|
60
|
+
const before: any[] = [];
|
|
61
|
+
const after: any[] = [];
|
|
62
|
+
for (let i = 0; i < pts.length; i++) {
|
|
63
|
+
const p = pts[i];
|
|
64
|
+
const pPrev = pts[(i - 1 + pts.length) % pts.length];
|
|
65
|
+
const pNext = pts[(i + 1) % pts.length];
|
|
66
|
+
before.push(lerp(p, pPrev, t));
|
|
67
|
+
after.push(lerp(p, pNext, t));
|
|
68
|
+
}
|
|
69
|
+
let d = `M ${after[0].x} ${after[0].y}`;
|
|
70
|
+
for (let i = 1; i < pts.length; i++) {
|
|
71
|
+
d += ` L ${before[i].x} ${before[i].y} Q ${pts[i].x} ${pts[i].y} ${after[i].x} ${after[i].y}`;
|
|
72
|
+
}
|
|
73
|
+
d += ` L ${before[0].x} ${before[0].y} Q ${pts[0].x} ${pts[0].y} ${after[0].x} ${after[0].y} Z`;
|
|
74
|
+
const svg = `<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><path fill='white' d='${d}'/></svg>`;
|
|
75
|
+
const encoded = encodeURIComponent(svg).replace(/'/g, '%27').replace(/"/g, '%22');
|
|
76
|
+
return `url("data:image/svg+xml,${encoded}")`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function applyMask() {
|
|
80
|
+
const node = el.value;
|
|
81
|
+
if (!node) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const cs = window.getComputedStyle(node);
|
|
85
|
+
|
|
86
|
+
const w = parsePx(cs.width);
|
|
87
|
+
const h = parsePx(cs.height);
|
|
88
|
+
// 允许通过 CSS 变量传递 px 值(推荐 px),非 px 或缺省将视作 0
|
|
89
|
+
const rVar = cs.getPropertyValue('--pubinfo-box-radius').trim();
|
|
90
|
+
let rPx: number = 0;
|
|
91
|
+
if (rVar) {
|
|
92
|
+
const numeric = parsePx(rVar);
|
|
93
|
+
rPx = numeric ?? (rVar.endsWith('px') ? parsePx(rVar) ?? 0 : 0);
|
|
94
|
+
}
|
|
95
|
+
// 变量取不到时,回退解析 border-radius 的左上角值(px)
|
|
96
|
+
if (!rPx) {
|
|
97
|
+
const br = cs.borderTopLeftRadius || cs.borderRadius;
|
|
98
|
+
const brNum = parsePx(br);
|
|
99
|
+
if (brNum) {
|
|
100
|
+
rPx = brNum;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 宽高可解析且 r>0 时启用圆角 mask(非正方形也允许,形状会按元素比例拉伸)
|
|
105
|
+
if (w && h && rPx > 0) {
|
|
106
|
+
const size = Math.min(w, h);
|
|
107
|
+
const maskUrl = genRoundedHexMask(size, rPx);
|
|
108
|
+
if (maskUrl) {
|
|
109
|
+
const supportsMask = typeof CSS !== 'undefined' && (
|
|
110
|
+
CSS.supports('mask-image', 'url("data:image/svg+xml,%3Csvg%3E%3C/svg%3E")')
|
|
111
|
+
|| CSS.supports('-webkit-mask-image', 'url("data:image/svg+xml,%3Csvg%3E%3C/svg%3E")')
|
|
112
|
+
);
|
|
113
|
+
node.style.webkitMaskImage = maskUrl as any;
|
|
114
|
+
node.style.maskImage = maskUrl as any;
|
|
115
|
+
node.style.webkitMaskRepeat = 'no-repeat';
|
|
116
|
+
node.style.maskRepeat = 'no-repeat';
|
|
117
|
+
node.style.webkitMaskSize = '100% 100%';
|
|
118
|
+
node.style.maskSize = '100% 100%';
|
|
119
|
+
node.style.webkitMaskPosition = 'center';
|
|
120
|
+
node.style.maskPosition = 'center';
|
|
121
|
+
if (supportsMask) {
|
|
122
|
+
// 移除 clip-path,避免其直角边缘覆盖圆角效果
|
|
123
|
+
node.style.clipPath = 'none';
|
|
124
|
+
(node.style as any).webkitClipPath = 'none';
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// 不支持 mask 时回退 clip-path 六边形
|
|
128
|
+
node.style.clipPath = 'polygon(50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%)';
|
|
129
|
+
(node.style as any).webkitClipPath = 'polygon(50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%)';
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// 回退:去掉 mask,仅使用 clip-path 六边形
|
|
135
|
+
node.style.webkitMaskImage = '';
|
|
136
|
+
node.style.maskImage = '';
|
|
137
|
+
node.style.webkitMaskRepeat = '';
|
|
138
|
+
node.style.maskRepeat = '';
|
|
139
|
+
node.style.webkitMaskSize = '';
|
|
140
|
+
node.style.maskSize = '';
|
|
141
|
+
node.style.webkitMaskPosition = '';
|
|
142
|
+
node.style.maskPosition = '';
|
|
143
|
+
node.style.clipPath = 'polygon(50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%)';
|
|
144
|
+
(node.style as any).webkitClipPath = 'polygon(50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%)';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
onMounted(() => {
|
|
148
|
+
applyMask();
|
|
149
|
+
ro = new ResizeObserver(() => applyMask());
|
|
150
|
+
if (el.value) {
|
|
151
|
+
ro.observe(el.value);
|
|
152
|
+
}
|
|
153
|
+
// 监听自身与文档根节点的样式/类变化,以捕获 CSS 变量或主题切换
|
|
154
|
+
mo = new MutationObserver(() => scheduleApply());
|
|
155
|
+
if (el.value) {
|
|
156
|
+
mo.observe(el.value, { attributes: true, attributeFilter: ['style', 'class'] });
|
|
157
|
+
}
|
|
158
|
+
mo.observe(document.documentElement, { attributes: true, attributeFilter: ['class', 'style'], subtree: true });
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
onBeforeUnmount(() => {
|
|
162
|
+
if (ro && el.value) {
|
|
163
|
+
ro.unobserve(el.value);
|
|
164
|
+
}
|
|
165
|
+
ro = null;
|
|
166
|
+
if (mo) {
|
|
167
|
+
mo.disconnect();
|
|
168
|
+
}
|
|
169
|
+
mo = null;
|
|
170
|
+
});
|
|
171
|
+
</script>
|
|
172
|
+
|
|
173
|
+
<template>
|
|
174
|
+
<div ref="el" class="prism-box">
|
|
175
|
+
<slot />
|
|
176
|
+
</div>
|
|
177
|
+
</template>
|
|
178
|
+
|
|
179
|
+
<style scoped>
|
|
180
|
+
.prism-box {
|
|
181
|
+
display: inline-flex;
|
|
182
|
+
align-items: center;
|
|
183
|
+
justify-content: center;
|
|
184
|
+
box-sizing: border-box;
|
|
185
|
+
overflow: hidden;
|
|
186
|
+
line-height: 1;
|
|
187
|
+
vertical-align: top;
|
|
188
|
+
width: var(--pubinfo-box-size, auto);
|
|
189
|
+
height: var(--pubinfo-box-size, auto);
|
|
190
|
+
border-radius: var(--pubinfo-box-radius) !important;
|
|
191
|
+
background: var(
|
|
192
|
+
--pubinfo-box-full-background,
|
|
193
|
+
linear-gradient(
|
|
194
|
+
var(--pubinfo-box-angle, 65deg),
|
|
195
|
+
var(--pubinfo-box-gradient-from, var(--pubinfo-box-background, #65E54A)),
|
|
196
|
+
var(--pubinfo-box-gradient-to, var(--pubinfo-box-background, #35C724))
|
|
197
|
+
)
|
|
198
|
+
);
|
|
199
|
+
/* 纯 CSS 六边形裁剪 */
|
|
200
|
+
-webkit-clip-path: polygon(50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%);
|
|
201
|
+
clip-path: polygon(50% 0%, 93% 25%, 93% 75%, 50% 100%, 7% 75%, 7% 25%);
|
|
202
|
+
}
|
|
203
|
+
</style>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* SquareBox
|
|
4
|
+
* 语义:图标 / 徽记 / 占位的方形或矩形“载体”容器。
|
|
5
|
+
*
|
|
6
|
+
* 设计目标:
|
|
7
|
+
* - 统一图标背景、圆角、渐变、尺寸计算逻辑(与 PrismBox 等保持一致)
|
|
8
|
+
* - 使用 <div> 而不是 <svg>,可直接放置任意 HTML / SVG / 组件
|
|
9
|
+
*
|
|
10
|
+
* 尺寸策略:
|
|
11
|
+
* - width / height / size 三种方式;size 作为宽高同步值
|
|
12
|
+
* - full=true 填充父容器(忽略 width/height/size)
|
|
13
|
+
* - 仅在固定 px 宽高时暴露:--square-inner-size / --box-inner-size = width - padding*2
|
|
14
|
+
*
|
|
15
|
+
* 圆角策略:
|
|
16
|
+
* - radius 支持 数字 | 纯数字字符串 | 其他合法 CSS 长度/百分比
|
|
17
|
+
* - 未传或空串 => 默认 6px;传 0 获得直角
|
|
18
|
+
* - 暴露 CSS 变量:--box-radius 便于主题覆写
|
|
19
|
+
*
|
|
20
|
+
* 背景策略:
|
|
21
|
+
* - 优先使用 props.background
|
|
22
|
+
* - 否则生成线性渐变:gradientFrom -> gradientTo
|
|
23
|
+
*
|
|
24
|
+
* 可访问性:
|
|
25
|
+
* - 不附带 role/aria-*,由使用方按内容语义自行添加
|
|
26
|
+
*
|
|
27
|
+
*/
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<template>
|
|
31
|
+
<div class="square-box">
|
|
32
|
+
<slot />
|
|
33
|
+
</div>
|
|
34
|
+
</template>
|
|
35
|
+
|
|
36
|
+
<style scoped>
|
|
37
|
+
.square-box {
|
|
38
|
+
display: inline-flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
justify-content: center;
|
|
41
|
+
box-sizing: border-box;
|
|
42
|
+
overflow: hidden;
|
|
43
|
+
line-height: 1;
|
|
44
|
+
vertical-align: top;
|
|
45
|
+
border-radius: var(--pubinfo-box-radius) !important;
|
|
46
|
+
/*
|
|
47
|
+
Allow a complete background override via CSS var when provided,
|
|
48
|
+
otherwise fall back to previous linear gradient behavior.
|
|
49
|
+
*/
|
|
50
|
+
background: var(
|
|
51
|
+
--pubinfo-box-full-background,
|
|
52
|
+
linear-gradient(
|
|
53
|
+
var(--pubinfo-box-angle, 65deg),
|
|
54
|
+
var(--pubinfo-box-gradient-from, var(--pubinfo-box-background, #65E54A)),
|
|
55
|
+
var(--pubinfo-box-gradient-to, var(--pubinfo-box-background, #35C724))
|
|
56
|
+
)
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
</style>
|
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import type { PubinfoIconOptions } from './props';
|
|
2
3
|
import * as AntdIcons from '@ant-design/icons-vue';
|
|
3
4
|
import { Icon } from '@iconify/vue';
|
|
4
5
|
import { computedAsync } from '@vueuse/core';
|
|
5
6
|
import { setupIcon } from '@/core';
|
|
6
7
|
import { useProvider } from '../PubinfoProvider';
|
|
8
|
+
import PrismBox from './PrismBox.vue';
|
|
9
|
+
import SquareBox from './SquareBox.vue';
|
|
7
10
|
|
|
8
11
|
defineOptions({
|
|
9
12
|
name: 'PubinfoIcon',
|
|
10
13
|
});
|
|
11
14
|
|
|
12
|
-
const props = defineProps<
|
|
13
|
-
name: string
|
|
14
|
-
async?: boolean
|
|
15
|
-
flip?: 'horizontal' | 'vertical' | 'both'
|
|
16
|
-
rotate?: number
|
|
17
|
-
color?: string
|
|
18
|
-
size?: string | number
|
|
19
|
-
}>();
|
|
15
|
+
const props = defineProps<PubinfoIconOptions>();
|
|
20
16
|
|
|
21
17
|
const { loadIcon } = useProvider();
|
|
22
18
|
|
|
23
|
-
const outputType = computed(() => {
|
|
19
|
+
const outputType = computed<'svg' | 'css' | 'antd' | 'custom'>(() => {
|
|
24
20
|
if (props.name.indexOf('i-') === 0) {
|
|
25
21
|
return props.async ? 'svg' : 'css';
|
|
26
22
|
}
|
|
@@ -51,35 +47,30 @@ const outputName = computed(() => {
|
|
|
51
47
|
return props.name;
|
|
52
48
|
});
|
|
53
49
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
switch (props.flip) {
|
|
58
|
-
case 'horizontal':
|
|
59
|
-
transform.push('rotateY(180deg)');
|
|
60
|
-
break;
|
|
61
|
-
case 'vertical':
|
|
62
|
-
transform.push('rotateX(180deg)');
|
|
63
|
-
break;
|
|
64
|
-
case 'both':
|
|
65
|
-
transform.push('rotateX(180deg)');
|
|
66
|
-
transform.push('rotateY(180deg)');
|
|
67
|
-
break;
|
|
68
|
-
}
|
|
50
|
+
const isBox = computed<'square' | 'prism' | 'null'>(() => {
|
|
51
|
+
if (props.box === undefined) {
|
|
52
|
+
return 'null';
|
|
69
53
|
}
|
|
70
|
-
if (props.
|
|
71
|
-
|
|
54
|
+
else if (['square', 'prism'].includes(props.box)) {
|
|
55
|
+
return props.box;
|
|
72
56
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
57
|
+
else {
|
|
58
|
+
return 'null';
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const element = computed(() => {
|
|
63
|
+
if (isBox.value === 'square') {
|
|
64
|
+
return SquareBox;
|
|
65
|
+
}
|
|
66
|
+
else if (isBox.value === 'prism') {
|
|
67
|
+
return PrismBox;
|
|
68
|
+
}
|
|
69
|
+
return 'i';
|
|
78
70
|
});
|
|
79
71
|
|
|
80
72
|
const icon = computedAsync(async () => {
|
|
81
73
|
const parse = parseString(props.name);
|
|
82
|
-
|
|
83
74
|
return loadIcon?.(props.name) ?? await setupIcon(`${parse.name}${parse.ext || '.svg'}`, parse.prefix);
|
|
84
75
|
});
|
|
85
76
|
|
|
@@ -124,17 +115,117 @@ function parseString(str: string): Parsed {
|
|
|
124
115
|
|
|
125
116
|
return { prefix, name, ext };
|
|
126
117
|
}
|
|
118
|
+
|
|
119
|
+
const style = computed(() => {
|
|
120
|
+
const transform: string[] = [];
|
|
121
|
+
if (props.flip) {
|
|
122
|
+
switch (props.flip) {
|
|
123
|
+
case 'horizontal':
|
|
124
|
+
transform.push('rotateY(180deg)');
|
|
125
|
+
break;
|
|
126
|
+
case 'vertical':
|
|
127
|
+
transform.push('rotateX(180deg)');
|
|
128
|
+
break;
|
|
129
|
+
case 'both':
|
|
130
|
+
transform.push('rotateX(180deg)');
|
|
131
|
+
transform.push('rotateY(180deg)');
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (props.rotate) {
|
|
136
|
+
transform.push(`rotate(${props.rotate % 360}deg)`);
|
|
137
|
+
}
|
|
138
|
+
const result: Record<string, string> = {
|
|
139
|
+
'--pubinfo-icon-color': '',
|
|
140
|
+
'--pubinfo-icon-size': '',
|
|
141
|
+
'--pubinfo-icon-transform': '',
|
|
142
|
+
};
|
|
143
|
+
if (props.color) {
|
|
144
|
+
result['--pubinfo-icon-color'] = props.color;
|
|
145
|
+
}
|
|
146
|
+
if (props.size) {
|
|
147
|
+
result['--pubinfo-icon-size'] = typeof props.size === 'number' ? `${props.size}px` : props.size;
|
|
148
|
+
}
|
|
149
|
+
if (transform.length) {
|
|
150
|
+
result['--pubinfo-icon-transform'] = transform.join(' ');
|
|
151
|
+
}
|
|
152
|
+
// 让内联元素在同一行内进行顶部对齐,避免默认 baseline 导致的视觉不齐
|
|
153
|
+
result.verticalAlign = 'top';
|
|
154
|
+
return result;
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const bind = computed(() => {
|
|
158
|
+
if (isBox.value !== 'null') {
|
|
159
|
+
const result: Record<
|
|
160
|
+
'--pubinfo-box-background' | '--pubinfo-box-size' | '--pubinfo-box-radius' | '--pubinfo-box-gradient-from' | '--pubinfo-box-gradient-to' | '--pubinfo-box-angle' | '--pubinfo-box-full-background',
|
|
161
|
+
string> = {
|
|
162
|
+
'--pubinfo-box-background': '',
|
|
163
|
+
'--pubinfo-box-size': '',
|
|
164
|
+
'--pubinfo-box-radius': '',
|
|
165
|
+
'--pubinfo-box-gradient-from': '',
|
|
166
|
+
'--pubinfo-box-gradient-to': '',
|
|
167
|
+
'--pubinfo-box-angle': '',
|
|
168
|
+
'--pubinfo-box-full-background': '',
|
|
169
|
+
};
|
|
170
|
+
if (typeof props.angle === 'string') {
|
|
171
|
+
result['--pubinfo-box-angle'] = props.angle;
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
result['--pubinfo-box-angle'] = `${props.angle || 65}deg`;
|
|
175
|
+
}
|
|
176
|
+
if (typeof props.background === 'string') {
|
|
177
|
+
// provide full override for any CSS background value (color or gradient)
|
|
178
|
+
result['--pubinfo-box-full-background'] = props.background;
|
|
179
|
+
// keep legacy variable for solid color fallback use-cases
|
|
180
|
+
result['--pubinfo-box-background'] = props.background;
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
result['--pubinfo-box-gradient-from'] = props.background?.from || '#65E54A';
|
|
184
|
+
result['--pubinfo-box-gradient-to'] = props.background?.to || '#35C724';
|
|
185
|
+
}
|
|
186
|
+
result['--pubinfo-box-radius'] = props.radius !== undefined
|
|
187
|
+
? (typeof props.radius === 'number' ? `${props.small ? props.radius / 2 : props.radius}px` : props.small ? `calc(${props.radius} / 2)` : props.radius)
|
|
188
|
+
: (props.box === 'prism' ? `${props.small ? '3px' : '6px'}` : `${props.small ? '7px' : '14px'}`);
|
|
189
|
+
if (props.size) {
|
|
190
|
+
result['--pubinfo-box-size'] = typeof props.size === 'number' ? `${props.size}px` : props.size;
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
style: { ...result, ...style.value },
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
return {
|
|
198
|
+
style: style.value,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
});
|
|
127
202
|
</script>
|
|
128
203
|
|
|
129
204
|
<template>
|
|
130
|
-
<
|
|
205
|
+
<component
|
|
206
|
+
:is="element"
|
|
207
|
+
:data-icones="props.name"
|
|
208
|
+
:data-box="isBox !== 'null' ? '' : undefined"
|
|
209
|
+
:data-box-type="isBox"
|
|
210
|
+
:data-icon-type="outputType"
|
|
211
|
+
:data-icon-small="props.small ? '' : undefined"
|
|
212
|
+
class="
|
|
213
|
+
data-[box-type=null]:relative data-[box-type=null]:inline-flex
|
|
214
|
+
data-[box-type=null]:items-center data-[box-type=null]:justify-center
|
|
215
|
+
data-[box-type=null]:fill-current data-[box-type=null]:leading-[1]
|
|
216
|
+
data-[box-type=null]:align-top
|
|
217
|
+
data-[box]:size-[var(--pubinfo-box-size)]!
|
|
218
|
+
data-[box]:text-[calc(var(--pubinfo-box-size)_*_0.5)]!
|
|
219
|
+
text-[calc(var(--pubinfo-icon-size))]
|
|
220
|
+
text-[var(--pubinfo-icon-color)]
|
|
221
|
+
[transform:var(--pubinfo-icon-transform)]"
|
|
222
|
+
v-bind="bind"
|
|
223
|
+
>
|
|
131
224
|
<i v-if="outputType === 'css'" :class="outputName" />
|
|
132
225
|
<Icon v-else-if="outputType === 'svg'" :icon="outputName" />
|
|
133
226
|
<span v-else-if="outputType === 'antd'" class="antd-icon" :data-icon="outputName">
|
|
134
|
-
<component
|
|
135
|
-
:is="AntdIcons[outputName as keyof typeof AntdIcons] as any"
|
|
136
|
-
/>
|
|
227
|
+
<component :is="(AntdIcons[outputName as keyof typeof AntdIcons] as any)" />
|
|
137
228
|
</span>
|
|
138
229
|
<img v-else-if="icon" class="size-1em" :src="icon">
|
|
139
|
-
</
|
|
230
|
+
</component>
|
|
140
231
|
</template>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 不带盒子外观的图标配置。
|
|
3
|
+
* 当未显式设置 `box` 时将使用该形状;并且此时不允许传入 `background` / `radius`。
|
|
4
|
+
*/
|
|
5
|
+
export interface PubinfoIconNotBoxOptions {
|
|
6
|
+
/** 图标名称 */
|
|
7
|
+
name: string
|
|
8
|
+
/** 是否异步加载 */
|
|
9
|
+
async?: boolean
|
|
10
|
+
/** 翻转方向 */
|
|
11
|
+
flip?: 'horizontal' | 'vertical' | 'both'
|
|
12
|
+
/** 旋转角度 */
|
|
13
|
+
rotate?: number
|
|
14
|
+
/** 颜色 */
|
|
15
|
+
color?: string
|
|
16
|
+
/** 尺寸 */
|
|
17
|
+
size?: string | number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 带盒子外观的图标配置。判定条件:存在 `box` 字段。
|
|
22
|
+
* 这里将 `box` 设为必填,用作判别式属性。
|
|
23
|
+
*/
|
|
24
|
+
export interface PubinfoIconBoxOptions extends PubinfoIconNotBoxOptions {
|
|
25
|
+
/** 盒子类型(存在该字段即可触发 Box 选项) */
|
|
26
|
+
box: 'square' | 'prism'
|
|
27
|
+
/** 背景:纯色或渐变 */
|
|
28
|
+
background?: string | {
|
|
29
|
+
from: string
|
|
30
|
+
to: string
|
|
31
|
+
}
|
|
32
|
+
/** 圆角 */
|
|
33
|
+
radius?: number | string
|
|
34
|
+
/** 背景渐变的角度 */
|
|
35
|
+
angle?: number | string
|
|
36
|
+
/** 是否是小 logo */
|
|
37
|
+
small?: boolean
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 根据是否提供 `box` 自动区分两类配置:
|
|
42
|
+
* - 未提供 `box`:禁止出现 `background` 与 `radius`(否则 TS 报错)
|
|
43
|
+
* - 提供 `box`:允许使用盒子相关属性
|
|
44
|
+
*/
|
|
45
|
+
export type PubinfoIconOptions
|
|
46
|
+
= | PubinfoIconBoxOptions
|
|
47
|
+
| (PubinfoIconNotBoxOptions & { box?: undefined, background?: never, radius?: never, angle?: never, small?: false });
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 可用于泛型推断的条件类型:传入的对象类型若含有 `box` 字段,则推断为 Box 版本,否则为普通版本。
|
|
51
|
+
* 一般情况下直接使用 `PubinfoIconOptions` 联合类型即可。
|
|
52
|
+
*/
|
|
53
|
+
export type InferPubinfoIconOptions<T extends { box?: any } | undefined>
|
|
54
|
+
= T extends { box: PubinfoIconBoxOptions['box'] } ? PubinfoIconBoxOptions : PubinfoIconNotBoxOptions;
|
|
@@ -4,5 +4,8 @@ export { default as PageMain } from './PageMain/index.vue';
|
|
|
4
4
|
export { default as PassStrengthValidator } from './PassStrengthValidator/index.vue';
|
|
5
5
|
export { default as PubinfoApp } from './PubinfoApp/index.vue';
|
|
6
6
|
export { default as PubinfoIcon } from './PubinfoIcon/index.vue';
|
|
7
|
+
export { default as PubinfoIconPrismBox } from './PubinfoIcon/PrismBox.vue';
|
|
8
|
+
export { default as PubinfoIconSquareBox } from './PubinfoIcon/SquareBox.vue';
|
|
7
9
|
export { default as PubinfoProvider } from './PubinfoProvider';
|
|
8
|
-
|
|
10
|
+
|
|
11
|
+
export { createPubinfoProvider, pubinfoInjectionKey } from './PubinfoProvider'; ;
|
|
@@ -9,26 +9,11 @@ interface InternalContext {
|
|
|
9
9
|
requestBasic: RequestInstance
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
const g = globalThis as any;
|
|
14
|
-
g.__PUBINFO__ = g.__PUBINFO__ || {};
|
|
15
|
-
if (!g.__PUBINFO__.ctx) {
|
|
16
|
-
g.__PUBINFO__.ctx = createContext<InternalContext>();
|
|
17
|
-
}
|
|
18
|
-
const ctx = g.__PUBINFO__.ctx as ReturnType<typeof createContext<InternalContext>>;
|
|
19
|
-
|
|
20
|
-
// 旧 state 恢复
|
|
21
|
-
if (g.__PUBINFO__.state) {
|
|
22
|
-
try {
|
|
23
|
-
ctx.set(g.__PUBINFO__.state);
|
|
24
|
-
}
|
|
25
|
-
catch {}
|
|
26
|
-
}
|
|
12
|
+
const ctx = createContext<InternalContext>('ctx', { stateKey: 'state' });
|
|
27
13
|
|
|
28
14
|
/** 初始化全局 context */
|
|
29
15
|
export function createPubinfoContext(newState: InternalContext) {
|
|
30
16
|
// 缓存用于 wrapProxy 兜底
|
|
31
|
-
g.__PUBINFO__.state = newState;
|
|
32
17
|
ctx.set(newState);
|
|
33
18
|
// 立即激活 use()
|
|
34
19
|
ctx.use();
|
|
@@ -40,12 +40,11 @@ const useRouteStore = defineStore(
|
|
|
40
40
|
* @returns 面包屑对象
|
|
41
41
|
*/
|
|
42
42
|
function createBreadcrumb(currentRouter: RouteRecordRaw, baseUrl?: RouteRecordRaw['path']): Route.breadcrumb {
|
|
43
|
-
const { title,
|
|
43
|
+
const { title, icon, activeIcon, breadcrumb = true } = defaultTo(currentRouter.meta, {} as RouteMeta);
|
|
44
44
|
const path = defaultTo(baseUrl, currentRouter.path);
|
|
45
45
|
return {
|
|
46
46
|
path,
|
|
47
47
|
title,
|
|
48
|
-
i18n,
|
|
49
48
|
icon,
|
|
50
49
|
activeIcon,
|
|
51
50
|
hide: !breadcrumb,
|
|
@@ -163,7 +162,6 @@ const useRouteStore = defineStore(
|
|
|
163
162
|
|
|
164
163
|
if (routesRaw.value.length) {
|
|
165
164
|
routesRaw.value.forEach((item) => {
|
|
166
|
-
// CLOSE: 开发模式应用的子路由不应该被扁平化到vue-router中
|
|
167
165
|
// 开发模式应用只在TopMode中显示,点击时显示开发状态
|
|
168
166
|
if (get(item, 'meta.isDev', false)) {
|
|
169
167
|
useWarn(`开发模式应用"${item.meta?.title}"的子路由将不会被注册到vue-router中`);
|
|
@@ -304,7 +302,6 @@ const useRouteStore = defineStore(
|
|
|
304
302
|
component: item.component,
|
|
305
303
|
meta: {
|
|
306
304
|
title: item.meta.title,
|
|
307
|
-
i18n: item.meta.i18n,
|
|
308
305
|
sidebar: false,
|
|
309
306
|
breadcrumb: false,
|
|
310
307
|
},
|
|
@@ -364,7 +361,6 @@ const useRouteStore = defineStore(
|
|
|
364
361
|
// 将本地应用替换成静态路由
|
|
365
362
|
if (route.type === RESOURCE_TYPE.APP) {
|
|
366
363
|
const foundStaticRoute = staticRoutes.find(staticRoute => staticRoute.meta?.auth === route.code);
|
|
367
|
-
// CLOSE: 静态应用如果是开发模式,仍然要返回路由对象供TopMode显示
|
|
368
364
|
// 但其子路由不会被注册到vue-router中,这由菜单系统和路由扁平化逻辑处理
|
|
369
365
|
if (foundStaticRoute && get(foundStaticRoute, 'meta.isDev', false)) {
|
|
370
366
|
useWarn(`静态应用"${foundStaticRoute.meta?.title}"处于开发模式,将在TopMode中显示开发状态`);
|
|
@@ -374,10 +370,10 @@ const useRouteStore = defineStore(
|
|
|
374
370
|
|
|
375
371
|
// 将动态应用数据格式化成路由数据
|
|
376
372
|
if (route.type === RESOURCE_TYPE.DYNAMIC_APP) {
|
|
377
|
-
// CLOSE: 动态应用如果是开发模式,仍然要返回路由对象供TopMode显示
|
|
378
373
|
if (route.meta?.isDev) {
|
|
379
374
|
useWarn(`动态应用"${route.name}"处于开发模式,将在TopMode中显示开发状态`);
|
|
380
375
|
}
|
|
376
|
+
|
|
381
377
|
return formatBackRoutes([route])[0] as any;
|
|
382
378
|
}
|
|
383
379
|
|
|
@@ -400,9 +396,6 @@ const useRouteStore = defineStore(
|
|
|
400
396
|
}
|
|
401
397
|
});
|
|
402
398
|
|
|
403
|
-
// FIX-ME routesRaw 最终会被 routes 使用,在 menuStore 中作为生成 allMenus 的数据源。
|
|
404
|
-
// 为了解决这个问题,我们需要重构一下逻辑,想办法将 路由 和 菜单 分开处理。
|
|
405
|
-
// 可以考虑直接让 menuStore 的菜单根据 remoteRoutesRaw 生成。
|
|
406
399
|
routesRaw.value = [...remoteRoutes, ...restStaticRoutes];
|
|
407
400
|
}
|
|
408
401
|
|
|
@@ -37,7 +37,6 @@ const useTabbarStore = defineStore(
|
|
|
37
37
|
routeName: route.name,
|
|
38
38
|
activeMenu: route.meta.activeMenu,
|
|
39
39
|
title: typeof route.meta.title === 'function' ? route.meta.title() : route.meta.title,
|
|
40
|
-
i18n: route.meta.i18n,
|
|
41
40
|
iframe: route.meta.iframe,
|
|
42
41
|
icon: route.meta?.icon ?? route.meta?.breadcrumbNeste?.findLast(item => item.icon)?.icon,
|
|
43
42
|
activeIcon: route.meta?.activeIcon ?? route.meta?.breadcrumbNeste?.findLast(item => item.activeIcon)?.activeIcon,
|
|
@@ -95,7 +94,6 @@ const useTabbarStore = defineStore(
|
|
|
95
94
|
routeName: route.name!,
|
|
96
95
|
activeMenu: meta?.activeMenu,
|
|
97
96
|
title: typeof meta?.title === 'function' ? meta.title() : meta?.title,
|
|
98
|
-
i18n: meta?.i18n,
|
|
99
97
|
iframe: meta?.iframe,
|
|
100
98
|
icon: meta?.icon ?? meta?.breadcrumbNeste?.findLast(item => item.icon)?.icon,
|
|
101
99
|
activeIcon: meta?.activeIcon ?? meta?.breadcrumbNeste?.findLast(item => item.activeIcon)?.activeIcon,
|
|
@@ -121,7 +119,6 @@ const useTabbarStore = defineStore(
|
|
|
121
119
|
findTab.routeName = route.name!;
|
|
122
120
|
findTab.activeMenu = meta?.activeMenu;
|
|
123
121
|
findTab.title = typeof meta?.title === 'function' ? meta.title() : meta?.title;
|
|
124
|
-
findTab.i18n = meta?.i18n;
|
|
125
122
|
findTab.iframe = meta?.iframe;
|
|
126
123
|
findTab.icon = meta?.icon ?? meta?.breadcrumbNeste?.findLast(item => item.icon)?.icon;
|
|
127
124
|
findTab.activeIcon = meta?.activeIcon ?? meta?.breadcrumbNeste?.findLast(item => item.activeIcon)?.activeIcon;
|