feat: 增加系统 IP 设置,实现容器端口跳转功能 (#1479)
This commit is contained in:
		| @@ -5,6 +5,7 @@ import "time" | ||||
| type SettingInfo struct { | ||||
| 	UserName      string `json:"userName"` | ||||
| 	Email         string `json:"email"` | ||||
| 	SystemIP      string `json:"systemIP"` | ||||
| 	SystemVersion string `json:"systemVersion"` | ||||
|  | ||||
| 	SessionTimeout string `json:"sessionTimeout"` | ||||
|   | ||||
| @@ -407,6 +407,9 @@ var AddMfaInterval = &gormigrate.Migration{ | ||||
| 		if err := tx.Create(&model.Setting{Key: "MFAInterval", Value: "30"}).Error; err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err := tx.Create(&model.Setting{Key: "SystemIP", Value: ""}).Error; err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return nil | ||||
| 	}, | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,7 @@ export namespace Setting { | ||||
|         userName: string; | ||||
|         password: string; | ||||
|         email: string; | ||||
|         systemIP: string; | ||||
|         systemVersion: string; | ||||
|  | ||||
|         sessionTimeout: number; | ||||
|   | ||||
| @@ -969,6 +969,8 @@ const message = { | ||||
|         sessionTimeoutError: 'The minimum timeout is 300 seconds', | ||||
|         sessionTimeoutHelper: | ||||
|             'If you do not operate the panel for more than {0} seconds, the panel automatically logs out', | ||||
|         systemIP: 'System IP', | ||||
|         systemIPWarning: 'Please set the system IP in the panel settings first.', | ||||
|         syncTime: 'Server time', | ||||
|         timeZone: 'Time Zone', | ||||
|         timeZoneChangeHelper: 'Changing the time zone requires restarting the service. Do you want to continue?', | ||||
|   | ||||
| @@ -949,6 +949,8 @@ const message = { | ||||
|         sessionTimeout: '超时时间', | ||||
|         sessionTimeoutError: '最小超时时间为 300 秒', | ||||
|         sessionTimeoutHelper: '如果用户超过 {0} 秒未操作面板,面板将自动退出登录', | ||||
|         systemIP: '服务器 IP', | ||||
|         systemIPWarning: '请先在面板设置中设置服务器 IP', | ||||
|         syncTime: '服务器时间', | ||||
|         timeZone: '系统时区', | ||||
|         timeZoneChangeHelper: '系统时区修改需要重启服务,是否继续?', | ||||
|   | ||||
| @@ -90,7 +90,16 @@ | ||||
|                             <div v-if="row.ports"> | ||||
|                                 <div v-for="(item, index) in row.ports" :key="index"> | ||||
|                                     <div v-if="row.expand || (!row.expand && index < 3)"> | ||||
|                                         <el-tag class="tagMargin">{{ item }}</el-tag> | ||||
|                                         <el-button | ||||
|                                             @click="goDashboard(item)" | ||||
|                                             class="tagMargin" | ||||
|                                             icon="Position" | ||||
|                                             type="primary" | ||||
|                                             plain | ||||
|                                             size="small" | ||||
|                                         > | ||||
|                                             {{ item }} | ||||
|                                         </el-button> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <div v-if="!row.expand && row.ports.length > 3"> | ||||
| @@ -154,8 +163,9 @@ import { Container } from '@/api/interface/container'; | ||||
| import { ElMessageBox } from 'element-plus'; | ||||
| import i18n from '@/lang'; | ||||
| import router from '@/routers'; | ||||
| import { MsgSuccess } from '@/utils/message'; | ||||
| import { MsgError, MsgSuccess } from '@/utils/message'; | ||||
| import { computeSize } from '@/utils/util'; | ||||
| import { getSettingInfo } from '@/api/modules/setting'; | ||||
|  | ||||
| const loading = ref(); | ||||
| const data = ref(); | ||||
| @@ -184,6 +194,20 @@ const loadStatus = async () => { | ||||
|             loading.value = false; | ||||
|         }); | ||||
| }; | ||||
|  | ||||
| const goDashboard = async (port: any) => { | ||||
|     if (!port || port.indexOf(':') === -1) { | ||||
|         return; | ||||
|     } | ||||
|     let portEx = port.split(':')[0]; | ||||
|     const res = await getSettingInfo(); | ||||
|     if (!res.data.systemIP) { | ||||
|         MsgError(i18n.global.t('setting.systemIPWarning')); | ||||
|         return; | ||||
|     } | ||||
|     window.open(`http://${res.data.systemIP}:${portEx}`, '_blank'); | ||||
| }; | ||||
|  | ||||
| const goSetting = async () => { | ||||
|     router.push({ name: 'ContainerSetting' }); | ||||
| }; | ||||
|   | ||||
| @@ -257,7 +257,6 @@ const acceptParams = (params: DialogProps): void => { | ||||
|             item.host = item.hostPort; | ||||
|         } | ||||
|         dialogData.value.rowData.volumes = dialogData.value.rowData.volumes || []; | ||||
|         console.log(dialogData.value.rowData.cpuShares); | ||||
|     } | ||||
|     loadLimit(); | ||||
|     loadImageOptions(); | ||||
|   | ||||
| @@ -164,6 +164,7 @@ import { GetAppPort } from '@/api/modules/app'; | ||||
| import router from '@/routers'; | ||||
| import { MsgError, MsgSuccess } from '@/utils/message'; | ||||
| import useClipboard from 'vue-clipboard3'; | ||||
| import { getSettingInfo } from '@/api/modules/setting'; | ||||
| const { toClipboard } = useClipboard(); | ||||
|  | ||||
| const loading = ref(false); | ||||
| @@ -261,9 +262,12 @@ const goDashboard = async () => { | ||||
|         phpVisiable.value = true; | ||||
|         return; | ||||
|     } | ||||
|     let href = window.location.href; | ||||
|     let ipLocal = href.split('//')[1].split(':')[0]; | ||||
|     window.open(`http://${ipLocal}:${phpadminPort.value}`, '_blank'); | ||||
|     const res = await getSettingInfo(); | ||||
|     if (!res.data.systemIP) { | ||||
|         MsgError(i18n.global.t('setting.systemIPWarning')); | ||||
|         return; | ||||
|     } | ||||
|     window.open(`http://${res.data.systemIP}:${phpadminPort.value}`, '_blank'); | ||||
| }; | ||||
| const getAppDetail = (key: string) => { | ||||
|     router.push({ name: 'AppDetail', params: { appKey: key } }); | ||||
|   | ||||
| @@ -65,6 +65,9 @@ import { nextTick, onBeforeUnmount, ref } from 'vue'; | ||||
| import { App } from '@/api/interface/app'; | ||||
| import { GetAppPort } from '@/api/modules/app'; | ||||
| import router from '@/routers'; | ||||
| import { MsgError } from '@/utils/message'; | ||||
| import { getSettingInfo } from '@/api/modules/setting'; | ||||
| import i18n from '@/lang'; | ||||
|  | ||||
| const loading = ref(false); | ||||
| const maskShow = ref(true); | ||||
| @@ -94,9 +97,12 @@ const goDashboard = async () => { | ||||
|         commandVisiable.value = true; | ||||
|         return; | ||||
|     } | ||||
|     let href = window.location.href; | ||||
|     let ipLocal = href.split('//')[1].split(':')[0]; | ||||
|     window.open(`http://${ipLocal}:${redisCommandPort.value}`, '_blank'); | ||||
|     const res = await getSettingInfo(); | ||||
|     if (!res.data.systemIP) { | ||||
|         MsgError(i18n.global.t('setting.systemIPWarning')); | ||||
|         return; | ||||
|     } | ||||
|     window.open(`http://${res.data.systemIP}:${redisCommandPort.value}`, '_blank'); | ||||
| }; | ||||
| const getAppDetail = (key: string) => { | ||||
|     router.push({ name: 'AppDetail', params: { appKey: key } }); | ||||
|   | ||||
| @@ -82,7 +82,6 @@ const handleExceed: UploadProps['onExceed'] = (files) => { | ||||
| }; | ||||
|  | ||||
| const hadleSuccess: UploadProps['onSuccess'] = (res, file) => { | ||||
|     console.log(file.name); | ||||
|     file.status = 'success'; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -82,6 +82,22 @@ | ||||
|                                     </template> | ||||
|                                 </el-input> | ||||
|                             </el-form-item> | ||||
|                             <el-form-item :label="$t('setting.systemIP')" prop="systemIP"> | ||||
|                                 <el-input disabled v-if="form.systemIP" v-model="form.systemIP"> | ||||
|                                     <template #append> | ||||
|                                         <el-button @click="onChangeSystemIP" icon="Setting"> | ||||
|                                             {{ $t('commons.button.set') }} | ||||
|                                         </el-button> | ||||
|                                     </template> | ||||
|                                 </el-input> | ||||
|                                 <el-input disabled v-if="!form.systemIP" v-model="unset"> | ||||
|                                     <template #append> | ||||
|                                         <el-button @click="onChangeSystemIP" icon="Setting"> | ||||
|                                             {{ $t('commons.button.set') }} | ||||
|                                         </el-button> | ||||
|                                     </template> | ||||
|                                 </el-input> | ||||
|                             </el-form-item> | ||||
|                             <el-form-item :label="$t('setting.syncTime')"> | ||||
|                                 <el-input disabled v-model="form.localTime"> | ||||
|                                     <template #append> | ||||
| @@ -100,6 +116,7 @@ | ||||
|         <Password ref="passwordRef" /> | ||||
|         <UserName ref="userNameRef" /> | ||||
|         <PanelName ref="panelNameRef" @search="search()" /> | ||||
|         <SystemIP ref="systemIPRef" @search="search()" /> | ||||
|         <Timeout ref="timeoutRef" @search="search()" /> | ||||
|         <TimeZone ref="timezoneRef" @search="search()" /> | ||||
|         <Ntp ref="ntpRef" @search="search()" /> | ||||
| @@ -118,6 +135,7 @@ import Password from '@/views/setting/panel/password/index.vue'; | ||||
| import UserName from '@/views/setting/panel/username/index.vue'; | ||||
| import Timeout from '@/views/setting/panel/timeout/index.vue'; | ||||
| import PanelName from '@/views/setting/panel/name/index.vue'; | ||||
| import SystemIP from '@/views/setting/panel/systemip/index.vue'; | ||||
| import TimeZone from '@/views/setting/panel/timezone/index.vue'; | ||||
| import Ntp from '@/views/setting/panel/ntp/index.vue'; | ||||
|  | ||||
| @@ -136,6 +154,7 @@ const form = reactive({ | ||||
|     timeZone: '', | ||||
|     ntpSite: '', | ||||
|     panelName: '', | ||||
|     systemIP: '', | ||||
|     theme: '', | ||||
|     language: '', | ||||
|     complexityVerification: '', | ||||
| @@ -146,9 +165,11 @@ const show = ref(); | ||||
| const userNameRef = ref(); | ||||
| const passwordRef = ref(); | ||||
| const panelNameRef = ref(); | ||||
| const systemIPRef = ref(); | ||||
| const timeoutRef = ref(); | ||||
| const ntpRef = ref(); | ||||
| const timezoneRef = ref(); | ||||
| const unset = ref(i18n.t('setting.unSetting')); | ||||
|  | ||||
| const search = async () => { | ||||
|     const res = await getSettingInfo(); | ||||
| @@ -159,6 +180,7 @@ const search = async () => { | ||||
|     form.timeZone = res.data.timeZone; | ||||
|     form.ntpSite = res.data.ntpSite; | ||||
|     form.panelName = res.data.panelName; | ||||
|     form.systemIP = res.data.systemIP; | ||||
|     form.theme = res.data.theme; | ||||
|     form.language = res.data.language; | ||||
|     form.complexityVerification = res.data.complexityVerification; | ||||
| @@ -176,6 +198,9 @@ const onChangeTitle = () => { | ||||
| const onChangeTimeout = () => { | ||||
|     timeoutRef.value.acceptParams({ sessionTimeout: form.sessionTimeout }); | ||||
| }; | ||||
| const onChangeSystemIP = () => { | ||||
|     systemIPRef.value.acceptParams({ systemIP: form.systemIP }); | ||||
| }; | ||||
| const onChangeTimeZone = () => { | ||||
|     timezoneRef.value.acceptParams({ timeZone: form.timeZone }); | ||||
| }; | ||||
|   | ||||
							
								
								
									
										80
									
								
								frontend/src/views/setting/panel/systemip/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								frontend/src/views/setting/panel/systemip/index.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| <template> | ||||
|     <div> | ||||
|         <el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="30%"> | ||||
|             <template #header> | ||||
|                 <DrawerHeader :header="$t('setting.systemIP')" :back="handleClose" /> | ||||
|             </template> | ||||
|             <el-form ref="formRef" label-position="top" :model="form" @submit.prevent v-loading="loading"> | ||||
|                 <el-row type="flex" justify="center"> | ||||
|                     <el-col :span="22"> | ||||
|                         <el-form-item :label="$t('setting.systemIP')" prop="systemIP" :rules="Rules.ip"> | ||||
|                             <el-input clearable v-model="form.systemIP" /> | ||||
|                         </el-form-item> | ||||
|                     </el-col> | ||||
|                 </el-row> | ||||
|             </el-form> | ||||
|             <template #footer> | ||||
|                 <span class="dialog-footer"> | ||||
|                     <el-button @click="drawerVisiable = false">{{ $t('commons.button.cancel') }}</el-button> | ||||
|                     <el-button :disabled="loading" type="primary" @click="onSaveSystemIP(formRef)"> | ||||
|                         {{ $t('commons.button.confirm') }} | ||||
|                     </el-button> | ||||
|                 </span> | ||||
|             </template> | ||||
|         </el-drawer> | ||||
|     </div> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import { reactive, ref } from 'vue'; | ||||
| import i18n from '@/lang'; | ||||
| import { MsgSuccess } from '@/utils/message'; | ||||
| import { updateSetting } from '@/api/modules/setting'; | ||||
| import { FormInstance } from 'element-plus'; | ||||
| import { Rules } from '@/global/form-rules'; | ||||
| import DrawerHeader from '@/components/drawer-header/index.vue'; | ||||
|  | ||||
| const emit = defineEmits<{ (e: 'search'): void }>(); | ||||
|  | ||||
| interface DialogProps { | ||||
|     systemIP: string; | ||||
| } | ||||
| const drawerVisiable = ref(); | ||||
| const loading = ref(); | ||||
|  | ||||
| const form = reactive({ | ||||
|     systemIP: '', | ||||
| }); | ||||
|  | ||||
| const formRef = ref<FormInstance>(); | ||||
|  | ||||
| const acceptParams = (params: DialogProps): void => { | ||||
|     form.systemIP = params.systemIP; | ||||
|     drawerVisiable.value = true; | ||||
| }; | ||||
|  | ||||
| const onSaveSystemIP = async (formEl: FormInstance | undefined) => { | ||||
|     if (!formEl) return; | ||||
|     formEl.validate(async (valid) => { | ||||
|         if (!valid) return; | ||||
|         await updateSetting({ key: 'SystemIP', value: form.systemIP }) | ||||
|             .then(async () => { | ||||
|                 MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); | ||||
|                 loading.value = false; | ||||
|                 drawerVisiable.value = false; | ||||
|                 emit('search'); | ||||
|                 return; | ||||
|             }) | ||||
|             .catch(() => { | ||||
|                 loading.value = false; | ||||
|             }); | ||||
|     }); | ||||
| }; | ||||
|  | ||||
| const handleClose = () => { | ||||
|     drawerVisiable.value = false; | ||||
| }; | ||||
|  | ||||
| defineExpose({ | ||||
|     acceptParams, | ||||
| }); | ||||
| </script> | ||||
		Reference in New Issue
	
	Block a user
	 ssongliu
					ssongliu