fix: 容器端口跳转样式调整 (#1533)

This commit is contained in:
ssongliu 2023-07-05 10:20:13 +08:00 committed by GitHub
parent 3cbaa052c8
commit bd2facebee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 131 additions and 55 deletions

View file

@ -144,10 +144,11 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
var ports []string
for _, port := range item.Ports {
if port.IP == "::" || port.PublicPort == 0 {
continue
itemPortStr := fmt.Sprintf("%v/%s", port.PrivatePort, port.Type)
if port.PublicPort != 0 {
itemPortStr = fmt.Sprintf("%s:%v->%v/%s", port.IP, port.PublicPort, port.PrivatePort, port.Type)
}
ports = append(ports, fmt.Sprintf("%v:%v/%s", port.PublicPort, port.PrivatePort, port.Type))
ports = append(ports, itemPortStr)
}
cpu, mem := loadCpuAndMem(client, item.ID)
backDatas[i] = dto.ContainerInfo{

View file

@ -0,0 +1,55 @@
<template>
<div>
<el-dialog
v-model="dialogVisiable"
:title="$t('app.checkTitle')"
width="30%"
:close-on-click-modal="false"
:destroy-on-close="true"
>
<el-alert :closable="false" :title="$t('setting.systemIPWarning')" type="info">
<el-link icon="Position" @click="goRouter('/settings/panel')" type="primary">
{{ $t('firewall.quickJump') }}
</el-link>
</el-alert>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { getSettingInfo } from '@/api/modules/setting';
import i18n from '@/lang';
import { MsgError } from '@/utils/message';
import { useRouter } from 'vue-router';
const router = useRouter();
const dialogVisiable = ref();
interface DialogProps {
port: any;
}
const acceptParams = async (params: DialogProps): Promise<void> => {
if (Number(params.port) === 0) {
MsgError(i18n.global.t('setting.errPort'));
return;
}
const res = await getSettingInfo();
if (!res.data.systemIP) {
dialogVisiable.value = true;
return;
}
window.open(`http://${res.data.systemIP}:${params.port}`, '_blank');
};
const goRouter = async (path: string) => {
router.push({ path: path });
};
defineExpose({ acceptParams });
</script>

View file

@ -468,6 +468,7 @@ const message = {
cleanSuccessWithSpace:
'The operation is successful. The number of disks cleared this time is {0}. The disk space freed is {1}!',
container: 'Container',
unExposedPort: 'The current port mapping address is 127.0.0.1, which cannot enable external access.',
upTime: 'UpTime',
all: 'All',
fetch: 'Fetch',
@ -937,7 +938,7 @@ const message = {
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.',
systemIPWarning: 'The server IP is not currently set. Please set it in the control panel first!',
syncTime: 'Server time',
timeZone: 'Time Zone',
timeZoneChangeHelper: 'Changing the time zone requires restarting the service. Do you want to continue?',

View file

@ -432,7 +432,8 @@ const message = {
localIP: '本機 IP',
},
container: {
createContainer: '創建容器',
create: '創建容器',
edit: '編輯容器',
updateContaienrHelper: '容器編輯需要重建容器任何未持久化的數據將會丟失是否繼續',
containerList: '容器列表',
operatorHelper: '將對選中容器進行 {0} 操作是否繼續',
@ -459,6 +460,7 @@ const message = {
cleanSuccess: '操作成功本次清理數量: {0} ',
cleanSuccessWithSpace: '操作成功本次清理數量: {0} 釋放磁盤空間: {1}',
container: '容器',
unExposedPort: '當前端口映射地址為 127.0.0.1無法實現外部訪問',
upTime: '運行時長',
all: '全部',
fetch: '過濾',
@ -621,6 +623,8 @@ const message = {
startIn: '中開啟',
},
cronjob: {
create: '創建計劃任務',
edit: '編輯計劃任務',
cronTask: '計劃任務',
changeStatus: '狀態修改',
disableMsg: '停止計劃任務會導致該任務不再自動執行是否繼續',
@ -898,7 +902,7 @@ const message = {
sessionTimeoutError: '最小超時時間為 300 ',
sessionTimeoutHelper: '如果用戶超過 {0} 秒未操作面板面板將自動退出登錄',
systemIP: '服務器 IP',
systemIPWarning: '請先在面板設置中設置服務器 IP',
systemIPWarning: '當前未設置服務器 IP請先在面板設置中設置',
syncTime: '服務器時間',
timeZone: '系統時區',
timeZoneChangeHelper: '系統時區修改需要重啟服務是否繼續',
@ -1406,6 +1410,8 @@ const message = {
acmeHelper: 'Acme 賬戶用於申請免費證書',
},
firewall: {
create: '創建規則',
edit: '編輯規則',
notSupport: '未檢測到系統防火墻firewalld 或者 ufw請參考官方文檔進行安裝',
ccDeny: 'CC 防護',
ipWhiteList: 'IP 白名單',

View file

@ -432,7 +432,8 @@ const message = {
localIP: '本机 IP',
},
container: {
createContainer: '创建容器',
create: '创建容器',
edit: '编辑容器',
updateContaienrHelper: '容器编辑需要重建容器任何未持久化的数据将会丢失是否继续',
containerList: '容器列表',
operatorHelper: '将对选中容器进行 {0} 操作是否继续',
@ -459,6 +460,7 @@ const message = {
cleanSuccess: '操作成功本次清理数量: {0} ',
cleanSuccessWithSpace: '操作成功本次清理数量: {0} 释放磁盘空间: {1}',
container: '容器',
unExposedPort: '当前端口映射地址为 127.0.0.1无法实现外部访问',
upTime: '运行时长',
all: '全部',
fetch: '过滤',
@ -621,6 +623,8 @@ const message = {
startIn: '中开启',
},
cronjob: {
create: '创建计划任务',
edit: '编辑计划任务',
cronTask: '计划任务',
changeStatus: '状态修改',
disableMsg: '停止计划任务会导致该任务不再自动执行是否继续',
@ -904,7 +908,7 @@ const message = {
sessionTimeoutError: '最小超时时间为 300 ',
sessionTimeoutHelper: '如果用户超过 {0} 秒未操作面板面板将自动退出登录',
systemIP: '服务器 IP',
systemIPWarning: '请先在面板设置中设置服务器 IP',
systemIPWarning: '当前未设置服务器 IP请先在面板设置中设置',
syncTime: '服务器时间',
timeZone: '系统时区',
timeZoneChangeHelper: '系统时区修改需要重启服务是否继续',
@ -1412,6 +1416,8 @@ const message = {
acmeHelper: 'Acme 账户用于申请免费证书',
},
firewall: {
create: '创建规则',
edit: '编辑规则',
notSupport: '未检测到系统防火墙firewalld 或者 ufw请参考官方文档进行安装',
ccDeny: 'CC 防护',
ipWhiteList: 'IP 白名单',

View file

@ -1,6 +1,4 @@
import { getSettingInfo } from '@/api/modules/setting';
import i18n from '@/lang';
import { MsgError } from './message';
export function deepCopy<T>(obj: any): T {
let newObj: any;
@ -258,16 +256,3 @@ export function downloadFile(filePath: string) {
let url = `${import.meta.env.VITE_API_URL as string}/files/download?`;
window.open(url + 'path=' + filePath, '_blank');
}
export async function JumpDashboard(port: any) {
if (Number(port) === 0) {
MsgError(i18n.global.t('setting.errPort'));
return;
}
const res = await getSettingInfo();
if (!res.data.systemIP) {
MsgError(i18n.global.t('setting.systemIPWarning'));
return;
}
window.open(`http://${res.data.systemIP}:${port}`, '_blank');
}

View file

@ -193,7 +193,7 @@
<el-tag
class="middle-center"
v-if="installed.httpPort > 0"
@click="JumpDashboard(installed.httpPort)"
@click="goDashboard(installed.httpPort)"
>
<el-icon class="middle-center"><Position /></el-icon>
{{ $t('app.busPort') }}{{ installed.httpPort }}
@ -239,6 +239,8 @@
<AppDelete ref="deleteRef" @close="search" />
<AppParams ref="appParamRef" />
<AppUpgrade ref="upgradeRef" @close="search" />
<PortJumpDialog ref="dialogPortJumpRef" />
</template>
<script lang="ts" setup>
@ -254,6 +256,7 @@ import i18n from '@/lang';
import { ElMessageBox } from 'element-plus';
import Backups from '@/components/backup/index.vue';
import Uploads from '@/components/upload/index.vue';
import PortJumpDialog from '@/components/port-jump/index.vue';
import AppResources from './check/index.vue';
import AppDelete from './delete/index.vue';
import AppParams from './detail/index.vue';
@ -264,7 +267,6 @@ import { getAge } from '@/utils/util';
import { useRouter } from 'vue-router';
import { MsgSuccess } from '@/utils/message';
import { toFolder } from '@/global/business';
import { JumpDashboard } from '@/utils/util';
const data = ref<any>();
const loading = ref(false);
@ -287,6 +289,7 @@ const checkRef = ref();
const deleteRef = ref();
const appParamRef = ref();
const upgradeRef = ref();
const dialogPortJumpRef = ref();
const tags = ref<App.Tag[]>([]);
const activeTag = ref('all');
const searchReq = reactive({
@ -338,6 +341,10 @@ const search = () => {
});
};
const goDashboard = async (port: any) => {
dialogPortJumpRef.value.acceptParams({ port: port });
};
const openOperate = (row: any, op: string) => {
operateReq.installId = row.id;
operateReq.operate = op;

View file

@ -10,7 +10,7 @@
<el-row>
<el-col :xs="24" :sm="16" :md="16" :lg="16" :xl="16">
<el-button type="primary" @click="onOpenDialog('create')">
{{ $t('container.createContainer') }}
{{ $t('container.create') }}
</el-button>
<el-button type="primary" plain @click="onClean()">
{{ $t('container.containerPrune') }}
@ -80,18 +80,19 @@
<Status :key="row.state" :status="row.state"></Status>
</template>
</el-table-column>
<el-table-column :label="$t('container.source')" show-overflow-tooltip min-width="125" fix>
<el-table-column :label="$t('container.source')" show-overflow-tooltip min-width="75" fix>
<template #default="{ row }">
CPU: {{ row.cpuPercent.toFixed(2) }}% {{ $t('monitor.memory') }}:
{{ row.memoryPercent.toFixed(2) }}%
<div>CPU: {{ row.cpuPercent.toFixed(2) }}%</div>
<div>{{ $t('monitor.memory') }}: {{ row.memoryPercent.toFixed(2) }}%</div>
</template>
</el-table-column>
<el-table-column :label="$t('commons.table.port')" min-width="80" prop="ports" fix>
<el-table-column :label="$t('commons.table.port')" min-width="120" prop="ports" fix>
<template #default="{ row }">
<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-button
v-if="item.indexOf('->') !== -1"
@click="goDashboard(item)"
class="tagMargin"
icon="Position"
@ -101,6 +102,9 @@
>
{{ item }}
</el-button>
<el-button v-else class="tagMargin" type="primary" plain size="small">
{{ item }}
</el-button>
</div>
</div>
<div v-if="!row.expand && row.ports.length > 3">
@ -137,6 +141,8 @@
<UpgraeDialog @search="search" ref="dialogUpgradeRef" />
<MonitorDialog ref="dialogMonitorRef" />
<TerminalDialog ref="dialogTerminalRef" />
<PortJumpDialog ref="dialogPortJumpRef" />
</div>
</template>
@ -150,6 +156,7 @@ import MonitorDialog from '@/views/container/container/monitor/index.vue';
import ContainerLogDialog from '@/views/container/container/log/index.vue';
import TerminalDialog from '@/views/container/container/terminal/index.vue';
import CodemirrorDialog from '@/components/codemirror-dialog/index.vue';
import PortJumpDialog from '@/components/port-jump/index.vue';
import Status from '@/components/status/index.vue';
import { reactive, onMounted, ref } from 'vue';
import {
@ -164,8 +171,8 @@ 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 { JumpDashboard, computeSize } from '@/utils/util';
import { MsgSuccess, MsgWarning } from '@/utils/message';
import { computeSize } from '@/utils/util';
const loading = ref();
const data = ref();
@ -177,6 +184,7 @@ const paginationConfig = reactive({
});
const searchName = ref();
const dialogUpgradeRef = ref();
const dialogPortJumpRef = ref();
const dockerStatus = ref('Running');
const loadStatus = async () => {
@ -196,11 +204,16 @@ const loadStatus = async () => {
};
const goDashboard = async (port: any) => {
if (!port || port.indexOf(':') === -1) {
if (port.indexOf('127.0.0.1') !== -1) {
MsgWarning(i18n.global.t('container.unExposedPort'));
return;
}
let portEx = port.split(':')[0];
JumpDashboard(portEx);
if (!port || port.indexOf(':') === -1 || port.indexOf('->') === -1) {
MsgWarning(i18n.global.t('commons.msg.errPort'));
return;
}
let portEx = port.match(/:(\d+)/)[1];
dialogPortJumpRef.value.acceptParams({ port: portEx });
};
const goSetting = async () => {

View file

@ -1,7 +1,7 @@
<template>
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<DrawerHeader :header="$t('container.createContainer')" :back="handleClose" />
<DrawerHeader :header="title" :back="handleClose" />
</template>
<el-form
ref="formRef"
@ -242,7 +242,7 @@ const dialogData = ref<DialogProps>({
});
const acceptParams = (params: DialogProps): void => {
dialogData.value = params;
title.value = i18n.global.t('commons.button.' + dialogData.value.title);
title.value = i18n.global.t('container.' + dialogData.value.title);
if (params.title === 'edit') {
dialogData.value.rowData.memoryItem = Number((dialogData.value.rowData.memory / 1024 / 1024).toFixed(2));
let itemCmd = '';
@ -277,7 +277,7 @@ const handleClose = () => {
};
const rules = reactive({
cpuShares: [Rules.number, checkNumberRange(2, 262144)],
cpuShares: [Rules.number, checkNumberRange(0, 262144)],
name: [Rules.requiredInput, Rules.name],
image: [Rules.requiredSelect],
nanoCPUs: [Rules.number],

View file

@ -1,7 +1,7 @@
<template>
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<DrawerHeader :header="$t('cronjob.cronTask')" :resource="dialogData.rowData?.name" :back="handleClose" />
<DrawerHeader :header="title" :resource="dialogData.rowData?.name" :back="handleClose" />
</template>
<el-form ref="formRef" label-position="top" :model="dialogData.rowData" :rules="rules">
<el-row type="flex" justify="center">
@ -238,7 +238,7 @@ const acceptParams = (params: DialogProps): void => {
if (dialogData.value.title === 'create') {
changeType();
}
title.value = i18n.global.t('commons.button.' + dialogData.value.title);
title.value = i18n.global.t('cronjob.' + dialogData.value.title);
if (dialogData.value?.rowData?.exclusionRules) {
dialogData.value.rowData.exclusionRules = dialogData.value.rowData.exclusionRules.replaceAll(',', '\n');
}

View file

@ -141,6 +141,8 @@
<AppResources ref="checkRef"></AppResources>
<DeleteDialog ref="deleteRef" @search="search" />
<PortJumpDialog ref="dialogPortJumpRef" />
</div>
</template>
@ -155,7 +157,8 @@ import Setting from '@/views/database/mysql/setting/index.vue';
import AppStatus from '@/components/app-status/index.vue';
import Backups from '@/components/backup/index.vue';
import UploadDialog from '@/components/upload/index.vue';
import { JumpDashboard, dateFormat } from '@/utils/util';
import PortJumpDialog from '@/components/port-jump/index.vue';
import { dateFormat } from '@/utils/util';
import { reactive, ref } from 'vue';
import { deleteCheckMysqlDB, loadRemoteAccess, searchMysqlDBs, updateMysqlDescription } from '@/api/modules/database';
import i18n from '@/lang';
@ -179,6 +182,8 @@ const deleteRef = ref();
const phpadminPort = ref();
const phpVisiable = ref(false);
const dialogPortJumpRef = ref();
const data = ref();
const paginationConfig = reactive({
currentPage: 1,
@ -264,7 +269,7 @@ const goDashboard = async () => {
phpVisiable.value = true;
return;
}
JumpDashboard(phpadminPort.value);
dialogPortJumpRef.value.acceptParams({ port: phpadminPort.value });
};
const getAppDetail = (key: string) => {

View file

@ -53,6 +53,8 @@
</span>
</template>
</el-dialog>
<PortJumpDialog ref="dialogPortJumpRef" />
</div>
</template>
@ -61,13 +63,11 @@ import Setting from '@/views/database/redis/setting/index.vue';
import Password from '@/views/database/redis/password/index.vue';
import Terminal from '@/components/terminal/index.vue';
import AppStatus from '@/components/app-status/index.vue';
import PortJumpDialog from '@/components/port-jump/index.vue';
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);
@ -83,6 +83,8 @@ const terminalShow = ref(false);
const redisCommandPort = ref();
const commandVisiable = ref(false);
const dialogPortJumpRef = ref();
const isRefresh = ref();
const onSetting = async () => {
@ -97,12 +99,7 @@ const goDashboard = async () => {
commandVisiable.value = true;
return;
}
const res = await getSettingInfo();
if (!res.data.systemIP) {
MsgError(i18n.global.t('setting.systemIPWarning'));
return;
}
window.open(`http://${res.data.systemIP}:${redisCommandPort.value}`, '_blank');
dialogPortJumpRef.value.acceptParams({ port: redisCommandPort.value });
};
const getAppDetail = (key: string) => {
router.push({ name: 'AppDetail', params: { appKey: key } });

View file

@ -1,7 +1,7 @@
<template>
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<DrawerHeader :header="$t('firewall.ipRule')" :back="handleClose" />
<DrawerHeader :header="title" :back="handleClose" />
</template>
<div v-loading="loading">
<el-form ref="formRef" label-position="top" :model="dialogData.rowData" :rules="rules">
@ -68,7 +68,7 @@ const acceptParams = (params: DialogProps): void => {
if (dialogData.value.title === 'edit') {
oldRule.value = deepCopy(params.rowData);
}
title.value = i18n.global.t('commons.button.' + dialogData.value.title);
title.value = i18n.global.t('firewall.' + dialogData.value.title);
drawerVisiable.value = true;
};
const emit = defineEmits<{ (e: 'search'): void }>();

View file

@ -1,7 +1,7 @@
<template>
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<DrawerHeader :header="$t('firewall.portRule')" :back="handleClose" />
<DrawerHeader :header="title" :back="handleClose" />
</template>
<div v-loading="loading">
<el-form ref="formRef" label-position="top" :model="dialogData.rowData" :rules="rules">
@ -98,7 +98,7 @@ const acceptParams = (params: DialogProps): void => {
}
oldRule.value = deepCopy(params.rowData);
}
title.value = i18n.global.t('commons.button.' + dialogData.value.title);
title.value = i18n.global.t('firewall.' + dialogData.value.title);
drawerVisiable.value = true;
};
const emit = defineEmits<{ (e: 'search'): void }>();