✨ feat: 优化了插件卡片的 UI,插件卡片支持显示 logo
This commit is contained in:
@@ -207,7 +207,7 @@ class PluginManager:
|
||||
|
||||
await self._unbind_plugin(smd.name, specified_module_path)
|
||||
|
||||
await self.load(specified_module_path)
|
||||
return await self.load(specified_module_path)
|
||||
|
||||
async def load(self, specified_module_path=None, specified_dir_name=None):
|
||||
"""载入插件。
|
||||
|
||||
@@ -1,32 +1,204 @@
|
||||
<script setup lang="ts">
|
||||
import { defineProps, defineEmits, ref, computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
link: String,
|
||||
logo: String,
|
||||
has_update: Boolean,
|
||||
activated: Boolean,
|
||||
extension: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
marketMode: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
// 定义要发送到父组件的事件
|
||||
const emit = defineEmits([
|
||||
'configure',
|
||||
'update',
|
||||
'reload',
|
||||
'install',
|
||||
'uninstall',
|
||||
'toggle-activation',
|
||||
'view-handlers'
|
||||
]);
|
||||
|
||||
const open = (link: string | undefined) => {
|
||||
window.open(link, '_blank');
|
||||
};
|
||||
|
||||
const reveal = ref(false);
|
||||
|
||||
// 检查是否有更新可用
|
||||
const hasUpdate = computed(() => {
|
||||
if (!props.extension.online_version || !props.extension.version) return false;
|
||||
return props.extension.online_version !== props.extension.version;
|
||||
});
|
||||
|
||||
// 操作函数
|
||||
const configure = () => {
|
||||
emit('configure', props.extension);
|
||||
};
|
||||
|
||||
const updateExtension = () => {
|
||||
emit('update', props.extension);
|
||||
};
|
||||
|
||||
const reloadExtension = () => {
|
||||
emit('reload', props.extension);
|
||||
};
|
||||
|
||||
const uninstallExtension = () => {
|
||||
emit('uninstall', props.extension);
|
||||
};
|
||||
|
||||
const toggleActivation = () => {
|
||||
emit('toggle-activation', props.extension);
|
||||
};
|
||||
|
||||
const viewHandlers = () => {
|
||||
emit('view-handlers', props.extension);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-card variant="outlined" elevation="0" class="withbg">
|
||||
<v-card-item style="padding: 10px 12px">
|
||||
<div class="d-sm-flex align-center justify-space-between">
|
||||
<img v-if="logo" :src="logo" alt="logo" style="width: 40px; height: 40px; margin-right: 8px;">
|
||||
<v-card-title style="font-size: 15px; max-width: 70%">{{ props.title }}</v-card-title>
|
||||
<v-spacer></v-spacer>
|
||||
<v-icon color="success" v-if="!activated">mdi-cancel</v-icon>
|
||||
<v-icon color="success" v-if="has_update">mdi-arrow-up-bold</v-icon>
|
||||
<v-btn size="small" text="Read" variant="flat" border @click="open(props.link)">帮助</v-btn>
|
||||
<v-card class="mx-auto d-flex flex-column" :style="{ height: $vuetify.display.xs ? '250px' : '220px' }">
|
||||
<v-card-text style="padding: 16px; padding-bottom: 0px; display: flex; justify-content: space-between;">
|
||||
|
||||
<div class="flex-grow-1">
|
||||
<div>{{ extension.author }} /</div>
|
||||
|
||||
<p class="text-h3 font-weight-black" :class="{ 'text-h4': $vuetify.display.xs }">
|
||||
{{ extension.name }}
|
||||
<v-tooltip location="top" v-if="hasUpdate && !marketMode">
|
||||
<template v-slot:activator="{ props: tooltipProps }">
|
||||
<v-icon v-bind="tooltipProps" color="warning" class="ml-2" icon="mdi-update" size="small"></v-icon>
|
||||
</template>
|
||||
<span>有新版本可用: {{ extension.online_version }}</span>
|
||||
</v-tooltip>
|
||||
<v-tooltip location="top" v-if="!extension.activated && !marketMode">
|
||||
<template v-slot:activator="{ props: tooltipProps }">
|
||||
<v-icon v-bind="tooltipProps" color="error" class="ml-2" icon="mdi-cancel" size="small"></v-icon>
|
||||
</template>
|
||||
<span>该插件已经被禁用</span>
|
||||
</v-tooltip>
|
||||
</p>
|
||||
|
||||
<div class="mt-1 d-flex flex-wrap">
|
||||
<v-chip color="primary" label size="small">
|
||||
<v-icon icon="mdi-source-branch" start></v-icon>
|
||||
{{ extension.version }}
|
||||
</v-chip>
|
||||
<v-chip v-if="hasUpdate" color="warning" label size="small" class="ml-2">
|
||||
<v-icon icon="mdi-arrow-up-bold" start></v-icon>
|
||||
{{ extension.online_version }}
|
||||
</v-chip>
|
||||
<v-chip color="primary" label size="small" class="ml-2" v-if="extension.handlers?.length">
|
||||
<v-icon icon="mdi-cogs" start></v-icon>
|
||||
{{ extension.handlers?.length }}个行为
|
||||
</v-chip>
|
||||
</div>
|
||||
|
||||
<div class="text-medium-emphasis mt-2" :class="{ 'text-caption': $vuetify.display.xs }">
|
||||
{{ extension.desc }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="extension-image-container" v-if="extension.logo">
|
||||
<img
|
||||
:src="extension.logo"
|
||||
:style="{
|
||||
height: $vuetify.display.xs ? '75px' : '100px',
|
||||
width: $vuetify.display.xs ? '75px' : '100px',
|
||||
borderRadius: '8px',
|
||||
objectFit: 'cover',
|
||||
objectPosition: 'center'
|
||||
}"
|
||||
alt="logo"
|
||||
/>
|
||||
</div>
|
||||
</v-card-item>
|
||||
<v-divider></v-divider>
|
||||
<v-card-text style="padding: 16px;">
|
||||
<slot />
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions style="padding: 0px; margin-top: auto;">
|
||||
<v-btn color="teal-accent-4" text="帮助" variant="text" @click="open(extension.repo)"></v-btn>
|
||||
<v-btn v-if="!marketMode" color="teal-accent-4" text="操作" variant="text" @click="reveal = true"></v-btn>
|
||||
<v-btn v-if="marketMode && !extension?.installed" color="teal-accent-4" text="安装" variant="text" @click="emit('install', extension)"></v-btn>
|
||||
<v-btn v-if="marketMode && extension?.installed" color="teal-accent-4" text="已安装" variant="text" disabled></v-btn>
|
||||
</v-card-actions>
|
||||
|
||||
<v-expand-transition v-if="!marketMode">
|
||||
<v-card v-if="reveal" class="position-absolute w-100" height="100%"
|
||||
style="bottom: 0; display: flex; flex-direction: column;">
|
||||
<v-card-text style="overflow-y: auto;">
|
||||
<div class="d-flex align-center mb-4">
|
||||
<img
|
||||
v-if="extension.logo"
|
||||
:src="extension.logo"
|
||||
style="height: 50px; width: 50px; border-radius: 8px; margin-right: 16px;"
|
||||
alt="扩展图标"
|
||||
/>
|
||||
<h3>{{ extension.name }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="mt-4" :style="{
|
||||
justifyContent: 'center',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexWrap: 'wrap',
|
||||
gap: '8px',
|
||||
flexDirection: $vuetify.display.xs ? 'column' : 'row'
|
||||
}">
|
||||
<v-btn prepend-icon="mdi-cog" color="primary" variant="tonal" @click="configure"
|
||||
:block="$vuetify.display.xs">
|
||||
插件配置
|
||||
</v-btn>
|
||||
|
||||
<v-btn prepend-icon="mdi-delete" color="error" variant="tonal" @click="uninstallExtension"
|
||||
:block="$vuetify.display.xs">
|
||||
卸载插件
|
||||
</v-btn>
|
||||
|
||||
<v-btn prepend-icon="mdi-reload" color="primary" variant="tonal" @click="reloadExtension"
|
||||
:block="$vuetify.display.xs">
|
||||
重载插件
|
||||
</v-btn>
|
||||
|
||||
<v-btn :prepend-icon="extension.activated ? 'mdi-cancel' : 'mdi-check-circle'"
|
||||
:color="extension.activated ? 'error' : 'success'" variant="tonal" @click="toggleActivation"
|
||||
:block="$vuetify.display.xs">
|
||||
{{ extension.activated ? '禁用' : '启用' }}插件
|
||||
</v-btn>
|
||||
|
||||
<v-btn prepend-icon="mdi-cogs" color="info" variant="tonal" @click="viewHandlers"
|
||||
:block="$vuetify.display.xs">
|
||||
查看行为 ({{ extension.handlers.length }})
|
||||
</v-btn>
|
||||
|
||||
<v-btn prepend-icon="mdi-update" color="primary" variant="tonal" :disabled="!hasUpdate"
|
||||
@click="updateExtension" :block="$vuetify.display.xs">
|
||||
更新到 {{ extension.online_version || extension.version }}
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions class="pt-0 d-flex justify-center">
|
||||
<v-btn color="teal-accent-4" text="返回" variant="text" @click="reveal = false"></v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-expand-transition>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.extension-image-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.extension-image-container {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -52,7 +52,7 @@ import { useCommonStore } from '@/stores/common';
|
||||
<template v-if="isListView">
|
||||
<v-col cols="12" md="12">
|
||||
<v-data-table :headers="pluginMarketHeaders" :items="pluginMarketData" item-key="name" :loading="loading_"
|
||||
v-model:search="marketSearch" :filter-keys="['name', 'desc']">
|
||||
v-model:search="marketSearch" :filter-keys="['name', 'desc', 'author']">
|
||||
<template v-slot:item.name="{ item }">
|
||||
<span v-if="item?.repo"><a :href="item?.repo"
|
||||
style="color: #000; text-decoration:none">{{
|
||||
@@ -79,21 +79,8 @@ import { useCommonStore } from '@/stores/common';
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-row style="margin: 8px;">
|
||||
<v-col cols="12" md="6" lg="3" v-for="plugin in filteredPluginMarketData">
|
||||
<ExtensionCard :key="plugin.name" :title="plugin.name" :link="plugin.repo"
|
||||
style="margin-bottom: 4px;">
|
||||
<div style="min-height: 130px; max-height: 130px; overflow: hidden;">
|
||||
<p style="font-weight: bold;">By @{{ plugin.author }}</p>
|
||||
{{ plugin.desc }}
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-center gap-2">
|
||||
<v-btn v-if="!plugin.installed" class="text-none mr-2" size="small" text="Read"
|
||||
variant="flat" border
|
||||
@click="extension_url = plugin.repo; newExtension()">安装</v-btn>
|
||||
<v-btn v-else class="text-none mr-2" size="small" text="Read" variant="flat" border
|
||||
disabled>已安装</v-btn>
|
||||
</div>
|
||||
<v-col cols="12" md="6" lg="6" v-for="plugin in filteredPluginMarketData">
|
||||
<ExtensionCard :extension="plugin" market-mode="true">
|
||||
</ExtensionCard>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
@@ -267,40 +267,14 @@ onMounted(async () => {
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" md="6" lg="3" v-for="extension in filteredExtensions" :key="extension.name">
|
||||
<ExtensionCard :title="extension.name" :link="extension.repo" :logo="extension?.logo"
|
||||
:has_update="extension.has_update" style="margin-bottom: 4px;" :activated="extension.activated">
|
||||
<div style="min-height: 140px; max-height: 140px; overflow: auto;">
|
||||
<div>
|
||||
<span style="font-weight: bold;">By @{{ extension.author }}</span>
|
||||
<span> | {{ extension.handlers.length }} 个行为</span>
|
||||
</div>
|
||||
<div>
|
||||
当前: <v-chip size="small" color="primary">{{ extension.version }}</v-chip>
|
||||
<span v-if="extension.online_version">
|
||||
| 最新: <v-chip size="small" color="primary">{{ extension.online_version }}</v-chip>
|
||||
</span>
|
||||
<span v-if="extension.has_update" style="font-weight: bold;">有更新</span>
|
||||
</div>
|
||||
<p style="margin-top: 8px;">{{ extension.desc }}</p>
|
||||
<a style="font-size: 12px; cursor: pointer; text-decoration: underline; color: #555;"
|
||||
@click="reloadPlugin(extension.name)">重载插件</a>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-center gap-2" style="overflow-x: auto;">
|
||||
<v-btn v-if="!extension.reserved" class="text-none mr-2" size="small" variant="flat" border
|
||||
@click="openExtensionConfig(extension.name)">配置</v-btn>
|
||||
<v-btn v-if="!extension.reserved" class="text-none mr-2" size="small" variant="flat" border
|
||||
@click="updateExtension(extension.name)">更新</v-btn>
|
||||
<v-btn v-if="!extension.reserved" class="text-none mr-2" size="small" variant="flat" border
|
||||
@click="uninstallExtension(extension.name)">卸载</v-btn>
|
||||
<v-btn class="text-none mr-2" size="small" variant="flat" border v-if="extension.activated"
|
||||
@click="pluginOff(extension)">禁用</v-btn>
|
||||
<v-btn class="text-none mr-2" size="small" variant="flat" border v-else
|
||||
@click="pluginOn(extension)">启用</v-btn>
|
||||
<v-btn class="text-none mr-2" size="small" variant="flat" border
|
||||
@click="showPluginInfo(extension)">行为</v-btn>
|
||||
</div>
|
||||
<v-col cols="10" md="6" lg="6" v-for="extension in filteredExtensions" :key="extension.name">
|
||||
<ExtensionCard :extension="extension"
|
||||
@configure="openExtensionConfig(extension.name)"
|
||||
@uninstall="uninstallExtension(extension.name)"
|
||||
@update="updateExtension(extension.name)"
|
||||
@reload="reloadPlugin(extension.name)"
|
||||
@toggle-activation="extension.activated ? pluginOff(extension) : pluginOn(extension)"
|
||||
@view-handlers="showPluginInfo(extension)">
|
||||
</ExtensionCard>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
@@ -71,16 +71,14 @@ class Main(star.Star):
|
||||
dashboard_version = await get_dashboard_version()
|
||||
|
||||
msg = f"""AstrBot v{VERSION}(WebUI: {dashboard_version})
|
||||
AstrBot 指令:
|
||||
内置指令:
|
||||
[System]
|
||||
/plugin: 查看插件、插件帮助
|
||||
/t2i: 开关文本转图片
|
||||
/tts: 开关文本转语音
|
||||
/sid: 获取会话 ID
|
||||
/op <admin_id>: 授权管理员(op)
|
||||
/deop <admin_id>: 取消管理员(op)
|
||||
/wl <sid>: 添加白名单(op)
|
||||
/dwl <sid>: 删除白名单(op)
|
||||
/op: 管理员
|
||||
/wl: 白名单
|
||||
/dashboard_update: 更新管理面板(op)
|
||||
/alter_cmd: 设置指令权限(op)
|
||||
|
||||
@@ -269,8 +267,11 @@ UID: {user_id} 此 ID 可用于设置管理员。/op <UID> 授权管理员, /deo
|
||||
|
||||
@filter.permission_type(filter.PermissionType.ADMIN)
|
||||
@filter.command("op")
|
||||
async def op(self, event: AstrMessageEvent, admin_id: str):
|
||||
async def op(self, event: AstrMessageEvent, admin_id: str=None):
|
||||
"""授权管理员。op <admin_id>"""
|
||||
if admin_id is None:
|
||||
event.set_result(MessageEventResult().message("使用方法: /op <id> 授权管理员;/deop <id> 取消管理员。可通过 /sid 获取 ID。"))
|
||||
return
|
||||
self.context.get_config()["admins_id"].append(admin_id)
|
||||
self.context.get_config().save_config()
|
||||
event.set_result(MessageEventResult().message("授权成功。"))
|
||||
@@ -290,8 +291,10 @@ UID: {user_id} 此 ID 可用于设置管理员。/op <UID> 授权管理员, /deo
|
||||
|
||||
@filter.permission_type(filter.PermissionType.ADMIN)
|
||||
@filter.command("wl")
|
||||
async def wl(self, event: AstrMessageEvent, sid: str):
|
||||
async def wl(self, event: AstrMessageEvent, sid: str=None):
|
||||
"""添加白名单。wl <sid>"""
|
||||
if sid is None:
|
||||
event.set_result(MessageEventResult().message("使用方法: /wl <id> 添加白名单;/dwl <id> 删除白名单。可通过 /sid 获取 ID。"))
|
||||
self.context.get_config()["platform_settings"]["id_whitelist"].append(sid)
|
||||
self.context.get_config().save_config()
|
||||
event.set_result(MessageEventResult().message("添加白名单成功。"))
|
||||
|
||||
Reference in New Issue
Block a user