mirror of https://github.com/halo-dev/halo-admin
refactor: plugin list item component
Signed-off-by: Ryan Wang <i@ryanc.cc>pull/591/head
parent
bb25524c0a
commit
7bdbf2500f
|
@ -3,30 +3,21 @@ import {
|
|||
IconAddCircle,
|
||||
IconArrowDown,
|
||||
IconPlug,
|
||||
IconSettings,
|
||||
VButton,
|
||||
VCard,
|
||||
VPageHeader,
|
||||
VPagination,
|
||||
VSpace,
|
||||
VSwitch,
|
||||
VTag,
|
||||
} from "@halo-dev/components";
|
||||
import PluginListItem from "./components/PluginListItem.vue";
|
||||
import PluginInstallModal from "./components/PluginInstallModal.vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { apiClient } from "@halo-dev/admin-shared";
|
||||
import type { Plugin } from "@halo-dev/api-client";
|
||||
import { usePluginLifeCycle } from "./composables/use-plugin";
|
||||
|
||||
const plugins = ref<Plugin[]>([] as Plugin[]);
|
||||
const pluginInstall = ref(false);
|
||||
const selectedPlugin = ref<Plugin | null>(null);
|
||||
|
||||
const { changeStatus, uninstall } = usePluginLifeCycle(selectedPlugin);
|
||||
|
||||
const isStarted = (plugin: Plugin) => {
|
||||
return plugin.status?.phase === "STARTED" && plugin.spec.enabled;
|
||||
};
|
||||
|
||||
const handleFetchPlugins = async () => {
|
||||
try {
|
||||
|
@ -38,16 +29,6 @@ const handleFetchPlugins = async () => {
|
|||
}
|
||||
};
|
||||
|
||||
function handleChangeStatus(plugin: Plugin) {
|
||||
selectedPlugin.value = plugin;
|
||||
changeStatus();
|
||||
}
|
||||
|
||||
function handleUninstall(plugin: Plugin) {
|
||||
selectedPlugin.value = plugin;
|
||||
uninstall();
|
||||
}
|
||||
|
||||
onMounted(handleFetchPlugins);
|
||||
</script>
|
||||
<template>
|
||||
|
@ -209,129 +190,7 @@ onMounted(handleFetchPlugins);
|
|||
</template>
|
||||
<ul class="box-border h-full w-full divide-y divide-gray-100" role="list">
|
||||
<li v-for="(plugin, index) in plugins" :key="index">
|
||||
<div
|
||||
class="relative block cursor-pointer px-4 py-3 transition-all hover:bg-gray-50"
|
||||
>
|
||||
<div class="relative flex flex-row items-center">
|
||||
<div v-if="plugin.spec.logo" class="mr-4">
|
||||
<RouterLink
|
||||
:to="{
|
||||
name: 'PluginDetail',
|
||||
params: { name: plugin.metadata.name },
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="h-12 w-12 rounded border bg-white p-1 hover:shadow-sm"
|
||||
>
|
||||
<img
|
||||
:alt="plugin.metadata.name"
|
||||
:src="plugin.spec.logo"
|
||||
class="h-full w-full"
|
||||
/>
|
||||
</div>
|
||||
</RouterLink>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="flex flex-row items-center">
|
||||
<RouterLink
|
||||
:to="{
|
||||
name: 'PluginDetail',
|
||||
params: { name: plugin.metadata.name },
|
||||
}"
|
||||
>
|
||||
<span
|
||||
class="mr-2 truncate text-sm font-medium text-gray-900"
|
||||
>
|
||||
{{ plugin.spec.displayName }}
|
||||
</span>
|
||||
</RouterLink>
|
||||
<VSpace>
|
||||
<VTag>
|
||||
{{ isStarted(plugin) ? "已启用" : "未启用" }}
|
||||
</VTag>
|
||||
</VSpace>
|
||||
</div>
|
||||
<div class="mt-2 flex">
|
||||
<VSpace align="start" direction="column" spacing="xs">
|
||||
<span class="text-xs text-gray-500">
|
||||
{{ plugin.spec.description }}
|
||||
</span>
|
||||
<span class="text-xs text-gray-500 sm:hidden">
|
||||
@{{ plugin.spec.author }} {{ plugin.spec.version }}
|
||||
</span>
|
||||
</VSpace>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div
|
||||
class="inline-flex flex-col flex-col-reverse items-end gap-4 sm:flex-row sm:items-center sm:gap-6"
|
||||
>
|
||||
<FloatingTooltip
|
||||
v-if="plugin.status?.phase === 'FAILED'"
|
||||
class="hidden items-center sm:flex"
|
||||
>
|
||||
<div
|
||||
class="inline-flex h-1.5 w-1.5 rounded-full bg-red-600"
|
||||
>
|
||||
<span
|
||||
class="inline-block h-1.5 w-1.5 animate-ping rounded-full bg-red-600"
|
||||
></span>
|
||||
</div>
|
||||
<template #popper>
|
||||
{{ plugin.status?.reason }}:
|
||||
{{ plugin.status?.message }}
|
||||
</template>
|
||||
</FloatingTooltip>
|
||||
<a
|
||||
:href="plugin.spec.homepage"
|
||||
class="hidden text-sm text-gray-500 hover:text-gray-900 sm:block"
|
||||
target="_blank"
|
||||
>
|
||||
@{{ plugin.spec.author }}
|
||||
</a>
|
||||
<span class="hidden text-sm text-gray-500 sm:block">
|
||||
{{ plugin.spec.version }}
|
||||
</span>
|
||||
<time
|
||||
class="hidden text-sm text-gray-500 sm:block"
|
||||
datetime="2020-01-07"
|
||||
>
|
||||
{{ plugin.metadata.creationTimestamp }}
|
||||
</time>
|
||||
<div
|
||||
v-permission="['system:plugins:manage']"
|
||||
class="flex items-center"
|
||||
>
|
||||
<VSwitch
|
||||
:model-value="plugin.spec.enabled"
|
||||
@click="handleChangeStatus(plugin)"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
v-permission="['system:plugins:manage']"
|
||||
class="cursor-pointer"
|
||||
>
|
||||
<FloatingDropdown>
|
||||
<IconSettings />
|
||||
<template #popper>
|
||||
<div class="links-w-48 links-p-2">
|
||||
<VSpace class="links-w-full" direction="column">
|
||||
<VButton
|
||||
block
|
||||
type="danger"
|
||||
@click="handleUninstall(plugin)"
|
||||
>
|
||||
卸载
|
||||
</VButton>
|
||||
</VSpace>
|
||||
</div>
|
||||
</template>
|
||||
</FloatingDropdown>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<PluginListItem :plugin="plugin" />
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
<script lang="ts" setup>
|
||||
import {
|
||||
IconSettings,
|
||||
VButton,
|
||||
VSpace,
|
||||
VSwitch,
|
||||
VTag,
|
||||
} from "@halo-dev/components";
|
||||
import type { PropType } from "vue";
|
||||
import { toRefs } from "vue";
|
||||
import { usePluginLifeCycle } from "../composables/use-plugin";
|
||||
import type { Plugin } from "@halo-dev/api-client";
|
||||
|
||||
const props = defineProps({
|
||||
plugin: {
|
||||
type: Object as PropType<Plugin | null>,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const { plugin } = toRefs(props);
|
||||
|
||||
const { isStarted, changeStatus, uninstall } = usePluginLifeCycle(plugin);
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
class="relative block cursor-pointer px-4 py-3 transition-all hover:bg-gray-50"
|
||||
>
|
||||
<div class="relative flex flex-row items-center">
|
||||
<div v-if="plugin?.spec.logo" class="mr-4">
|
||||
<RouterLink
|
||||
:to="{
|
||||
name: 'PluginDetail',
|
||||
params: { name: plugin?.metadata.name },
|
||||
}"
|
||||
>
|
||||
<div class="h-12 w-12 rounded border bg-white p-1 hover:shadow-sm">
|
||||
<img
|
||||
:alt="plugin?.metadata.name"
|
||||
:src="plugin?.spec.logo"
|
||||
class="h-full w-full"
|
||||
/>
|
||||
</div>
|
||||
</RouterLink>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="flex flex-row items-center">
|
||||
<RouterLink
|
||||
:to="{
|
||||
name: 'PluginDetail',
|
||||
params: { name: plugin?.metadata.name },
|
||||
}"
|
||||
>
|
||||
<span class="mr-2 truncate text-sm font-medium text-gray-900">
|
||||
{{ plugin?.spec.displayName }}
|
||||
</span>
|
||||
</RouterLink>
|
||||
<VSpace>
|
||||
<VTag>
|
||||
{{ isStarted ? "已启用" : "未启用" }}
|
||||
</VTag>
|
||||
</VSpace>
|
||||
</div>
|
||||
<div class="mt-2 flex">
|
||||
<VSpace align="start" direction="column" spacing="xs">
|
||||
<span class="text-xs text-gray-500">
|
||||
{{ plugin?.spec.description }}
|
||||
</span>
|
||||
<span class="text-xs text-gray-500 sm:hidden">
|
||||
@{{ plugin?.spec.author }} {{ plugin?.spec.version }}
|
||||
</span>
|
||||
</VSpace>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div
|
||||
class="inline-flex flex-col flex-col-reverse items-end gap-4 sm:flex-row sm:items-center sm:gap-6"
|
||||
>
|
||||
<FloatingTooltip
|
||||
v-if="plugin?.status?.phase === 'FAILED'"
|
||||
class="hidden items-center sm:flex"
|
||||
>
|
||||
<div class="inline-flex h-1.5 w-1.5 rounded-full bg-red-600">
|
||||
<span
|
||||
class="inline-block h-1.5 w-1.5 animate-ping rounded-full bg-red-600"
|
||||
></span>
|
||||
</div>
|
||||
<template #popper>
|
||||
{{ plugin?.status?.reason }}:
|
||||
{{ plugin?.status?.message }}
|
||||
</template>
|
||||
</FloatingTooltip>
|
||||
<a
|
||||
:href="plugin?.spec.homepage"
|
||||
class="hidden text-sm text-gray-500 hover:text-gray-900 sm:block"
|
||||
target="_blank"
|
||||
>
|
||||
@{{ plugin?.spec.author }}
|
||||
</a>
|
||||
<span class="hidden text-sm text-gray-500 sm:block">
|
||||
{{ plugin?.spec.version }}
|
||||
</span>
|
||||
<time
|
||||
class="hidden text-sm text-gray-500 sm:block"
|
||||
datetime="2020-01-07"
|
||||
>
|
||||
{{ plugin?.metadata.creationTimestamp }}
|
||||
</time>
|
||||
<div
|
||||
v-permission="['system:plugins:manage']"
|
||||
class="flex items-center"
|
||||
>
|
||||
<VSwitch
|
||||
:model-value="plugin?.spec.enabled"
|
||||
@click="changeStatus"
|
||||
/>
|
||||
</div>
|
||||
<span v-permission="['system:plugins:manage']" class="cursor-pointer">
|
||||
<FloatingDropdown>
|
||||
<IconSettings />
|
||||
<template #popper>
|
||||
<div class="links-w-48 links-p-2">
|
||||
<VSpace class="links-w-full" direction="column">
|
||||
<VButton block type="danger" @click="uninstall">
|
||||
卸载
|
||||
</VButton>
|
||||
</VSpace>
|
||||
</div>
|
||||
</template>
|
||||
</FloatingDropdown>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -1,4 +1,5 @@
|
|||
import type { Ref } from "vue";
|
||||
import { computed } from "vue";
|
||||
import type { Plugin } from "@halo-dev/api-client";
|
||||
import cloneDeep from "lodash.clonedeep";
|
||||
import { apiClient } from "@halo-dev/admin-shared";
|
||||
|
@ -7,6 +8,12 @@ import { useDialog } from "@halo-dev/components";
|
|||
export function usePluginLifeCycle(plugin: Ref<Plugin | null>) {
|
||||
const dialog = useDialog();
|
||||
|
||||
const isStarted = computed(() => {
|
||||
return (
|
||||
plugin.value?.status?.phase === "STARTED" && plugin.value?.spec.enabled
|
||||
);
|
||||
});
|
||||
|
||||
const changeStatus = () => {
|
||||
if (!plugin.value) return;
|
||||
|
||||
|
@ -68,6 +75,7 @@ export function usePluginLifeCycle(plugin: Ref<Plugin | null>) {
|
|||
};
|
||||
|
||||
return {
|
||||
isStarted,
|
||||
changeStatus,
|
||||
uninstall,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue