diff --git a/package.json b/package.json
index 4787bd5da8..3ca0f442de 100644
--- a/package.json
+++ b/package.json
@@ -33,7 +33,7 @@
     "@formkit/inputs": "^1.0.0-beta.11",
     "@formkit/themes": "^1.0.0-beta.11",
     "@formkit/vue": "^1.0.0-beta.11",
-    "@halo-dev/api-client": "^0.0.38",
+    "@halo-dev/api-client": "^0.0.39",
     "@halo-dev/components": "workspace:*",
     "@halo-dev/console-shared": "workspace:*",
     "@halo-dev/richtext-editor": "^0.0.0-alpha.8",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 1a74a8d456..856329f880 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -13,7 +13,7 @@ importers:
       '@formkit/inputs': ^1.0.0-beta.11
       '@formkit/themes': ^1.0.0-beta.11
       '@formkit/vue': ^1.0.0-beta.11
-      '@halo-dev/api-client': ^0.0.38
+      '@halo-dev/api-client': ^0.0.39
       '@halo-dev/components': workspace:*
       '@halo-dev/console-shared': workspace:*
       '@halo-dev/richtext-editor': ^0.0.0-alpha.8
@@ -101,7 +101,7 @@ importers:
       '@formkit/inputs': 1.0.0-beta.11
       '@formkit/themes': 1.0.0-beta.11_tailwindcss@3.1.8
       '@formkit/vue': 1.0.0-beta.11_k5hp3txgeyj6le63abiyc7wx3u
-      '@halo-dev/api-client': 0.0.38
+      '@halo-dev/api-client': 0.0.39
       '@halo-dev/components': link:packages/components
       '@halo-dev/console-shared': link:packages/shared
       '@halo-dev/richtext-editor': 0.0.0-alpha.8_vue@3.2.40
@@ -1892,8 +1892,8 @@ packages:
       - windicss
     dev: false
 
-  /@halo-dev/api-client/0.0.38:
-    resolution: {integrity: sha512-7Vlc/tXkpERi2xWxtoWOVtYbZi+sksrueBAj64gr0cAUOqRy7A82oJXpeabLKeJAjDNdQKZ7eL7CClnf9NJTEA==}
+  /@halo-dev/api-client/0.0.39:
+    resolution: {integrity: sha512-GuTTJDOj0PPMXo3KTiNYGACRUXqJKnjnApK303eNPWqVodgR3mJVLFTXwa+euAJOkcSG3KkB5OtUFAkZeHRbPA==}
     dev: false
 
   /@halo-dev/richtext-editor/0.0.0-alpha.8_vue@3.2.40:
diff --git a/src/modules/system/plugins/PluginList.vue b/src/modules/system/plugins/PluginList.vue
index 4aceba2deb..3686498f12 100644
--- a/src/modules/system/plugins/PluginList.vue
+++ b/src/modules/system/plugins/PluginList.vue
@@ -13,7 +13,7 @@ import {
   VSpace,
 } from "@halo-dev/components";
 import PluginListItem from "./components/PluginListItem.vue";
-import PluginInstallModal from "./components/PluginInstallModal.vue";
+import PluginUploadModal from "./components/PluginUploadModal.vue";
 import { onMounted, ref } from "vue";
 import { apiClient } from "@/utils/api-client";
 import type { PluginList } from "@halo-dev/api-client";
@@ -140,7 +140,7 @@ function handleSortItemChange(sortItem?: SortItem) {
 }
 </script>
 <template>
-  <PluginInstallModal
+  <PluginUploadModal
     v-if="currentUserHasPermission(['system:plugins:manage'])"
     v-model:visible="pluginInstall"
     @close="handleFetchPlugins"
