Skip to content

Commit cd9315d

Browse files
committed
feat: 增加系统 IP,实现容器端口跳转功能
1 parent 38c0d29 commit cd9315d

File tree

10 files changed

+156
-8
lines changed

10 files changed

+156
-8
lines changed

backend/app/dto/setting.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import "time"
55
type SettingInfo struct {
66
UserName string `json:"userName"`
77
Email string `json:"email"`
8+
SystemIP string `json:"systemIP"`
89
SystemVersion string `json:"systemVersion"`
910

1011
SessionTimeout string `json:"sessionTimeout"`

backend/init/migration/migrations/init.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,9 @@ var AddMfaInterval = &gormigrate.Migration{
407407
if err := tx.Create(&model.Setting{Key: "MFAInterval", Value: "30"}).Error; err != nil {
408408
return err
409409
}
410+
if err := tx.Create(&model.Setting{Key: "SystemIP", Value: ""}).Error; err != nil {
411+
return err
412+
}
410413
return nil
411414
},
412415
}

frontend/src/api/interface/setting.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export namespace Setting {
55
userName: string;
66
password: string;
77
email: string;
8+
systemIP: string;
89
systemVersion: string;
910

1011
sessionTimeout: number;

frontend/src/lang/modules/en.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,8 @@ const message = {
969969
sessionTimeoutError: 'The minimum timeout is 300 seconds',
970970
sessionTimeoutHelper:
971971
'If you do not operate the panel for more than {0} seconds, the panel automatically logs out',
972+
systemIP: 'System IP',
973+
systemIPWarning: 'Please set the server IP in the panel settings first.',
972974
syncTime: 'Server time',
973975
timeZone: 'Time Zone',
974976
timeZoneChangeHelper: 'Changing the time zone requires restarting the service. Do you want to continue?',

frontend/src/lang/modules/zh.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,8 @@ const message = {
949949
sessionTimeout: '超时时间',
950950
sessionTimeoutError: '最小超时时间为 300 秒',
951951
sessionTimeoutHelper: '如果用户超过 {0} 秒未操作面板,面板将自动退出登录',
952+
systemIP: '服务器 IP',
953+
systemIPWarning: '请先在面板设置中设置服务器 IP',
952954
syncTime: '服务器时间',
953955
timeZone: '系统时区',
954956
timeZoneChangeHelper: '系统时区修改需要重启服务,是否继续?',

frontend/src/views/container/container/index.vue

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,16 @@
9090
<div v-if="row.ports">
9191
<div v-for="(item, index) in row.ports" :key="index">
9292
<div v-if="row.expand || (!row.expand && index < 3)">
93-
<el-tag class="tagMargin">{{ item }}</el-tag>
93+
<el-button
94+
@click="goDashboard(item)"
95+
class="tagMargin"
96+
icon="Position"
97+
type="primary"
98+
plain
99+
size="small"
100+
>
101+
{{ item }}
102+
</el-button>
94103
</div>
95104
</div>
96105
<div v-if="!row.expand && row.ports.length > 3">
@@ -154,8 +163,9 @@ import { Container } from '@/api/interface/container';
154163
import { ElMessageBox } from 'element-plus';
155164
import i18n from '@/lang';
156165
import router from '@/routers';
157-
import { MsgSuccess } from '@/utils/message';
166+
import { MsgError, MsgSuccess } from '@/utils/message';
158167
import { computeSize } from '@/utils/util';
168+
import { getSettingInfo } from '@/api/modules/setting';
159169
160170
const loading = ref();
161171
const data = ref();
@@ -184,6 +194,20 @@ const loadStatus = async () => {
184194
loading.value = false;
185195
});
186196
};
197+
198+
const goDashboard = async (port: any) => {
199+
if (!port || port.indexOf(':') === -1) {
200+
return;
201+
}
202+
let portEx = port.split(':')[0];
203+
const res = await getSettingInfo();
204+
if (!res.data.systemIP) {
205+
MsgError(i18n.global.t('setting.systemIPWarning'));
206+
return;
207+
}
208+
window.open(`http://${res.data.systemIP}:${portEx}`, '_blank');
209+
};
210+
187211
const goSetting = async () => {
188212
router.push({ name: 'ContainerSetting' });
189213
};

frontend/src/views/database/mysql/index.vue

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ import { GetAppPort } from '@/api/modules/app';
164164
import router from '@/routers';
165165
import { MsgError, MsgSuccess } from '@/utils/message';
166166
import useClipboard from 'vue-clipboard3';
167+
import { getSettingInfo } from '@/api/modules/setting';
167168
const { toClipboard } = useClipboard();
168169
169170
const loading = ref(false);
@@ -261,9 +262,12 @@ const goDashboard = async () => {
261262
phpVisiable.value = true;
262263
return;
263264
}
264-
let href = window.location.href;
265-
let ipLocal = href.split('//')[1].split(':')[0];
266-
window.open(`http://${ipLocal}:${phpadminPort.value}`, '_blank');
265+
const res = await getSettingInfo();
266+
if (!res.data.systemIP) {
267+
MsgError(i18n.global.t('setting.systemIPWarning'));
268+
return;
269+
}
270+
window.open(`http://${res.data.systemIP}:${phpadminPort.value}`, '_blank');
267271
};
268272
const getAppDetail = (key: string) => {
269273
router.push({ name: 'AppDetail', params: { appKey: key } });

frontend/src/views/database/redis/index.vue

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ import { nextTick, onBeforeUnmount, ref } from 'vue';
6565
import { App } from '@/api/interface/app';
6666
import { GetAppPort } from '@/api/modules/app';
6767
import router from '@/routers';
68+
import { MsgError } from '@/utils/message';
69+
import { getSettingInfo } from '@/api/modules/setting';
70+
import i18n from '@/lang';
6871
6972
const loading = ref(false);
7073
const maskShow = ref(true);
@@ -94,9 +97,12 @@ const goDashboard = async () => {
9497
commandVisiable.value = true;
9598
return;
9699
}
97-
let href = window.location.href;
98-
let ipLocal = href.split('//')[1].split(':')[0];
99-
window.open(`http://${ipLocal}:${redisCommandPort.value}`, '_blank');
100+
const res = await getSettingInfo();
101+
if (!res.data.systemIP) {
102+
MsgError(i18n.global.t('setting.systemIPWarning'));
103+
return;
104+
}
105+
window.open(`http://${res.data.systemIP}:${redisCommandPort.value}`, '_blank');
100106
};
101107
const getAppDetail = (key: string) => {
102108
router.push({ name: 'AppDetail', params: { appKey: key } });

frontend/src/views/setting/panel/index.vue

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,22 @@
8282
</template>
8383
</el-input>
8484
</el-form-item>
85+
<el-form-item :label="$t('setting.systemIP')" prop="systemIP">
86+
<el-input disabled v-if="form.systemIP" v-model="form.systemIP">
87+
<template #append>
88+
<el-button @click="onChangeSystemIP" icon="Setting">
89+
{{ $t('commons.button.set') }}
90+
</el-button>
91+
</template>
92+
</el-input>
93+
<el-input disabled v-if="!form.systemIP" v-model="unset">
94+
<template #append>
95+
<el-button @click="onChangeSystemIP" icon="Setting">
96+
{{ $t('commons.button.set') }}
97+
</el-button>
98+
</template>
99+
</el-input>
100+
</el-form-item>
85101
<el-form-item :label="$t('setting.syncTime')">
86102
<el-input disabled v-model="form.localTime">
87103
<template #append>
@@ -100,6 +116,7 @@
100116
<Password ref="passwordRef" />
101117
<UserName ref="userNameRef" />
102118
<PanelName ref="panelNameRef" @search="search()" />
119+
<SystemIP ref="systemIPRef" @search="search()" />
103120
<Timeout ref="timeoutRef" @search="search()" />
104121
<TimeZone ref="timezoneRef" @search="search()" />
105122
<Ntp ref="ntpRef" @search="search()" />
@@ -118,6 +135,7 @@ import Password from '@/views/setting/panel/password/index.vue';
118135
import UserName from '@/views/setting/panel/username/index.vue';
119136
import Timeout from '@/views/setting/panel/timeout/index.vue';
120137
import PanelName from '@/views/setting/panel/name/index.vue';
138+
import SystemIP from '@/views/setting/panel/systemip/index.vue';
121139
import TimeZone from '@/views/setting/panel/timezone/index.vue';
122140
import Ntp from '@/views/setting/panel/ntp/index.vue';
123141
@@ -136,6 +154,7 @@ const form = reactive({
136154
timeZone: '',
137155
ntpSite: '',
138156
panelName: '',
157+
systemIP: '',
139158
theme: '',
140159
language: '',
141160
complexityVerification: '',
@@ -146,9 +165,11 @@ const show = ref();
146165
const userNameRef = ref();
147166
const passwordRef = ref();
148167
const panelNameRef = ref();
168+
const systemIPRef = ref();
149169
const timeoutRef = ref();
150170
const ntpRef = ref();
151171
const timezoneRef = ref();
172+
const unset = ref(i18n.t('setting.unSetting'));
152173
153174
const search = async () => {
154175
const res = await getSettingInfo();
@@ -159,6 +180,7 @@ const search = async () => {
159180
form.timeZone = res.data.timeZone;
160181
form.ntpSite = res.data.ntpSite;
161182
form.panelName = res.data.panelName;
183+
form.systemIP = res.data.systemIP;
162184
form.theme = res.data.theme;
163185
form.language = res.data.language;
164186
form.complexityVerification = res.data.complexityVerification;
@@ -176,6 +198,9 @@ const onChangeTitle = () => {
176198
const onChangeTimeout = () => {
177199
timeoutRef.value.acceptParams({ sessionTimeout: form.sessionTimeout });
178200
};
201+
const onChangeSystemIP = () => {
202+
systemIPRef.value.acceptParams({ systemIP: form.systemIP });
203+
};
179204
const onChangeTimeZone = () => {
180205
timezoneRef.value.acceptParams({ timeZone: form.timeZone });
181206
};
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<template>
2+
<div>
3+
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="30%">
4+
<template #header>
5+
<DrawerHeader :header="$t('setting.systemIP')" :back="handleClose" />
6+
</template>
7+
<el-form ref="formRef" label-position="top" :model="form" @submit.prevent v-loading="loading">
8+
<el-row type="flex" justify="center">
9+
<el-col :span="22">
10+
<el-form-item :label="$t('setting.systemIP')" prop="systemIP" :rules="Rules.ip">
11+
<el-input clearable v-model="form.systemIP" />
12+
</el-form-item>
13+
</el-col>
14+
</el-row>
15+
</el-form>
16+
<template #footer>
17+
<span class="dialog-footer">
18+
<el-button @click="drawerVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
19+
<el-button :disabled="loading" type="primary" @click="onSaveSystemIP(formRef)">
20+
{{ $t('commons.button.confirm') }}
21+
</el-button>
22+
</span>
23+
</template>
24+
</el-drawer>
25+
</div>
26+
</template>
27+
<script lang="ts" setup>
28+
import { reactive, ref } from 'vue';
29+
import i18n from '@/lang';
30+
import { MsgSuccess } from '@/utils/message';
31+
import { updateSetting } from '@/api/modules/setting';
32+
import { FormInstance } from 'element-plus';
33+
import { Rules } from '@/global/form-rules';
34+
import DrawerHeader from '@/components/drawer-header/index.vue';
35+
36+
const emit = defineEmits<{ (e: 'search'): void }>();
37+
38+
interface DialogProps {
39+
systemIP: string;
40+
}
41+
const drawerVisiable = ref();
42+
const loading = ref();
43+
44+
const form = reactive({
45+
systemIP: '',
46+
});
47+
48+
const formRef = ref<FormInstance>();
49+
50+
const acceptParams = (params: DialogProps): void => {
51+
form.systemIP = params.systemIP;
52+
drawerVisiable.value = true;
53+
};
54+
55+
const onSaveSystemIP = async (formEl: FormInstance | undefined) => {
56+
if (!formEl) return;
57+
formEl.validate(async (valid) => {
58+
if (!valid) return;
59+
await updateSetting({ key: 'SystemIP', value: form.systemIP })
60+
.then(async () => {
61+
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
62+
loading.value = false;
63+
drawerVisiable.value = false;
64+
emit('search');
65+
return;
66+
})
67+
.catch(() => {
68+
loading.value = false;
69+
});
70+
});
71+
};
72+
73+
const handleClose = () => {
74+
drawerVisiable.value = false;
75+
};
76+
77+
defineExpose({
78+
acceptParams,
79+
});
80+
</script>

0 commit comments

Comments
 (0)