Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions dashboard/src/assets/mdi-subset/materialdesignicons-subset.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Auto-generated MDI subset – 230 icons */
/* Auto-generated MDI subset – 231 icons */
/* Do not edit manually. Run: pnpm run subset-icons */

@font-face {
Expand Down Expand Up @@ -692,6 +692,14 @@
content: "\F03F6";
}

.mdi-pin::before {
content: "\F0403";
}

.mdi-pin-outline::before {
content: "\F0931";
}

.mdi-play::before {
content: "\F040A";
}
Expand Down Expand Up @@ -720,10 +728,6 @@
content: "\F0A66";
}

.mdi-qrcode::before {
content: "\F0432";
}

.mdi-refresh::before {
content: "\F0450";
}
Expand Down
Binary file not shown.
Binary file not shown.
183 changes: 183 additions & 0 deletions dashboard/src/components/extension/PinnedPluginItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
<script setup>
import { computed } from 'vue';
import defaultPluginIcon from "@/assets/images/plugin_icon.png";

const props = defineProps({
plugin: {
type: Object,
required: true
},
isPinned: {
type: Boolean,
default: false
},
tm: {
type: Function,
required: true
},
dragged: {
type: Boolean,
default: false
}
});

const emit = defineEmits([
'toggle-pin',
'view-readme',
'open-config',
'reload',
'update',
'show-info',
'uninstall',
'dragstart',
'dragover',
'dragenter',
'dragend',
'drop'
]);

const handlePinnedImgError = (e) => {
e.target.src = defaultPluginIcon;
};
</script>

<template>
<div
class="pinned-item pinned-card-wrapper"
:class="{ 'is-dragging': dragged }"
style="position:relative;"
draggable="true"
@dragstart="$emit('dragstart')"
@dragover.prevent="$emit('dragover', $event)"
@dragenter.prevent="$emit('dragenter', $event)"
@dragend="$emit('dragend', $event)"
@drop="$emit('drop', $event)"
>
Comment on lines +45 to +55
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

style="position:relative;" 这个内联样式是多余的。该元素已经应用了 pinned-card-wrapper 类 (在第46行),而这个类在组件的样式中 (第157行) 已经定义了 position: relative;。为了代码整洁,可以安全地移除这个内联样式。

  <div
    class="pinned-item pinned-card-wrapper"
    :class="{ 'is-dragging': dragged }"
    draggable="true"
    @dragstart="$emit('dragstart')"
    @dragover.prevent="$emit('dragover', $event)"
    @dragenter.prevent="$emit('dragenter', $event)"
    @dragend="$emit('dragend', $event)"
    @drop="$emit('drop', $event)"
  >

<v-menu offset-y>
<template #activator="{ props: menuProps }">
<v-avatar
v-bind="menuProps"
size="72"
class="pinned-avatar activator-avatar"
:title="plugin.display_name || plugin.name"
>
<img
:src="(typeof plugin.logo === 'string' && plugin.logo.trim()) ? plugin.logo : defaultPluginIcon"
:alt="plugin.name"
@error="handlePinnedImgError"
/>
</v-avatar>
</template>

<v-card>
<v-card-text class="d-flex" style="gap:8px; padding:12px;">
<v-tooltip location="top" :text="tm('buttons.viewDocs')">
<template #activator="{ props: a }">
<v-btn v-bind="a" icon size="small" variant="tonal" color="info" @click.stop="$emit('view-readme', plugin)">
<v-icon>mdi-book-open-page-variant</v-icon>
</v-btn>
</template>
</v-tooltip>

<v-tooltip location="top" :text="tm('card.actions.pluginConfig')">
<template #activator="{ props: a }">
<v-btn v-bind="a" icon size="small" variant="tonal" color="primary" @click.stop="$emit('open-config', plugin.name)">
<v-icon>mdi-cog</v-icon>
</v-btn>
</template>
</v-tooltip>

<v-tooltip location="top" :text="tm('card.actions.reloadPlugin')">
<template #activator="{ props: a }">
<v-btn v-bind="a" icon size="small" variant="tonal" color="primary" @click.stop="$emit('reload', plugin.name)">
<v-icon>mdi-refresh</v-icon>
</v-btn>
</template>
</v-tooltip>

