1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 08:19:15 +08:00

feat: 增加应用参数修改功能

This commit is contained in:
zhengkunwang223 2023-03-08 11:04:22 +08:00 committed by zhengkunwang223
parent b54cbe1d11
commit 1e9833d7b7
19 changed files with 285 additions and 55 deletions

View File

@ -283,3 +283,25 @@ func (b *BaseApi) GetParams(c *gin.Context) {
} }
helper.SuccessWithData(c, content) helper.SuccessWithData(c, content)
} }
// @Tags App
// @Summary Change app params
// @Description 修改应用参数
// @Accept json
// @Param request body request.AppInstalledUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /apps/installed/params/update [post]
// @x-panel-log {"bodyKeys":["installId"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"应用参数修改 [installId]","formatEN":"Application param update [installId]"}
func (b *BaseApi) UpdateInstalled(c *gin.Context) {
var req request.AppInstalledUpdate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := appInstallService.Update(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}

View File

@ -76,6 +76,8 @@ type AppFormFields struct {
Default interface{} `json:"default"` Default interface{} `json:"default"`
EnvKey string `json:"envKey"` EnvKey string `json:"envKey"`
Disabled bool `json:"disabled"` Disabled bool `json:"disabled"`
Edit bool `json:"edit"`
Rule string `json:"rule"`
} }
type AppResource struct { type AppResource struct {

View File

@ -48,6 +48,11 @@ type AppInstalledOperate struct {
DeleteDB bool `json:"deleteDB"` DeleteDB bool `json:"deleteDB"`
} }
type AppInstalledUpdate struct {
InstallId uint `json:"installId" validate:"required"`
Params map[string]interface{} `json:"params" validate:"required"`
}
type PortUpdate struct { type PortUpdate struct {
Key string `json:"key"` Key string `json:"key"`
Name string `json:"name"` Name string `json:"name"`

View File

@ -61,6 +61,11 @@ type AppService struct {
} }
type AppParam struct { type AppParam struct {
Label string `json:"label"` Value interface{} `json:"value"`
Value interface{} `json:"value"` Edit bool `json:"edit"`
Key string `json:"key"`
Rule string `json:"rule"`
LabelZh string `json:"labelZh"`
LabelEn string `json:"labelEn"`
Type string `json:"type"`
} }

View File

@ -3,7 +3,9 @@ package service
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/joho/godotenv"
"io/ioutil" "io/ioutil"
"math"
"os" "os"
"path" "path"
"reflect" "reflect"
@ -156,15 +158,7 @@ func (a AppInstallService) Operate(req request.AppInstalledOperate) error {
dockerComposePath := install.GetComposePath() dockerComposePath := install.GetComposePath()
switch req.Operate { switch req.Operate {
case constant.Rebuild: case constant.Rebuild:
out, err := compose.Down(dockerComposePath) return rebuildApp(install)
if err != nil {
return handleErr(install, err, out)
}
out, err = compose.Up(dockerComposePath)
if err != nil {
return handleErr(install, err, out)
}
return syncById(install.ID)
case constant.Start: case constant.Start:
out, err := compose.Start(dockerComposePath) out, err := compose.Start(dockerComposePath)
if err != nil { if err != nil {
@ -193,13 +187,59 @@ func (a AppInstallService) Operate(req request.AppInstalledOperate) error {
return nil return nil
case constant.Sync: case constant.Sync:
return syncById(install.ID) return syncById(install.ID)
case constant.Update: case constant.Upgrade:
return updateInstall(install.ID, req.DetailId) return updateInstall(install.ID, req.DetailId)
default: default:
return errors.New("operate not support") return errors.New("operate not support")
} }
} }
func (a AppInstallService) Update(req request.AppInstalledUpdate) error {
installed, err := appInstallRepo.GetFirst(commonRepo.WithByID(req.InstallId))
if err != nil {
return err
}
port, ok := req.Params["PANEL_APP_PORT_HTTP"]
if ok {
portN := int(math.Ceil(port.(float64)))
if portN != installed.HttpPort {
httpPort, err := checkPort("PANEL_APP_PORT_HTTP", req.Params)
if err != nil {
return err
}
installed.HttpPort = httpPort
}
}
ports, ok := req.Params["PANEL_APP_PORT_HTTPS"]
if ok {
portN := int(math.Ceil(ports.(float64)))
if portN != installed.HttpsPort {
httpsPort, err := checkPort("PANEL_APP_PORT_HTTPS", req.Params)
if err != nil {
return err
}
installed.HttpsPort = httpsPort
}
}
envPath := path.Join(installed.GetPath(), ".env")
oldEnvMaps, err := godotenv.Read(envPath)
if err != nil {
return err
}
handleMap(req.Params, oldEnvMaps)
paramByte, err := json.Marshal(oldEnvMaps)
if err != nil {
return err
}
installed.Env = string(paramByte)
if err := godotenv.Write(oldEnvMaps, envPath); err != nil {
return err
}
_ = appInstallRepo.Save(&installed)
return rebuildApp(installed)
}
func (a AppInstallService) SyncAll() error { func (a AppInstallService) SyncAll() error {
allList, err := appInstallRepo.ListBy() allList, err := appInstallRepo.ListBy()
if err != nil { if err != nil {
@ -391,17 +431,24 @@ func (a AppInstallService) GetParams(id uint) ([]response.AppParam, error) {
} }
for _, form := range appForm.FormFields { for _, form := range appForm.FormFields {
if v, ok := envs[form.EnvKey]; ok { if v, ok := envs[form.EnvKey]; ok {
appParam := response.AppParam{
Edit: false,
Key: form.EnvKey,
Rule: form.Rule,
Type: form.Type,
}
if form.Edit {
appParam.Edit = true
}
appParam.LabelZh = form.LabelZh
appParam.LabelEn = form.LabelEn
if form.Type == "service" { if form.Type == "service" {
appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithServiceName(v.(string))) appInstall, _ := appInstallRepo.GetFirst(appInstallRepo.WithServiceName(v.(string)))
res = append(res, response.AppParam{ appParam.Value = appInstall.Name
Label: form.LabelZh, res = append(res, appParam)
Value: appInstall.Name,
})
} else { } else {
res = append(res, response.AppParam{ appParam.Value = v
Label: form.LabelZh, res = append(res, appParam)
Value: v,
})
} }
} }
} }

View File

@ -345,6 +345,19 @@ func upApp(composeFilePath string, appInstall model.AppInstall) {
} }
} }
func rebuildApp(appInstall model.AppInstall) error {
dockerComposePath := appInstall.GetComposePath()
out, err := compose.Down(dockerComposePath)
if err != nil {
return handleErr(appInstall, err, out)
}
out, err = compose.Up(dockerComposePath)
if err != nil {
return handleErr(appInstall, err, out)
}
return syncById(appInstall.ID)
}
func getAppDetails(details []model.AppDetail, versions []string) map[string]model.AppDetail { func getAppDetails(details []model.AppDetail, versions []string) map[string]model.AppDetail {
appDetails := make(map[string]model.AppDetail, len(details)) appDetails := make(map[string]model.AppDetail, len(details))
for _, old := range details { for _, old := range details {

View File

@ -32,4 +32,5 @@ var (
Restore AppOperate = "restore" Restore AppOperate = "restore"
Update AppOperate = "update" Update AppOperate = "update"
Rebuild AppOperate = "rebuild" Rebuild AppOperate = "rebuild"
Upgrade AppOperate = "upgrade"
) )

View File

@ -33,5 +33,6 @@ func (a *AppRouter) InitAppRouter(Router *gin.RouterGroup) {
appRouter.GET("/services/:key", baseApi.GetServices) appRouter.GET("/services/:key", baseApi.GetServices)
appRouter.GET("/installed/conf/:key", baseApi.GetDefaultConfig) appRouter.GET("/installed/conf/:key", baseApi.GetDefaultConfig)
appRouter.GET("/installed/params/:appInstallId", baseApi.GetParams) appRouter.GET("/installed/params/:appInstallId", baseApi.GetParams)
appRouter.POST("/installed/params/update", baseApi.UpdateInstalled)
} }
} }

View File

@ -1,6 +1,6 @@
// Generated by 'unplugin-auto-import' // Generated by 'unplugin-auto-import'
// We suggest you to commit this file into source control // We suggest you to commit this file into source control
declare global { declare global {
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
} }
export {} export {}

View File

@ -151,7 +151,12 @@ export namespace App {
} }
export interface InstallParams { export interface InstallParams {
label: string; labelZh: string;
value: string; labelEn: string;
value: any;
edit: boolean;
key: string;
rule: string;
type: string;
} }
} }

