Skip to content

Commit fa1d1e6

Browse files
authored
feat(dashboard): improve plugin platform support display and mobile accessibility (#5271)
* feat(dashboard): improve plugin platform support display and mobile accessibility - Replace hover-based tooltips with interactive click menus for platform support information. - Fix mobile touch issues by introducing explicit state control for status capsules. - Enhance UI aesthetics with platform-specific icons and a structured vertical list layout. - Add dynamic chevron icons to provide clear visual cues for expandable content. * refactor(dashboard): refactor market card with computed properties for performance * refactor(dashboard): unify plugin platform support UI with new reusable chip component - Create shared 'PluginPlatformChip' component to encapsulate platform meta display. - Fix mobile interaction bugs by simplifying menu triggers and event handling. - Add stacked platform icon previews and dynamic chevron indicators within capsules. - Improve information hierarchy using structured vertical lists for platform details. - Optimize rendering efficiency with computed properties across both card views.
1 parent a404436 commit fa1d1e6

File tree

3 files changed

+138
-50
lines changed

3 files changed

+138
-50
lines changed

dashboard/src/components/extension/MarketPluginCard.vue

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
<script setup>
2+
import { ref, computed } from "vue";
23
import { useModuleI18n } from "@/i18n/composables";
3-
import { getPlatformDisplayName } from "@/utils/platformUtils";
4+
import PluginPlatformChip from "@/components/shared/PluginPlatformChip.vue";
45
56
const { tm } = useModuleI18n("features/extension");
67
7-
defineProps({
8+
const props = defineProps({
89
plugin: {
910
type: Object,
1011
required: true,
@@ -26,12 +27,6 @@ const normalizePlatformList = (platforms) => {
2627
return platforms.filter((item) => typeof item === "string");
2728
};
2829
29-
const getPlatformDisplayList = (platforms) => {
30-
return normalizePlatformList(platforms).map((platformId) =>
31-
getPlatformDisplayName(platformId),
32-
);
33-
};
34-
3530
const handleInstall = (plugin) => {
3631
emit("install", plugin);
3732
};
@@ -165,9 +160,9 @@ const handleInstall = (plugin) => {
165160
</div>
166161

167162
<div
168-
v-if="plugin.astrbot_version || normalizePlatformList(plugin.support_platforms).length"
163+
v-if="plugin.astrbot_version || platformDisplayList.length"
169164
class="d-flex align-center flex-wrap"
170-
style="gap: 4px; margin-top: 4px; margin-bottom: 4px;"
165+
style="gap: 4px; margin-top: 4px; margin-bottom: 4px"
171166
>
172167
<v-chip
173168
v-if="plugin.astrbot_version"
@@ -178,26 +173,11 @@ const handleInstall = (plugin) => {
178173
>
179174
AstrBot: {{ plugin.astrbot_version }}
180175
</v-chip>
181-
<v-chip
182-
v-if="normalizePlatformList(plugin.support_platforms).length"
176+
<PluginPlatformChip
177+
:platforms="plugin.support_platforms"
183178
size="x-small"
184-
color="info"
185-
variant="outlined"
186-
style="height: 20px"
187-
>
188-
<v-tooltip location="top">
189-
<template v-slot:activator="{ props: tooltipProps }">
190-
<span v-bind="tooltipProps">
191-
{{
192-
tm("card.status.supportPlatformsCount", {
193-
count: getPlatformDisplayList(plugin.support_platforms).length,
194-
})
195-
}}
196-
</span>
197-
</template>
198-
<span>{{ getPlatformDisplayList(plugin.support_platforms).join(", ") }}</span>
199-
</v-tooltip>
200-
</v-chip>
179+
:chip-style="{ height: '20px' }"
180+
/>
201181
</div>
202182

203183
<div class="d-flex align-center" style="gap: 8px; margin-top: auto">

dashboard/src/components/shared/ExtensionCard.vue

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
import { ref, computed, inject } from "vue";
33
import { useCustomizerStore } from "@/stores/customizer";
44
import { useModuleI18n } from "@/i18n/composables";
5-
import { getPlatformDisplayName } from "@/utils/platformUtils";
5+
import { getPlatformDisplayName, getPlatformIcon } from "@/utils/platformUtils";
66
import UninstallConfirmDialog from "./UninstallConfirmDialog.vue";
7+
import PluginPlatformChip from "./PluginPlatformChip.vue";
78
89
const props = defineProps({
910
extension: {
@@ -336,27 +337,10 @@ const viewChangelog = () => {
336337
>
337338
{{ tag === "danger" ? tm("tags.danger") : tag }}
338339
</v-chip>
339-
<v-chip
340-
v-if="supportPlatforms.length"
341-
color="info"
342-
variant="outlined"
343-
label
344-
size="small"
340+
<PluginPlatformChip
341+
:platforms="supportPlatforms"
345342
class="ml-2"
346-
>
347-
<v-tooltip location="top">
348-
<template v-slot:activator="{ props: tooltipProps }">
349-
<span v-bind="tooltipProps">
350-
{{
351-
tm("card.status.supportPlatformsCount", {
352-
count: supportPlatformDisplayNames.length,
353-
})
354-
}}
355-
</span>
356-
</template>
357-
<span>{{ supportPlatformDisplayNames.join(", ") }}</span>
358-
</v-tooltip>
359-
</v-chip>
343+
/>
360344
<v-chip
361345
v-if="astrbotVersionRequirement"
362346
color="secondary"
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<script setup lang="ts">
2+
import { ref, computed } from "vue";
3+
import { getPlatformDisplayName, getPlatformIcon } from "@/utils/platformUtils";
4+
import { useModuleI18n } from "@/i18n/composables";
5+
6+
const props = defineProps({
7+
platforms: {
8+
type: Array,
9+
default: () => [],
10+
},
11+
size: {
12+
type: String,
13+
default: "small",
14+
},
15+
chipStyle: {
16+
type: Object,
17+
default: () => ({}),
18+
},
19+
});
20+
21+
const { tm } = useModuleI18n("features/extension");
22+
23+
const showMenu = ref(false);
24+
25+
const platformDetails = computed(() => {
26+
if (!Array.isArray(props.platforms)) return [];
27+
return props.platforms
28+
.filter((item) => typeof item === "string")
29+
.map((platformId) => ({
30+
name: getPlatformDisplayName(platformId as string),
31+
icon: getPlatformIcon(platformId as string),
32+
}));
33+
});
34+
</script>
35+
36+
<template>
37+
<div class="d-inline-block">
38+
<v-chip
39+
v-if="platformDetails.length"
40+
color="info"
41+
variant="outlined"
42+
label
43+
:size="size"
44+
class="plugin-platform-chip"
45+
:style="{ cursor: 'pointer', ...chipStyle }"
46+
@click.stop="showMenu = !showMenu"
47+
>
48+
<div class="d-flex align-center" style="gap: 2px">
49+
<!-- 显示图标,最多 5 个 -->
50+
<div class="d-flex align-center mr-1" v-if="platformDetails.some(p => p.icon)">
51+
<v-avatar
52+
v-for="(platform, index) in platformDetails.slice(0, 5)"
53+
:key="index"
54+
:size="size === 'x-small' ? 12 : 14"
55+
class="platform-mini-icon"
56+
:style="{ marginLeft: index > 0 ? '-4px' : '0', zIndex: 10 - index }"
57+
>
58+
<v-img v-if="platform.icon" :src="platform.icon"></v-img>
59+
<v-icon v-else icon="mdi-circle-small" :size="size === 'x-small' ? 8 : 10"></v-icon>
60+
</v-avatar>
61+
</div>
62+
63+
<span class="text-caption font-weight-bold">
64+
{{
65+
tm("card.status.supportPlatformsCount", {
66+
count: platformDetails.length,
67+
})
68+
}}
69+
</span>
70+
71+
<v-icon
72+
:icon="showMenu ? 'mdi-chevron-up' : 'mdi-chevron-down'"
73+
:size="size === 'x-small' ? 14 : 16"
74+
class="ml-n1"
75+
></v-icon>
76+
</div>
77+
78+
<v-menu
79+
v-model="showMenu"
80+
activator="parent"
81+
location="top"
82+
:close-on-content-click="false"
83+
transition="scale-transition"
84+
open-on-hover
85+
>
86+
<v-list density="compact" border elevation="12" class="rounded-lg pa-1">
87+
<v-list-item
88+
v-for="platform in platformDetails"
89+
:key="platform.name"
90+
min-height="24"
91+
class="px-2"
92+
>
93+
<template v-slot:prepend>
94+
<v-avatar size="14" class="mr-2" v-if="platform.icon">
95+
<v-img :src="platform.icon"></v-img>
96+
</v-avatar>
97+
<v-icon v-else icon="mdi-platform" size="12" class="mr-2"></v-icon>
98+
</template>
99+
<v-list-item-title class="text-caption font-weight-bold" style="font-size: 0.75rem !important">
100+
{{ platform.name }}
101+
</v-list-item-title>
102+
</v-list-item>
103+
</v-list>
104+
</v-menu>
105+
</v-chip>
106+
</div>
107+
</template>
108+
109+
<style scoped>
110+
.plugin-platform-chip {
111+
padding-left: 6px !important;
112+
padding-right: 4px !important;
113+
transition: all 0.2s ease;
114+
}
115+
116+
.platform-mini-icon {
117+
border: 1px solid rgba(var(--v-theme-info), 0.3);
118+
background: rgba(var(--v-theme-surface));
119+
}
120+
121+
.plugin-platform-chip:hover {
122+
background: rgba(var(--v-theme-info), 0.08);
123+
}
124+
</style>

0 commit comments

Comments
 (0)