feat: support bulk modification of plugin status

Signed-off-by: Ryan Wang <i@ryanc.cc>
pull/588/head
Ryan Wang 2022-07-08 11:46:51 +08:00
parent 086343ce74
commit 8f95fce0e4
2 changed files with 98 additions and 27 deletions

View File

@ -25,11 +25,11 @@ const buttonClassification = {
const theme: Record<string, Record<string, string>> = { const theme: Record<string, Record<string, string>> = {
global: { global: {
outer: "formkit-disabled:opacity-50 py-3", outer: "formkit-disabled:opacity-50",
help: "text-xs text-gray-500", help: "text-xs text-gray-500",
messages: "list-none p-0 mt-1 mb-0", messages: "list-none p-0 mt-1 mb-0",
message: "text-red-500 mb-1 text-xs", message: "text-red-500 mb-1 text-xs",
form: "flex flex-col divide-y divide-gray-100", form: "flex flex-col divide-y divide-gray-100 space-y-3",
}, },
button: buttonClassification, button: buttonClassification,
color: { color: {

View File

@ -4,6 +4,7 @@ import {
IconArrowDown, IconArrowDown,
IconPlug, IconPlug,
IconSettings, IconSettings,
useDialog,
VButton, VButton,
VCard, VCard,
VPageHeader, VPageHeader,
@ -11,15 +12,25 @@ import {
VSwitch, VSwitch,
VTag, VTag,
} from "@halo-dev/components"; } from "@halo-dev/components";
import { onMounted, ref } from "vue"; import { onMounted, ref, watch } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import type { Plugin } from "@/types/extension"; import type { Plugin } from "@/types/extension";
import { axiosInstance } from "@halo-dev/admin-shared"; import { axiosInstance } from "@halo-dev/admin-shared";
import cloneDeep from "lodash.clonedeep";
const checkAll = ref(false); const checkedAll = ref(false);
const plugins = ref<Plugin[]>([]); const plugins = ref<Plugin[]>([] as Plugin[]);
const selectedPlugins = ref<string[]>([]);
const router = useRouter(); const router = useRouter();
const dialog = useDialog();
watch(
() => selectedPlugins.value,
(newValue) => {
checkedAll.value = newValue.length === plugins.value?.length;
}
);
const handleRouteToDetail = (plugin: Plugin) => { const handleRouteToDetail = (plugin: Plugin) => {
router.push({ router.push({
@ -28,9 +39,9 @@ const handleRouteToDetail = (plugin: Plugin) => {
}); });
}; };
function isStarted(plugin: Plugin) { const isStarted = (plugin: Plugin) => {
return plugin.status?.phase === "STARTED" && plugin.spec.enabled; return plugin.status?.phase === "STARTED" && plugin.spec.enabled;
} };
const handleFetchPlugins = async () => { const handleFetchPlugins = async () => {
try { try {
@ -43,20 +54,67 @@ const handleFetchPlugins = async () => {
} }
}; };
const handleChangePluginStatus = async (plugin: Plugin) => { const handleChangeStatus = (plugin: Plugin) => {
try { const pluginToUpdate = cloneDeep(plugin);
plugin.spec.enabled = !plugin.spec.enabled;
await axiosInstance.put( dialog.info({
`/apis/plugin.halo.run/v1alpha1/plugins/${plugin.metadata.name}`, title: `确定要${plugin.spec.enabled ? "停止" : "启动"}该插件吗?`,
plugin onConfirm: async () => {
); try {
} catch (e) { pluginToUpdate.spec.enabled = !pluginToUpdate.spec.enabled;
console.error(e); await axiosInstance.put(
} finally { `/apis/plugin.halo.run/v1alpha1/plugins/${plugin.metadata.name}`,
window.location.reload(); pluginToUpdate
);
} catch (e) {
console.error(e);
} finally {
window.location.reload();
}
},
});
};
const handleCheckedAllChange = () => {
if (checkedAll.value) {
selectedPlugins.value = plugins.value.map((plugin) => plugin.metadata.name);
} else {
selectedPlugins.value.length = 0;
} }
}; };
const handleChangeStatusInBatch = (enable: boolean) => {
const pluginsToUpdate = plugins.value.filter(
(plugin) =>
selectedPlugins.value.includes(plugin.metadata.name) &&
plugin.spec.enabled !== enable
);
if (pluginsToUpdate.length === 0) {
alert("没有需要更新的插件");
return;
}
dialog.warning({
title: `确定要${enable ? "启动" : "停止"}所选插件吗?`,
onConfirm: async () => {
try {
for (const plugin of pluginsToUpdate) {
plugin.spec.enabled = enable;
await axiosInstance.put(
`/apis/plugin.halo.run/v1alpha1/plugins/${plugin.metadata.name}`,
plugin
);
}
} catch (e) {
console.error(e);
} finally {
window.location.reload();
}
},
});
};
onMounted(handleFetchPlugins); onMounted(handleFetchPlugins);
</script> </script>
<template> <template>
@ -83,19 +141,31 @@ onMounted(handleFetchPlugins);
> >
<div class="mr-4 hidden items-center sm:flex"> <div class="mr-4 hidden items-center sm:flex">
<input <input
v-model="checkAll" v-model="checkedAll"
class="h-4 w-4 rounded border-gray-300 text-indigo-600" class="h-4 w-4 rounded border-gray-300 text-indigo-600"
type="checkbox" type="checkbox"
@change="handleCheckedAllChange"
/> />
</div> </div>
<div class="flex w-full flex-1 sm:w-auto"> <div class="flex w-full flex-1 sm:w-auto">
<FormKit <FormKit
v-if="!checkAll" v-if="!selectedPlugins.length"
placeholder="输入关键词搜索" placeholder="输入关键词搜索"
type="text" type="text"
></FormKit> ></FormKit>
<VSpace v-else> <VSpace v-else>
<VButton type="default">禁用</VButton> <VButton
type="default"
@click="handleChangeStatusInBatch(true)"
>
启用
</VButton>
<VButton
type="default"
@click="handleChangeStatusInBatch(false)"
>
禁用
</VButton>
<VButton type="danger">卸载</VButton> <VButton type="danger">卸载</VButton>
</VSpace> </VSpace>
</div> </div>
@ -225,18 +295,19 @@ onMounted(handleFetchPlugins);
<li v-for="(plugin, index) in plugins" :key="index"> <li v-for="(plugin, index) in plugins" :key="index">
<div <div
:class="{ :class="{
'bg-gray-100': checkAll, 'bg-gray-100': selectedPlugins.includes(plugin.metadata.name),
}" }"
class="relative block cursor-pointer px-4 py-3 transition-all hover:bg-gray-50" class="relative block cursor-pointer px-4 py-3 transition-all hover:bg-gray-50"
> >
<div <div
v-show="checkAll" v-show="selectedPlugins.includes(plugin.metadata.name)"
class="absolute inset-y-0 left-0 w-0.5 bg-themeable-primary" class="absolute inset-y-0 left-0 w-0.5 bg-themeable-primary"
></div> ></div>
<div class="relative flex flex-row items-center"> <div class="relative flex flex-row items-center">
<div class="mr-4 hidden items-center sm:flex"> <div class="mr-4 hidden items-center sm:flex">
<input <input
v-model="checkAll" v-model="selectedPlugins"
:value="plugin.metadata.name"
class="h-4 w-4 rounded border-gray-300 text-indigo-600" class="h-4 w-4 rounded border-gray-300 text-indigo-600"
type="checkbox" type="checkbox"
/> />
@ -283,12 +354,12 @@ onMounted(handleFetchPlugins);
> >
<a <a
:href="plugin.spec.homepage" :href="plugin.spec.homepage"
class="hidden text-sm text-gray-500 hover:text-gray-900 sm:block" class="text-sm text-gray-500 hover:text-gray-900"
target="_blank" target="_blank"
> >
@{{ plugin.spec.author }} @{{ plugin.spec.author }}
</a> </a>
<span class="hidden text-sm text-gray-500 sm:block"> <span class="text-sm text-gray-500">
{{ plugin.spec.version }} {{ plugin.spec.version }}
</span> </span>
<time class="text-sm text-gray-500" datetime="2020-01-07"> <time class="text-sm text-gray-500" datetime="2020-01-07">
@ -297,7 +368,7 @@ onMounted(handleFetchPlugins);
<div class="flex items-center"> <div class="flex items-center">
<VSwitch <VSwitch
:model-value="isStarted(plugin)" :model-value="isStarted(plugin)"
@click="handleChangePluginStatus(plugin)" @click="handleChangeStatus(plugin)"
/> />
</div> </div>
<span class="cursor-pointer"> <span class="cursor-pointer">