<v-tooltip location="top" :text="tm('buttons.update')">
<template #activator="{ props: a }">
<v-btn v-bind="a" icon size="small" variant="tonal" color="warning" @click.stop="$emit('update', plugin.name)">
<v-icon>mdi-update</v-icon>
</v-btn>
</template>
</v-tooltip>

<v-tooltip location="top" :text="tm('buttons.viewInfo')">
<template #activator="{ props: a }">
<v-btn v-bind="a" icon size="small" variant="tonal" color="secondary" @click.stop="$emit('show-info', plugin)">
<v-icon>mdi-information</v-icon>
</v-btn>
</template>
</v-tooltip>

<v-tooltip location="top" :text="tm('buttons.uninstall')">
<template #activator="{ props: a }">
<v-btn v-bind="a" icon size="small" variant="tonal" color="error" @click.stop="$emit('uninstall', plugin.name)" v-if="!plugin.reserved">
<v-icon>mdi-delete</v-icon>
</v-btn>
</template>
</v-tooltip>
</v-card-text>
</v-card>
</v-menu>

<v-btn
icon
size="small"
class="pinned-pin-btn"
:color="isPinned ? 'primary' : 'secondary'"
@click.stop="$emit('toggle-pin', plugin)"
:title="isPinned ? tm('buttons.unpin') : tm('buttons.pin')"
style="position:absolute; top:6px; right:6px; min-width:22px; width:22px; height:22px;"
>
<v-icon size="14">{{ isPinned ? 'mdi-pin' : 'mdi-pin-outline' }}</v-icon>
</v-btn>
Comment on lines +125 to +135
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

为了提高可维护性和分离关注点,最好将内联样式移至 CSS 类中。这个置顶按钮的样式可以移到已有的 pinned-pin-btn 类里。

    <v-btn
      icon
      size="small"
      class="pinned-pin-btn"
      :color="isPinned ? 'primary' : 'secondary'"
      @click.stop="$emit('toggle-pin', plugin)"
      :title="isPinned ? tm('buttons.unpin') : tm('buttons.pin')"
    >
      <v-icon size="14">{{ isPinned ? 'mdi-pin' : 'mdi-pin-outline' }}</v-icon>
    </v-btn>

</div>
</template>

<style scoped>
.pinned-avatar {
display: inline-flex;
width: 100%;
height: 100%;
overflow: hidden;
cursor: pointer;
border-radius: 12px;
}

.pinned-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}

.pinned-card-wrapper {
position: relative;
display: inline-block;
width: 72px;
height: 72px;
}

.pinned-item {
display: inline-flex;
align-items: center;
justify-content: center;
transition: transform 0.2s ease, opacity 0.2s ease;
}

.is-dragging {
opacity: 0.5;
transform: scale(0.95);
cursor: grabbing;
}

[draggable="true"] {
cursor: grab;
}

[draggable="true"]:active {
cursor: grabbing;
}
</style>
8 changes: 4 additions & 4 deletions dashboard/src/components/folder/BaseFolderCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,26 @@
<template v-slot:prepend>
<v-icon size="small">mdi-folder-open</v-icon>
</template>
<v-list-item-title>{{ labels.open }}</v-list-item-title>
<v-list-item-title>{{ mergedLabels.open }}</v-list-item-title>
</v-list-item>
<v-list-item @click.stop="$emit('rename')">
<template v-slot:prepend>
<v-icon size="small">mdi-pencil</v-icon>
</template>
<v-list-item-title>{{ labels.rename }}</v-list-item-title>
<v-list-item-title>{{ mergedLabels.rename }}</v-list-item-title>
</v-list-item>
<v-list-item @click.stop="$emit('move')">
<template v-slot:prepend>
<v-icon size="small">mdi-folder-move</v-icon>
</template>
<v-list-item-title>{{ labels.moveTo }}</v-list-item-title>
<v-list-item-title>{{ mergedLabels.moveTo }}</v-list-item-title>
</v-list-item>
<v-divider class="my-1" />
<v-list-item @click.stop="$emit('delete')" class="text-error">
<template v-slot:prepend>
<v-icon size="small" color="error">mdi-delete</v-icon>
</template>
<v-list-item-title>{{ labels.delete }}</v-list-item-title>
<v-list-item-title>{{ mergedLabels.delete }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
Expand Down
53 changes: 42 additions & 11 deletions dashboard/src/components/shared/ExtensionCard.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, computed, inject, watch } from "vue";
import { ref, computed, inject, watch, useAttrs } from "vue";
import { useCustomizerStore } from "@/stores/customizer";
import { useModuleI18n } from "@/i18n/composables";
import { getPlatformDisplayName, getPlatformIcon } from "@/utils/platformUtils";
Expand All @@ -13,6 +13,10 @@ const props = defineProps({
type: Object,
required: true,
},
pinned: {
type: Boolean,
default: false,
},
marketMode: {
type: Boolean,
default: false,
Expand All @@ -31,6 +35,7 @@ const emit = defineEmits([
"install",
"uninstall",
"toggle-activation",
"toggle-pin",
"view-handlers",
"view-readme",
"view-changelog",
Expand All @@ -39,6 +44,8 @@ const emit = defineEmits([
const reveal = ref(false);
const showUninstallDialog = ref(false);

const attrs = useAttrs();

// 国际化
const { tm } = useModuleI18n("features/extension");

Expand Down Expand Up @@ -114,6 +121,11 @@ const toggleActivation = () => {
emit("toggle-activation", props.extension);
};

const togglePin = (e?: Event) => {
if (e) e.stopPropagation();
emit("toggle-pin", props.extension);
};

const viewHandlers = () => {
emit("view-handlers", props.extension);
};
Expand All @@ -130,6 +142,7 @@ const viewChangelog = () => {

<template>
<v-card
v-bind="attrs"
class="mx-auto d-flex flex-column h-100"
elevation="0"
height="100%"
Expand Down Expand Up @@ -203,16 +216,34 @@ const viewChangelog = () => {
<template v-if="!marketMode">
<v-tooltip location="left">
<template v-slot:activator="{ props: tooltipProps }">
<div v-bind="tooltipProps" class="extension-switch-wrap" @click.stop>
<v-switch
:model-value="extension.activated"
color="success"
density="compact"
hide-details
inset
@update:model-value="toggleActivation"
></v-switch>
</div>
<div class="extension-switch-wrap" @click.stop>
<div v-bind="tooltipProps" style="display:inline-flex; align-items:center;">
<v-switch
:model-value="extension.activated"
color="success"
density="compact"
hide-details
inset
@update:model-value="toggleActivation"
></v-switch>
</div>

<v-tooltip location="top" :text="pinned ? tm('buttons.unpin') : tm('buttons.pin')">
<template #activator="{ props: pinProps }">
<v-btn
v-bind="pinProps"
icon
size="small"
variant="tonal"
:color="pinned ? 'primary' : 'secondary'"
class="ml-2"
@click.stop="togglePin"
>
<v-icon size="18">{{ pinned ? 'mdi-pin' : 'mdi-pin-outline' }}</v-icon>
</v-btn>
</template>
</v-tooltip>
</div>
</template>
<span>{{
extension.activated ? tm("buttons.disable") : tm("buttons.enable")
Expand Down
5 changes: 4 additions & 1 deletion dashboard/src/i18n/locales/zh-CN/features/extension.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"handlersOperation": "管理行为"
},
"titles": {
"installedAstrBotPlugins": "已安装的 AstrBot 插件"
"installedAstrBotPlugins": "已安装的 AstrBot 插件",
"pinnedPlugins": "置顶插件"
},
"failedPlugins": {
"title": "加载失败插件({count})",
Expand Down Expand Up @@ -53,6 +54,8 @@
"refresh": "刷新",
"updateAll": "更新全部插件",
"deleteSource": "删除源",
"pin": "置顶",
"unpin": "取消置顶",
"reshuffle": "随机一发"
},
"status": {
Expand Down
Loading
Loading