View File

@ -77,3 +77,7 @@ export const GetAppDefaultConfig = (key: string) => {
export const GetAppInstallParams = (id: number) => { export const GetAppInstallParams = (id: number) => {
return http.get<App.InstallParams[]>(`apps/installed/params/${id}`); return http.get<App.InstallParams[]>(`apps/installed/params/${id}`);
}; };
export const UpdateAppInstallParams = (req: any) => {
return http.post<any>(`apps/installed/params/update`, req);
};

View File

@ -150,7 +150,7 @@ const checkParamCommon = (rule: any, value: any, callback: any) => {
if (value === '' || typeof value === 'undefined' || value == null) { if (value === '' || typeof value === 'undefined' || value == null) {
callback(new Error(i18n.global.t('commons.rule.paramName'))); callback(new Error(i18n.global.t('commons.rule.paramName')));
} else { } else {
const reg = /^[a-zA-Z0-9]{1}[a-zA-Z0-9._-]{2,30}$/; const reg = /^[a-zA-Z0-9]{1}[a-zA-Z0-9._-]{1,29}$/;
if (!reg.test(value) && value !== '') { if (!reg.test(value) && value !== '') {
callback(new Error(i18n.global.t('commons.rule.paramName'))); callback(new Error(i18n.global.t('commons.rule.paramName')));
} else { } else {
@ -163,7 +163,7 @@ const checkParamComplexity = (rule: any, value: any, callback: any) => {
if (value === '' || typeof value === 'undefined' || value == null) { if (value === '' || typeof value === 'undefined' || value == null) {
callback(new Error(i18n.global.t('commons.rule.paramComplexity', ['.%@$!&~_']))); callback(new Error(i18n.global.t('commons.rule.paramComplexity', ['.%@$!&~_'])));
} else { } else {
const reg = /^[a-zA-Z0-9]{1}[a-zA-Z0-9.%@$!&~_]{6,30}$/; const reg = /^[a-zA-Z0-9]{1}[a-zA-Z0-9.%@$!&~_]{5,29}$/;
if (!reg.test(value) && value !== '') { if (!reg.test(value) && value !== '') {
callback(new Error(i18n.global.t('commons.rule.paramComplexity', ['.%@$!&~_']))); callback(new Error(i18n.global.t('commons.rule.paramComplexity', ['.%@$!&~_'])));
} else { } else {

View File

@ -881,7 +881,7 @@ export default {
deleteWarn: deleteWarn:
'The delete operation will delete all data and backups together. This operation cannot be rolled back. Do you want to continue? ', 'The delete operation will delete all data and backups together. This operation cannot be rolled back. Do you want to continue? ',
syncSuccess: 'Sync successfully', syncSuccess: 'Sync successfully',
canUpdate: 'Upgrade', canUpgrade: 'Upgrade',
backup: 'Backup', backup: 'Backup',
backupName: 'File Name', backupName: 'File Name',
backupPath: 'File Path', backupPath: 'File Path',
@ -889,7 +889,8 @@ export default {
restore: 'Restore', restore: 'Restore',
restoreWarn: restoreWarn:
'The restore operation will restart the application and replace the data. This operation cannot be rolled back. Do you want to continue?', 'The restore operation will restart the application and replace the data. This operation cannot be rolled back. Do you want to continue?',
update: 'upgrade', update: 'update',
upgrade: 'upgrade',
versioneSelect: 'Please select a version', versioneSelect: 'Please select a version',
operatorHelper: 'Operation {0} will be performed on the selected application, continue? ', operatorHelper: 'Operation {0} will be performed on the selected application, continue? ',
checkInstalledWarn: '{0} is not detected, please enter the app store and click to install!', checkInstalledWarn: '{0} is not detected, please enter the app store and click to install!',
@ -919,6 +920,7 @@ export default {
syncAppList: 'Sync', syncAppList: 'Sync',
updatePrompt: 'The current application is the latest version', updatePrompt: 'The current application is the latest version',
installPrompt: 'No apps installed yet', installPrompt: 'No apps installed yet',
updateHelper: 'Updating parameters may cause the application to fail to start, please operate with caution',
}, },
website: { website: {
website: 'Website', website: 'Website',

View File

@ -885,14 +885,15 @@ export default {
delete: '删除', delete: '删除',
deleteWarn: '删除操作会把所有数据和备份一并删除此操作不可回滚是否继续', deleteWarn: '删除操作会把所有数据和备份一并删除此操作不可回滚是否继续',
syncSuccess: '同步成功', syncSuccess: '同步成功',
canUpdate: '可升级', canUpgrade: '可升级',
backup: '备份', backup: '备份',
backupName: '文件名称', backupName: '文件名称',
backupPath: '文件路径', backupPath: '文件路径',
backupdate: '备份时间', backupdate: '备份时间',
restore: '恢复', restore: '恢复',
restoreWarn: '恢复操作会重启应用,并替换数据,此操作不可回滚,是否继续?', restoreWarn: '恢复操作会重启应用,并替换数据,此操作不可回滚,是否继续?',
update: '升级', update: '更新',
upgrade: '升级',
versioneSelect: '请选择版本', versioneSelect: '请选择版本',
operatorHelper: '将对选中应用进行 {0} 操作是否继续', operatorHelper: '将对选中应用进行 {0} 操作是否继续',
checkInstalledWarn: '未检测到 {0} ,请进入应用商店点击安装!', checkInstalledWarn: '未检测到 {0} ,请进入应用商店点击安装!',
@ -929,6 +930,8 @@ export default {
document: '文档说明', document: '文档说明',
updatePrompt: '当前应用均为最新版本', updatePrompt: '当前应用均为最新版本',
installPrompt: '尚未安装任何应用', installPrompt: '尚未安装任何应用',
updateHelper: '更新参数可能导致应用无法启动请提前备份并谨慎操作',
updateWarn: '更新参数需要重建应用是否继续',
}, },
website: { website: {
website: '网站', website: '网站',

View File

@ -51,8 +51,8 @@ const appStoreRouter = {
}, },
}, },
{ {
path: 'update', path: 'upgrade',
name: 'AppUpdate', name: 'AppUpgrade',
component: () => import('@/views/app-store/installed/index.vue'), component: () => import('@/views/app-store/installed/index.vue'),
props: true, props: true,
hidden: true, hidden: true,

View File

@ -27,8 +27,8 @@ const buttons = [
path: '/apps/installed', path: '/apps/installed',
}, },
{ {
label: i18n.global.t('app.canUpdate'), label: i18n.global.t('app.canUpgrade'),
path: '/apps/update', path: '/apps/upgrade',
count: 0, count: 0,
}, },
]; ];
@ -49,7 +49,7 @@ const search = () => {
onMounted(() => { onMounted(() => {
search(); search();
bus.on('update', () => { bus.on('upgrade', () => {
showButton.value = false; showButton.value = false;
search(); search();
}); });

View File

@ -1,20 +1,57 @@
<template> <template>
<el-drawer :close-on-click-modal="false" v-model="open" size="40%"> <el-drawer :close-on-click-modal="false" v-model="open" size="40%">
<template #header> <template #header>
<Header :header="$t('app.param')" :back="handleClose"></Header> <Header :header="$t('app.param')" :back="handleClose">
<template #buttons v-if="canEdit">
<el-button type="primary" plain @click="editParam" :disabled="loading">
{{ edit ? $t('app.detail') : $t('commons.button.edit') }}
</el-button>
</template>
</Header>
</template> </template>
<el-descriptions border :column="1"> <el-descriptions border :column="1" v-if="!edit">
<el-descriptions-item v-for="(param, key) in params" :label="param.label" :key="key"> <el-descriptions-item v-for="(param, key) in params" :label="getLabel(param)" :key="key">
{{ param.value }} {{ param.value }}
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
<el-row v-else v-loading="loading">
<el-col :span="22" :offset="1">
<el-alert :title="$t('app.updateHelper')" type="warning" :closable="false" />
<el-form ref="paramForm" :model="paramModel" label-position="top" :rules="rules">
<div v-for="(p, index) in params" :key="index">
<el-form-item :prop="p.key" :label="getLabel(p)">
<el-input
v-if="p.type == 'number'"
type="number"
v-model.number="paramModel[p.key]"
:disabled="!p.edit"
></el-input>
<el-input v-else v-model.trim="paramModel[p.key]" :disabled="!p.edit"></el-input>
</el-form-item>
</div>
</el-form>
</el-col>
</el-row>
<template #footer v-if="edit">
<span>
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
<el-button type="primary" :disabled="loading" @click="submit(paramForm)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer> </el-drawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { App } from '@/api/interface/app'; import { App } from '@/api/interface/app';
import { GetAppInstallParams } from '@/api/modules/app'; import { GetAppInstallParams, UpdateAppInstallParams } from '@/api/modules/app';
import { ref } from 'vue'; import { reactive, ref } from 'vue';
import Header from '@/components/drawer-header/index.vue'; import Header from '@/components/drawer-header/index.vue';
import { useI18n } from 'vue-i18n';
import { FormInstance } from 'element-plus';
import { Rules } from '@/global/form-rules';
import { MsgSuccess } from '@/utils/message';
import i18n from '@/lang';
interface ParamProps { interface ParamProps {
id: Number; id: Number;
@ -23,31 +60,114 @@ const paramData = ref<ParamProps>({
id: 0, id: 0,
}); });
interface EditForm extends App.InstallParams {
default: any;
}
let open = ref(false); let open = ref(false);
let loading = ref(false); let loading = ref(false);
const params = ref<App.InstallParams[]>(); const params = ref<EditForm[]>();
let edit = ref(false);
const paramForm = ref<FormInstance>();
let paramModel = ref<any>({});
let rules = reactive({});
let submitModel = ref<any>({});
let canEdit = ref(false);
const acceptParams = (props: ParamProps) => { const acceptParams = async (props: ParamProps) => {
submitModel.value.installId = props.id;
params.value = []; params.value = [];
paramData.value.id = props.id; paramData.value.id = props.id;
get(); edit.value = false;
await get();
open.value = true; open.value = true;
}; };
const handleClose = () => { const handleClose = () => {
open.value = false; open.value = false;
}; };
const editParam = () => {
params.value.forEach((param: EditForm) => {
paramModel.value[param.key] = param.value;
});
edit.value = !edit.value;
};
const get = async () => { const get = async () => {
try { try {
loading.value = true; loading.value = true;
const res = await GetAppInstallParams(Number(paramData.value.id)); const res = await GetAppInstallParams(Number(paramData.value.id));
params.value = res.data; if (res.data && res.data.length > 0) {
res.data.forEach((d) => {
if (d.edit) {
console.log(d.edit);
canEdit.value = true;
}
let value = d.value;
if (d.type === 'number') {
value = Number(value);
}
params.value.push({
default: value,
labelEn: d.labelEn,
labelZh: d.labelZh,
rule: d.rule,
value: value,
edit: d.edit,
key: d.key,
type: d.type,
});
rules[d.key] = [Rules.requiredInput];
if (d.rule) {
rules[d.key].push(Rules[d.rule]);
}
});
}
} catch (error) { } catch (error) {
} finally { } finally {
loading.value = false; loading.value = false;
} }
}; };
const getLabel = (row: EditForm): string => {
const language = useI18n().locale.value;
if (language == 'zh') {
return row.labelZh;
} else {
return row.labelEn;
}
};
const submit = async (formEl: FormInstance) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (!valid) {
return;
}
ElMessageBox.confirm(i18n.global.t('app.updateWarn'), i18n.global.t('app.update'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
}).then(async () => {
submitModel.value.params = paramModel.value;
try {
loading.value = true;
await UpdateAppInstallParams(submitModel.value);
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
handleClose();
} catch (error) {
loading.value = false;
}
});
});
};
defineExpose({ acceptParams }); defineExpose({ acceptParams });
</script> </script>
<style lang="scss">
.change-button {
margin-top: 5px;
}
</style>

View File

@ -50,7 +50,7 @@
<template #main> <template #main>
<div class="update-prompt" v-if="data == null"> <div class="update-prompt" v-if="data == null">
<span>{{ mode === 'update' ? $t('app.updatePrompt') : $t('app.installPrompt') }}</span> <span>{{ mode === 'upgrade' ? $t('app.updatePrompt') : $t('app.installPrompt') }}</span>
<div> <div>
<img src="@/assets/images/no_update_app.svg" /> <img src="@/assets/images/no_update_app.svg" />
</div> </div>
@ -129,7 +129,7 @@
round round
size="small" size="small"
@click="openOperate(installed, 'update')" @click="openOperate(installed, 'update')"
v-if="mode === 'update'" v-if="mode === 'upgrade'"
> >
{{ $t('app.update') }} {{ $t('app.update') }}
</el-button> </el-button>
@ -171,7 +171,7 @@
<AppResources ref="checkRef" /> <AppResources ref="checkRef" />
<AppDelete ref="deleteRef" @close="search" /> <AppDelete ref="deleteRef" @close="search" />
<AppParams ref="appParamRef" /> <AppParams ref="appParamRef" />
<AppUpdate ref="updateRef" @close="search" /> <AppUpgrade ref="upgradeRef" @close="search" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -191,7 +191,7 @@ import Uploads from '@/components/upload/index.vue';
import AppResources from './check/index.vue'; import AppResources from './check/index.vue';
import AppDelete from './delete/index.vue'; import AppDelete from './delete/index.vue';
import AppParams from './detail/index.vue'; import AppParams from './detail/index.vue';
import AppUpdate from './update/index.vue'; import AppUpgrade from './upgrade/index.vue';
import { App } from '@/api/interface/app'; import { App } from '@/api/interface/app';
import Status from '@/components/status/index.vue'; import Status from '@/components/status/index.vue';
import { getAge } from '@/utils/util'; import { getAge } from '@/utils/util';
@ -217,7 +217,7 @@ const uploadRef = ref();
const checkRef = ref(); const checkRef = ref();
const deleteRef = ref(); const deleteRef = ref();
const appParamRef = ref(); const appParamRef = ref();
const updateRef = ref(); const upgradeRef = ref();
let tags = ref<App.Tag[]>([]); let tags = ref<App.Tag[]>([]);
let activeTag = ref('all'); let activeTag = ref('all');
let searchReq = reactive({ let searchReq = reactive({
@ -273,7 +273,7 @@ const openOperate = (row: any, op: string) => {
operateReq.installId = row.id; operateReq.installId = row.id;
operateReq.operate = op; operateReq.operate = op;
if (op == 'update') { if (op == 'update') {
updateRef.value.acceptParams(row.id, row.name); upgradeRef.value.acceptParams(row.id, row.name);
} else if (op == 'delete') { } else if (op == 'delete') {
AppInstalledDeleteCheck(row.id).then(async (res) => { AppInstalledDeleteCheck(row.id).then(async (res) => {
const items = res.data; const items = res.data;
@ -393,9 +393,9 @@ const openParam = (installId: number) => {
onMounted(() => { onMounted(() => {
const path = router.currentRoute.value.path; const path = router.currentRoute.value.path;
if (path == '/apps/update') { if (path == '/apps/upgrade') {
activeName.value = i18n.global.t('app.canUpdate'); activeName.value = i18n.global.t('app.canUpgrade');
mode.value = 'update'; mode.value = 'upgrade';
searchReq.update = true; searchReq.update = true;
} }
search(); search();

View File

@ -46,7 +46,7 @@ let loading = ref(false);
let versions = ref<App.VersionDetail[]>(); let versions = ref<App.VersionDetail[]>();
let operateReq = reactive({ let operateReq = reactive({
detailId: 0, detailId: 0,
operate: 'update', operate: 'upgrade',
installId: 0, installId: 0,
}); });
const resourceName = ref(''); const resourceName = ref('');
@ -77,7 +77,7 @@ const operate = async () => {
await InstalledOp(operateReq) await InstalledOp(operateReq)
.then(() => { .then(() => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
bus.emit('update', true); bus.emit('upgrade', true);
handleClose(); handleClose();
}) })
.finally(() => { .finally(() => {
@ -100,7 +100,7 @@ const onOperate = async () => {
}; };
onBeforeUnmount(() => { onBeforeUnmount(() => {
bus.off('update'); bus.off('upgrade');
}); });
defineExpose({ defineExpose({