258 lines
8.5 KiB
Vue
258 lines
8.5 KiB
Vue
<script setup lang="ts">
|
|
import { ref, computed, inject } from 'vue';
|
|
import { useCustomizerStore } from "@/stores/customizer";
|
|
import { useModuleI18n } from '@/i18n/composables';
|
|
import UninstallConfirmDialog from './UninstallConfirmDialog.vue';
|
|
|
|
const props = defineProps({
|
|
extension: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
marketMode: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
highlight: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
});
|
|
|
|
// 定义要发送到父组件的事件
|
|
const emit = defineEmits([
|
|
'configure',
|
|
'update',
|
|
'reload',
|
|
'install',
|
|
'uninstall',
|
|
'toggle-activation',
|
|
'view-handlers',
|
|
'view-readme'
|
|
]);
|
|
|
|
const reveal = ref(false);
|
|
const showUninstallDialog = ref(false);
|
|
|
|
// 国际化
|
|
const { tm } = useModuleI18n('features/extension');
|
|
|
|
// 操作函数
|
|
const configure = () => {
|
|
emit('configure', props.extension);
|
|
};
|
|
|
|
const updateExtension = () => {
|
|
emit('update', props.extension);
|
|
};
|
|
|
|
const reloadExtension = () => {
|
|
emit('reload', props.extension);
|
|
};
|
|
|
|
const $confirm = inject("$confirm");
|
|
|
|
const installExtension = async () => {
|
|
emit('install', props.extension);
|
|
};
|
|
|
|
const uninstallExtension = async () => {
|
|
showUninstallDialog.value = true;
|
|
};
|
|
|
|
const handleUninstallConfirm = (options: { deleteConfig: boolean; deleteData: boolean }) => {
|
|
emit("uninstall", props.extension, options);
|
|
};
|
|
|
|
const toggleActivation = () => {
|
|
emit('toggle-activation', props.extension);
|
|
};
|
|
|
|
const viewHandlers = () => {
|
|
emit('view-handlers', props.extension);
|
|
};
|
|
|
|
const viewReadme = () => {
|
|
emit('view-readme', props.extension);
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<v-card class="mx-auto d-flex flex-column" elevation="0" :style="{
|
|
position: 'relative',
|
|
backgroundColor: useCustomizerStore().uiTheme === 'PurpleTheme' ? marketMode ? '#f8f0dd' : '#ffffff' : '#282833',
|
|
color: useCustomizerStore().uiTheme === 'PurpleTheme' ? '#000000dd' : '#ffffff'
|
|
}">
|
|
<v-card-text style="padding: 16px; padding-bottom: 0px; display: flex; gap: 16px; width: 100%;">
|
|
<div v-if="extension?.logo">
|
|
<img :src="extension.logo" :alt="extension.name" cover width="100"/>
|
|
</div>
|
|
|
|
<div style="overflow-x: auto;">
|
|
<!-- Top-right three-dot menu -->
|
|
<div style="position: absolute; right: 8px; top: 8px; z-index: 5;">
|
|
<v-menu offset-y>
|
|
<template v-slot:activator="{ props: menuProps }">
|
|
<v-btn icon variant="text" aria-label="more" v-if="extension?.repo" :href="extension?.repo"
|
|
target="_blank">
|
|
<v-icon icon="mdi-github"></v-icon>
|
|
</v-btn>
|
|
<v-btn v-bind="menuProps" icon variant="text" aria-label="more">
|
|
<v-icon icon="mdi-dots-vertical"></v-icon>
|
|
</v-btn>
|
|
</template>
|
|
|
|
<v-list>
|
|
<v-list-item @click="viewReadme">
|
|
<v-list-item-title>📄 {{ tm('buttons.viewDocs') }}</v-list-item-title>
|
|
</v-list-item>
|
|
|
|
<v-list-item v-if="marketMode && !extension?.installed" @click="installExtension">
|
|
<v-list-item-title>
|
|
{{ tm('buttons.install') }}</v-list-item-title>
|
|
</v-list-item>
|
|
|
|
<v-list-item v-if="marketMode && extension?.installed">
|
|
<v-list-item-title class="text--disabled">{{ tm('status.installed') }}</v-list-item-title>
|
|
</v-list-item>
|
|
|
|
<!-- Divider between market actions and plugin actions -->
|
|
<v-divider v-if="!marketMode" />
|
|
|
|
<template v-if="!marketMode">
|
|
<v-list-item @click="configure">
|
|
<v-list-item-title>
|
|
{{ tm('card.actions.pluginConfig') }}</v-list-item-title>
|
|
</v-list-item>
|
|
|
|
<v-list-item @click="uninstallExtension">
|
|
<v-list-item-title class="text-error">{{ tm('card.actions.uninstallPlugin') }}</v-list-item-title>
|
|
</v-list-item>
|
|
|
|
<v-list-item @click="reloadExtension">
|
|
<v-list-item-title>{{ tm('card.actions.reloadPlugin') }}</v-list-item-title>
|
|
</v-list-item>
|
|
|
|
<v-list-item @click="toggleActivation">
|
|
<v-list-item-title>
|
|
{{ extension.activated ? tm('buttons.disable') : tm('buttons.enable') }}{{
|
|
tm('card.actions.togglePlugin') }}
|
|
</v-list-item-title>
|
|
</v-list-item>
|
|
|
|
<v-list-item @click="viewHandlers">
|
|
<v-list-item-title>{{ tm('card.actions.viewHandlers') }} ({{ extension.handlers.length
|
|
}})</v-list-item-title>
|
|
</v-list-item>
|
|
|
|
<v-list-item @click="updateExtension">
|
|
<v-list-item-title>
|
|
{{ extension.has_update
|
|
? tm('card.actions.updateTo') + ' ' + extension.online_version
|
|
: tm('card.actions.reinstall') }}
|
|
</v-list-item-title>
|
|
</v-list-item>
|
|
</template>
|
|
</v-list>
|
|
</v-menu>
|
|
</div>
|
|
|
|
<div style="width: 100%; margin-bottom: 24px;">
|
|
<!-- 最多一行 -->
|
|
<div class="text-caption"
|
|
style="color: gray; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-right: 84px;">
|
|
{{ extension.author }} / {{ extension.name }}
|
|
</div>
|
|
<p class="text-h3 font-weight-black extension-title" :class="{ 'text-h4': $vuetify.display.xs }">
|
|
<span class="extension-title__text">{{ extension.display_name?.length ? extension.display_name : extension.name }}</span>
|
|
<v-tooltip location="top" v-if="extension?.has_update && !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>{{ tm("card.status.hasUpdate") }}: {{ 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>{{ tm("card.status.disabled") }}</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="extension?.has_update" 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" @click="viewHandlers" style="cursor: pointer;">
|
|
<v-icon icon="mdi-cogs" start></v-icon>
|
|
{{ extension.handlers?.length }}{{ tm("card.status.handlersCount") }}
|
|
</v-chip>
|
|
<v-chip v-for="tag in extension.tags" :key="tag" :color="tag === 'danger' ? 'error' : 'primary'" label
|
|
size="small" class="ml-2">
|
|
{{ tag === 'danger' ? tm('tags.danger') : tag }}
|
|
</v-chip>
|
|
</div>
|
|
|
|
<div class="mt-2" :class="{ 'text-caption': $vuetify.display.xs }" style="overflow-y: auto; height: 70px; font-size: 90%;">
|
|
{{ extension.desc }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</v-card-text>
|
|
|
|
<v-card-actions class="extension-actions">
|
|
<v-btn color="primary" size="small" @click="viewReadme">
|
|
{{ tm('buttons.viewDocs') }}
|
|
</v-btn>
|
|
<v-btn v-if="!marketMode" color="primary" size="small" @click="configure">
|
|
{{ tm('card.actions.pluginConfig') }}
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
|
|
<!-- 卸载确认对话框 -->
|
|
<UninstallConfirmDialog
|
|
v-model="showUninstallDialog"
|
|
@confirm="handleUninstallConfirm"
|
|
/>
|
|
|
|
</template>
|
|
|
|
<style scoped>
|
|
.extension-image-container {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-left: 12px;
|
|
}
|
|
|
|
.extension-title {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.extension-title__text {
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
padding-top: 6px
|
|
}
|
|
|
|
@media (max-width: 600px) {
|
|
.extension-image-container {
|
|
margin-left: 8px;
|
|
}
|
|
}
|
|
|
|
.extension-actions {
|
|
margin-top: auto;
|
|
gap: 8px;
|
|
}
|
|
</style>
|