@@ -306,7 +306,7 @@ function handleSortItemChange(sortItem?: SortItem) {
         role="list"
       >
         <li v-for="(plugin, index) in plugins.items" :key="index">
-          <PluginListItem :plugin="plugin" />
+          <PluginListItem :plugin="plugin" @reload="handleFetchPlugins" />
         </li>
       </ul>
 
diff --git a/src/modules/system/plugins/components/PluginListItem.vue b/src/modules/system/plugins/components/PluginListItem.vue
index 6270f266a6..e2d3c152db 100644
--- a/src/modules/system/plugins/components/PluginListItem.vue
+++ b/src/modules/system/plugins/components/PluginListItem.vue
@@ -9,7 +9,8 @@ import {
   VEntityField,
   VAvatar,
 } from "@halo-dev/components";
-import { toRefs } from "vue";
+import PluginUploadModal from "./PluginUploadModal.vue";
+import { ref, toRefs } from "vue";
 import { usePluginLifeCycle } from "../composables/use-plugin";
 import type { Plugin } from "@halo-dev/api-client";
 import { formatDatetime } from "@/utils/date";
@@ -26,11 +27,26 @@ const props = withDefaults(
   }
 );
 
+const emit = defineEmits<{
+  (event: "reload"): void;
+}>();
+
 const { plugin } = toRefs(props);
 
+const upgradeModal = ref(false);
+
 const { isStarted, changeStatus, uninstall } = usePluginLifeCycle(plugin);
+
+const onUpgradeModalClose = () => {
+  emit("reload");
+};
 </script>
 <template>
+  <PluginUploadModal
+    v-model:visible="upgradeModal"
+    :upgrade-plugin="plugin"
+    @close="onUpgradeModalClose"
+  />
   <VEntity>
     <template #start>
       <VEntityField>
@@ -108,6 +124,14 @@ const { isStarted, changeStatus, uninstall } = usePluginLifeCycle(plugin);
       v-if="currentUserHasPermission(['system:plugins:manage'])"
       #dropdownItems
     >
+      <VButton
+        v-close-popper
+        block
+        type="secondary"
+        @click="upgradeModal = true"
+      >
+        升级
+      </VButton>
       <VButton v-close-popper block type="danger" @click="uninstall">
         卸载
       </VButton>
diff --git a/src/modules/system/plugins/components/PluginInstallModal.vue b/src/modules/system/plugins/components/PluginUploadModal.vue
similarity index 76%
rename from src/modules/system/plugins/components/PluginInstallModal.vue
rename to src/modules/system/plugins/components/PluginUploadModal.vue
index 3455531bb8..a19c75de34 100644
--- a/src/modules/system/plugins/components/PluginInstallModal.vue
+++ b/src/modules/system/plugins/components/PluginUploadModal.vue
@@ -6,12 +6,14 @@ import type { Plugin } from "@halo-dev/api-client";
 import { computed, ref } from "vue";
 import type { AxiosResponse } from "axios";
 
-withDefaults(
+const props = withDefaults(
   defineProps<{
     visible: boolean;
+    upgradePlugin?: Plugin;
   }>(),
   {
     visible: false,
+    upgradePlugin: undefined,
   }
 );
 
@@ -22,6 +24,12 @@ const emit = defineEmits<{
 
 const FilePondUploadRef = ref();
 
+const modalTitle = computed(() => {
+  return props.upgradePlugin
+    ? `升级插件(${props.upgradePlugin.spec.displayName})`
+    : "安装插件";
+});
+
 const handleVisibleChange = (visible: boolean) => {
   emit("update:visible", visible);
   if (!visible) {
@@ -31,6 +39,16 @@ const handleVisibleChange = (visible: boolean) => {
 };
 
 const uploadHandler = computed(() => {
+  if (props.upgradePlugin) {
+    return (file, config) =>
+      apiClient.plugin.upgradePlugin(
+        {
+          name: props.upgradePlugin.metadata.name as string,
+          file: file,
+        },
+        config
+      );
+  }
   return (file, config) =>
     apiClient.plugin.installPlugin(
       {
@@ -41,6 +59,11 @@ const uploadHandler = computed(() => {
 });
 
 const onUploaded = async (response: AxiosResponse) => {
+  if (props.upgradePlugin) {
+    handleVisibleChange(false);
+    return;
+  }
+
   const plugin = response.data as Plugin;
   handleVisibleChange(false);
   Dialog.success({
@@ -71,10 +94,11 @@ const onUploaded = async (response: AxiosResponse) => {
   <VModal
     :visible="visible"
     :width="500"
-    title="安装插件"
+    :title="modalTitle"
     @update:visible="handleVisibleChange"
   >
     <FilePondUpload
+      v-if="visible && uploadHandler"
       ref="FilePondUploadRef"
       :allow-multiple="false"
       :handler="uploadHandler"