summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorzyh <[email protected]>2024-09-19 16:13:43 +0800
committerzyh <[email protected]>2024-09-19 16:13:43 +0800
commit3cdd54de65a323bcaace75ef32c0fe3028128999 (patch)
treea630611598f57adba0dc7d324d0e786a9cb62a5e /src
parent61949349cce42dddb8e9a19a53c3f25b9480a9ea (diff)
ASW-78 feat: Environment ACL界面开发
Diffstat (limited to 'src')
-rw-r--r--src/axios/api/environment.js43
-rw-r--r--src/components/mind/index.vue2
-rw-r--r--src/i18n/en.js7
-rw-r--r--src/i18n/zh.js7
-rw-r--r--src/styles/common.scss36
-rw-r--r--src/utils/validator.js20
-rw-r--r--src/views/environments/start.vue500
7 files changed, 496 insertions, 119 deletions
diff --git a/src/axios/api/environment.js b/src/axios/api/environment.js
index 3770fb3..9e8681b 100644
--- a/src/axios/api/environment.js
+++ b/src/axios/api/environment.js
@@ -192,3 +192,46 @@ export const environmentFileDownloadApi = async (
return err;
}
};
+
+// acl规则列表
+export const environmentAclListApi = async (envId, sessionId, data) => {
+ try {
+ const res = await axiosInstance({
+ url: `/api/v1/env/${envId}/session/${sessionId}/acl`,
+ method: 'GET',
+ params: data,
+ });
+ return res.data;
+ } catch (err) {
+ return err.data;
+ }
+};
+
+// 添加acl规则
+export const environmentAclAddApi = async (envId, sessionId, data) => {
+ try {
+ const res = await axiosInstance({
+ url: `/api/v1/env/${envId}/session/${sessionId}/acl`,
+ method: 'POST',
+ data: data,
+ });
+ return res.data;
+ } catch (err) {
+ return err.data;
+ }
+};
+
+// 删除acl规则
+export const environmentAclDeleteApi = async (envId, sessionId, data) => {
+ console.log(data)
+ try {
+ const res = await axiosInstance({
+ url: `/api/v1/env/${envId}/session/${sessionId}/acl`,
+ method: 'DELETE',
+ data: data,
+ });
+ return res.data;
+ } catch (err) {
+ return err.data;
+ }
+};
diff --git a/src/components/mind/index.vue b/src/components/mind/index.vue
index 97afcc0..de97c12 100644
--- a/src/components/mind/index.vue
+++ b/src/components/mind/index.vue
@@ -556,7 +556,7 @@ const nameValidator = (rule, value, callback) => {
if (validatorIp && validatorPort) {
callback();
} else {
- callback(t('validator.invalid_IP'));
+ callback(t('validator.invalid_ip'));
}
}
};
diff --git a/src/i18n/en.js b/src/i18n/en.js
index 84195ee..302b4a9 100644
--- a/src/i18n/en.js
+++ b/src/i18n/en.js
@@ -93,6 +93,7 @@ export default {
about: 'About',
type: 'Type',
ip: 'IP',
+ port: 'Port',
size: 'Size',
permission: 'Permission',
creator: 'Creator'
@@ -196,6 +197,7 @@ export default {
end: 'End',
tcpdump: 'Tcpdump',
acl: 'ACL',
+ terminal: 'Terminal',
upload_application_package: 'Upload application package',
installed: 'Installed',
uninstall: 'Uninstall',
@@ -214,6 +216,8 @@ export default {
fileName: 'File name',
quality: 'Quality',
compression_level: 'Compression level',
+ new_acl: 'New ACL',
+ protocol: 'Protocol',
},
job: {
create_job: 'Create Job',
@@ -226,7 +230,8 @@ export default {
},
validator: {
required: 'Required',
- invalid_IP: 'Invalid IP address',
+ invalid_ip: 'Invalid IP address',
+ invalid_port: 'Invalid Port',
at_least_eight: 'At Least eight',
password_error: "Only English letters, numbers, and special characters ~!{'@'}#$%^&*.?",
password_two_types: 'Must contain two or more types',
diff --git a/src/i18n/zh.js b/src/i18n/zh.js
index 08cd555..5079e4f 100644
--- a/src/i18n/zh.js
+++ b/src/i18n/zh.js
@@ -93,6 +93,7 @@ export default {
about: '关于',
type: '类型',
ip: 'IP',
+ port: '端口',
size: '大小',
permission: '权限',
creator: '创建者'
@@ -196,6 +197,7 @@ export default {
end: '结束',
tcpdump: 'Tcpdump',
acl: 'ACL',
+ terminal: '终端',
upload_application_package: '上传应用包',
installed: '已安装',
uninstall: '卸载',
@@ -214,6 +216,8 @@ export default {
fileName: '文件名',
quality: '画质',
compression_level: '压缩等级',
+ new_acl: '新ACL',
+ protocol: '协议',
},
job: {
create_job: '创建职位',
@@ -226,7 +230,8 @@ export default {
},
validator: {
required: '必填项',
- invalid_IP: '无效的IP地址',
+ invalid_ip: '无效的IP地址',
+ invalid_port: '无效的端口',
at_least_eight: '至少八位',
password_error: "仅限英文字母、数字和特殊字符~!{'@'}#$%^&*.?",
password_two_types: '必须包含两种或以上的类型',
diff --git a/src/styles/common.scss b/src/styles/common.scss
index 39947a0..cc4d2d9 100644
--- a/src/styles/common.scss
+++ b/src/styles/common.scss
@@ -8,7 +8,7 @@
.disabled {
cursor: not-allowed;
- color:var(--el-text-color-disabled) !important;
+ color: var(--el-text-color-disabled) !important;
}
.flex-center {
@@ -61,8 +61,9 @@
background-color: var(--el-dropdown-menuItem-hover-fill);
color: var(--el-dropdown-menuItem-hover-color);
}
-.el-upload-dragger.is-dragover{
- padding: var(--el-upload-dragger-padding-horizontal) var(--el-upload-dragger-padding-vertical);
+.el-upload-dragger.is-dragover {
+ padding: var(--el-upload-dragger-padding-horizontal)
+ var(--el-upload-dragger-padding-vertical);
border: 1px dashed var(--el-color-primary);
}
@@ -99,9 +100,9 @@
height: 50px;
width: 20px;
margin-right: 20px;
- .el-icon svg{
+ .el-icon svg {
color: var(--el-messagebox-title-color);
- transform: scale(1.5,1.5);
+ transform: scale(1.5, 1.5);
}
}
}
@@ -123,12 +124,29 @@
height: 40px;
}
.el-button--primary {
- background: #DD1718;
- border: #DD1718;
+ background: #dd1718;
+ border: #dd1718;
}
.el-button--primary:hover {
- background: #F89898;
- border: #F89898;
+ background: #f89898;
+ border: #f89898;
}
}
}
+
+.asw-dialog {
+ padding: 0;
+ .el-dialog__header {
+ padding: 12px 20px;
+ font-size: 16px;
+ color: var(--text);
+ font-weight: 600;
+ border-bottom: 1px solid var(--border);
+ }
+ .el-dialog__body {
+ padding: 0 20px;
+ }
+ .el-dialog__footer {
+ padding: 20px;
+ }
+}
diff --git a/src/utils/validator.js b/src/utils/validator.js
index fe733de..fd9ba5b 100644
--- a/src/utils/validator.js
+++ b/src/utils/validator.js
@@ -1,3 +1,6 @@
+import i18n from '@/i18n/index';
+const t = i18n.global.t;
+
export const IPv4_reg =
/^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/;
@@ -18,3 +21,20 @@ export const IPv6Range_reg =
export const port_reg =
/^([1-9](\d{0,3}))$|^([1-5]\d{4})$|^(6[0-4]\d{3})$|^(65[0-4]\d{2})$|^(655[0-2]\d)$|^(6553[0-5])$/;
+
+// ip校验
+export const ipValidator = (rule, value, callback) => {
+ if (IPv4_reg.test(value) || IPv6_reg.test(value)) {
+ callback();
+ } else {
+ callback(t('validator.invalid_ip'));
+ }
+};
+// port校验
+export const portValidator = (rule, value, callback) => {
+ if (port_reg.test(value)) {
+ callback();
+ } else {
+ callback(t('validator.invalid_port'));
+ }
+};
diff --git a/src/views/environments/start.vue b/src/views/environments/start.vue
index 32c77dd..ef05479 100644
--- a/src/views/environments/start.vue
+++ b/src/views/environments/start.vue
@@ -129,6 +129,61 @@
{{ t('environment.capture') }}
</el-button>
</el-tab-pane>
+ <!-- ACL -->
+ <el-tab-pane :label="t('environment.acl')" class="acl-tab">
+ <div class="acl-header">
+ <el-button type="primary" size="large" @click="newAcl">
+ {{ t('environment.new_acl') }}
+ </el-button>
+ </div>
+ <el-table v-loading="aclLoading" :data="aclData" class="acl-table">
+ <el-table-column
+ v-for="item in aclTableTitle"
+ :key="item.prop"
+ :min-width="`${item.minWidth}`"
+ :width="`${item.width}`"
+ :label="item.label"
+ :prop="item.prop"
+ :sortable="item.sortable"
+ >
+ <template #default="scope">
+ <template v-if="item.prop === 'protocol'">
+ <span style="text-transform: capitalize">
+ {{ scope.row.protocol }}
+ </span>
+ </template>
+ <template v-else-if="scope.row[item.prop]">
+ {{ scope.row[item.prop] }}
+ </template>
+ <template v-else>-</template>
+ </template>
+ </el-table-column>
+ <el-table-column
+ width="105"
+ fixed="right"
+ align="center"
+ :label="t('overall.actions')"
+ >
+ <template #default="scope">
+ <el-tooltip
+ effect="dark"
+ :content="t('overall.delete')"
+ placement="top"
+ >
+ <i
+ class="asw-icon icon-Delete cp"
+ style="font-size: 18px"
+ @click="aclDelete(scope.row)"
+ ></i>
+ </el-tooltip>
+ </template>
+ </el-table-column>
+ </el-table>
+ </el-tab-pane>
+ <!-- Terminal -->
+ <el-tab-pane :label="t('environment.terminal')" class="terminal-tab">
+ 999
+ </el-tab-pane>
<!-- file explorer -->
<el-tab-pane :label="t('environment.file_explorer')" class="files-tab">
<div class="directory-header">
@@ -238,7 +293,7 @@
align-center
width="480px"
:title="t('environment.attribute')"
- class="directory-dialog"
+ class="asw-dialog directory-dialog"
>
<div class="directory-dialog-name">
<svg v-if="directoryInfo.isDir">
@@ -299,13 +354,81 @@
</div>
</div>
</el-dialog>
+ <!-- acl对话框 -->
+ <el-dialog
+ v-if="aclVisible"
+ v-model="aclVisible"
+ align-center
+ width="580px"
+ :title="t('environment.new_acl')"
+ class="asw-dialog acl-dialog"
+ >
+ <el-form
+ ref="aclFormRef"
+ :model="aclForm"
+ :rules="aclRules"
+ label-width="auto"
+ label-position="top"
+ >
+ <!-- Protocol -->
+ <el-form-item
+ :label="t('environment.protocol') + ':'"
+ prop="protocol"
+ size="default"
+ >
+ <el-select
+ v-model="aclForm.protocol"
+ placeholder="Select"
+ size="default"
+ >
+ <el-option
+ v-for="item in protocolOptions"
+ :key="item.value"
+ :label="item.name"
+ :value="item.value"
+ />
+ </el-select>
+ </el-form-item>
+ <!-- ip -->
+ <el-form-item :label="t('overall.ip') + ':'" prop="ip" size="default">
+ <el-input
+ v-model="aclForm.ip"
+ :placeholder="t('overall.please_input')"
+ size="default"
+ />
+ </el-form-item>
+ <!-- port -->
+ <el-form-item :label="t('overall.port') + ':'" prop="port" size="default">
+ <el-input
+ v-model="aclForm.port"
+ :placeholder="t('overall.please_input')"
+ size="default"
+ />
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <div class="dialog-footer">
+ <el-button size="large" @click="cancelAcl">
+ {{ t('overall.cancel') }}
+ </el-button>
+ <el-button
+ size="large"
+ type="primary"
+ :disabled="aclSubmitting"
+ @click="saveAcl"
+ >
+ {{ t('overall.save') }}
+ </el-button>
+ </div>
+ </template>
+ </el-dialog>
<!-- 停止捕包对话框 -->
<el-dialog
v-model="captureVisible"
align-center
width="480px"
:title="t('environment.stop_capture')"
- class="capture-dialog"
+ class="asw-dialog capture-dialog"
>
<div class="capture-dialog-switch">
<span>{{ t('environment.save_pcap') }}</span>
@@ -338,7 +461,7 @@ import { useI18n } from 'vue-i18n';
import RFB from '@novnc/novnc/lib/rfb';
import mitter from '@/utils/mitter';
import { debounce } from 'lodash';
-import { bytes, downloadFile } from '@/utils';
+import { bytes, downloadFile, ipValidator, portValidator } from '@/utils';
import moment from 'moment-timezone';
import { get } from 'lodash';
import { useSystemStore } from '@/store/index';
@@ -353,6 +476,9 @@ import {
environmentFileUploadApi,
environmentFileListApi,
environmentFileDownloadApi,
+ environmentAclListApi,
+ environmentAclAddApi,
+ environmentAclDeleteApi,
} from '@/axios/api';
import { ElMessage, ElMessageBox } from 'element-plus';
import axiosInstance from '@/axios/index.js';
@@ -373,6 +499,99 @@ const disposeRoute = () => {
};
disposeRoute();
+// 获取设备信息
+const loading = ref(false);
+const environmentInfo = ref({});
+const getData = async () => {
+ loading.value = true;
+ const res = await environmentDetailApi(routeParams.id);
+ if (res.code == 200) {
+ environmentInfo.value = res.data.record;
+ } else {
+ ElMessage.error(res.msg || res.error);
+ }
+ loading.value = false;
+};
+
+// 连接novnc
+const novncRef = ref(null);
+let rfb = null;
+const connect = () => {
+ // 获取baseURL
+ let baseURL = axiosInstance.defaults.baseURL;
+ console.log(baseURL);
+ const protocol =
+ window.location.protocol.indexOf('https') > -1 ? 'wss' : 'ws';
+ let url = '';
+ if (baseURL.startsWith('/')) {
+ url = `${protocol}://${window.location.host}${baseURL}`;
+ } else {
+ url = baseURL.replace('http://', 'ws://').replace('https://', 'wss://');
+ }
+ if (!url.endsWith('/')) {
+ url += '/';
+ }
+
+ // 获取token
+ let userInfo = localStorage.getItem('asg-userInfo');
+ let tokenValue;
+ try {
+ userInfo = JSON.parse(userInfo);
+ tokenValue = userInfo.tokenValue;
+ } catch (error) {}
+ url += `api/v1/env/${routeParams.id}/session/${routeParams.sessionId}/novnc?token=${tokenValue}`;
+ rfb = new RFB(novncRef.value, url);
+ rfb.scaleViewport = true; // 远程会话是否应在本地缩放以适合其容器
+ rfb.clipViewport = true; // 远程会话是否应被剪裁到其容器,禁用时将显示滚动条以处理由此产生的溢出
+ rfb.resizeSession = false; // 每当容器尺寸调整是否发送调整远程会话大小的请求
+ rfb.showDotCursor = true; // 如果服务器设置了这种不可见光标,是否应显示点光标而不是零大小或完全透明的光标
+ rfb.viewOnly = false; // 是否应阻止将任何事件(例如按键或鼠标移动)发送到服务器
+ rfb.qualityLevel = quality.value; // 范围为[0-9]的整数,控制所需的JPEG质量。0表示低质量,9表示高质量。
+ rfb.compressionLevel = compressionLevel.value; // 范围为[0-9]的整数,用于控制所需的压缩级别。0表示不压缩。级别1使用最少的CPU资源,实现较弱的压缩比,而级别9提供最好的压缩,但在服务器端的CPU消耗方面很慢。
+
+ rfb._sock._websocket.onclose = (err) => {
+ if (err.reason) {
+ ElMessage.error(err.reason);
+ }
+ desktopEnd();
+ };
+};
+
+// novnc调整画质
+const qualityVisible = ref(false);
+const quality = ref(5);
+const qualityChange = () => {
+ rfb.qualityLevel = quality.value;
+};
+const compressionLevel = ref(5);
+const compressionLevelChange = () => {
+ rfb.compressionLevel = compressionLevel.value;
+};
+
+// back
+const environmentBack = (e) => {
+ rfb.sendKey('0xff1b', 'Escape', true);
+};
+// home
+const environmentHome = () => {
+ rfb.sendKey('0xff50', 'Home', true);
+};
+// task
+const environmentTask = () => {
+ rfb.sendKey('0xffe3', 'ControlLeft', true);
+ rfb.sendKey('0xffe1', 'ShiftLeft', true);
+ rfb.sendKey('0xff1b', 'Escape', true);
+ rfb.sendKey('0xffe3', 'ControlLeft', false);
+ rfb.sendKey('0xffe1', 'ShiftLeft', false);
+ rfb.sendKey('0xff1b', 'Escape', false);
+};
+
+const zmkm = () => {
+ if (packageName.value == 'zmkm') {
+ rfb.sendCtrlAltDel();
+ }
+};
+
// 已下载列表
const appLoading = ref(false);
const appList = ref([]);
@@ -596,81 +815,148 @@ const directoryDownload = async (data) => {
};
// 删除文件
const directoryDelete = async (data) => {};
-
const fileSize = (number) => {
const data = bytes(number, -1, 1);
return data.value + ' ' + data.unit;
};
-// 获取设备信息
-const loading = ref(false);
-const environmentInfo = ref({});
-const getData = async () => {
- loading.value = true;
- const res = await environmentDetailApi(routeParams.id);
+const aclTableTitle = ref([
+ {
+ minWidth: 100,
+ prop: 'protocol',
+ label: t('environment.protocol'),
+ sortable: false,
+ },
+ {
+ minWidth: 200,
+ prop: 'ip',
+ label: t('overall.ip'),
+ sortable: false,
+ },
+ {
+ minWidth: 200,
+ prop: 'port',
+ label: t('overall.port'),
+ sortable: false,
+ },
+]);
+const aclLoading = ref(false);
+const aclData = ref([]);
+const getAclData = async () => {
+ aclLoading.value = true;
+ const res = await environmentAclListApi(
+ routeParams.id,
+ routeParams.sessionId
+ );
if (res.code == 200) {
- environmentInfo.value = res.data.record;
+ const records = get(res, 'data.records', []);
+ aclData.value = records;
} else {
ElMessage.error(res.msg || res.error);
}
- loading.value = false;
+ aclLoading.value = false;
};
-
-// 连接novnc
-const novncRef = ref(null);
-let rfb = null;
-const connect = () => {
- // 获取baseURL
- let baseURL = axiosInstance.defaults.baseURL;
- console.log(baseURL);
- const protocol =
- window.location.protocol.indexOf('https') > -1 ? 'wss' : 'ws';
- let url = '';
- if (baseURL.startsWith('/')) {
- url = `${protocol}://${window.location.host}${baseURL}`;
- } else {
- url = baseURL.replace('http://', 'ws://').replace('https://', 'wss://');
- }
- if (!url.endsWith('/')) {
- url += '/';
+const aclVisible = ref(false);
+const aclFormRef = ref(null);
+const aclRules = reactive({
+ ip: [
+ { required: true, message: t('validator.required'), trigger: 'blur' },
+ { validator: ipValidator, trigger: 'blur' },
+ ],
+ port: [
+ { required: true, message: t('validator.required'), trigger: 'blur' },
+ { validator: portValidator, trigger: 'blur' },
+ ],
+});
+const protocolOptions = reactive([
+ { name: 'All', value: 'all' },
+ { name: 'Tcp', value: 'tcp' },
+ { name: 'Udp', value: 'udp' },
+]);
+const aclForm = ref({
+ protocol: 'all',
+ ip: '',
+ port: '',
+});
+// 显示新增弹窗
+const newAcl = () => {
+ aclForm.value = {
+ protocol: 'all',
+ ip: '',
+ port: '',
+ };
+ aclVisible.value = true;
+};
+const aclSubmitting = ref(false);
+// 新增acl
+const saveAcl = async () => {
+ if (aclSubmitting.value) {
+ return;
}
-
- // 获取token
- let userInfo = localStorage.getItem('asg-userInfo');
- let tokenValue;
- try {
- userInfo = JSON.parse(userInfo);
- tokenValue = userInfo.tokenValue;
- } catch (error) {}
- url += `api/v1/env/${routeParams.id}/session/${routeParams.sessionId}/novnc?token=${tokenValue}`;
- rfb = new RFB(novncRef.value, url);
- rfb.scaleViewport = true; // 远程会话是否应在本地缩放以适合其容器
- rfb.clipViewport = true; // 远程会话是否应被剪裁到其容器,禁用时将显示滚动条以处理由此产生的溢出
- rfb.resizeSession = false; // 每当容器尺寸调整是否发送调整远程会话大小的请求
- rfb.showDotCursor = true; // 如果服务器设置了这种不可见光标,是否应显示点光标而不是零大小或完全透明的光标
- rfb.viewOnly = false; // 是否应阻止将任何事件(例如按键或鼠标移动)发送到服务器
- rfb.qualityLevel = quality.value; // 范围为[0-9]的整数,控制所需的JPEG质量。0表示低质量,9表示高质量。
- rfb.compressionLevel = compressionLevel.value; // 范围为[0-9]的整数,用于控制所需的压缩级别。0表示不压缩。级别1使用最少的CPU资源,实现较弱的压缩比,而级别9提供最好的压缩,但在服务器端的CPU消耗方面很慢。
-
- rfb._sock._websocket.onclose = (err) => {
- if (err.reason) {
- ElMessage.error(err.reason);
+ aclSubmitting.value = true;
+ await aclFormRef.value.validate(async (valid) => {
+ if (valid) {
+ const params = {
+ protocol: aclForm.value.protocol,
+ ip: aclForm.value.ip,
+ port: aclForm.value.port,
+ };
+ const res = await environmentAclAddApi(
+ routeParams.id,
+ routeParams.sessionId,
+ params
+ );
+ if (res.code == 200) {
+ aclVisible.value = false;
+ ElMessage.success(t('message.save_success'));
+ getAclData();
+ } else {
+ ElMessage.error(res.msg || res.error);
+ }
}
- desktopEnd();
- };
+ });
+ aclSubmitting.value = false;
};
-
-// novnc调整画质
-const qualityVisible = ref(false);
-const quality = ref(5);
-const qualityChange = () => {
- rfb.qualityLevel = quality.value;
+const cancelAcl = () => {
+ aclVisible.value = false;
};
-const compressionLevel = ref(5);
-const compressionLevelChange = () => {
- rfb.compressionLevel = compressionLevel.value;
+// 删除acl
+const aclDelete = async (row) => {
+ ElMessageBox.confirm(
+ t('env_mgt.delete_hint_message', {
+ environment: t('environment.acl'),
+ }),
+ t('env_mgt.delete_hint_title'),
+ {
+ confirmButtonText: t('overall.delete'),
+ cancelButtonText: t('overall.cancel'),
+ iconClass: 'width:0px;height:0px;',
+ customClass: 'delete-box',
+ }
+ )
+ .then(async () => {
+ const params = {
+ protocol: row.protocol,
+ ip: row.ip,
+ port: row.port,
+ };
+ const res = await environmentAclDeleteApi(
+ routeParams.id,
+ routeParams.sessionId,
+ params
+ );
+ console.log(res);
+ if (res.code == 200) {
+ getAclData();
+ ElMessage.success(t('message.save_success'));
+ } else {
+ ElMessage.error(res.msg || res.error);
+ }
+ })
+ .catch((e) => {});
};
+// 结束操作
const desktopEnd = () => {
childEnd();
window.close();
@@ -682,7 +968,6 @@ const desktopEnd = () => {
},
});
};
-
// 结束会话
mitter.on('desktopEnd', async () => {
const params = {
@@ -699,7 +984,6 @@ mitter.on('desktopEnd', async () => {
ElMessage.error(res.msg || res.error);
}
});
-
// 向父页面发送end消息
const childEnd = () => {
if (window.opener) {
@@ -709,32 +993,9 @@ const childEnd = () => {
}
};
-// back
-const environmentBack = (e) => {
- rfb.sendKey('0xff1b', 'Escape', true);
-};
-// home
-const environmentHome = () => {
- rfb.sendKey('0xff50', 'Home', true);
-};
-// task
-const environmentTask = () => {
- rfb.sendKey('0xffe3', 'ControlLeft', true);
- rfb.sendKey('0xffe1', 'ShiftLeft', true);
- rfb.sendKey('0xff1b', 'Escape', true);
- rfb.sendKey('0xffe3', 'ControlLeft', false);
- rfb.sendKey('0xffe1', 'ShiftLeft', false);
- rfb.sendKey('0xff1b', 'Escape', false);
-};
-
-const zmkm = () => {
- if (packageName.value == 'zmkm') {
- rfb.sendCtrlAltDel();
- }
-};
-
onMounted(async () => {
getAppList();
+ getAclData();
getDirectory(directoryPath.value);
getData();
connect();
@@ -845,6 +1106,7 @@ onBeforeUnmount(() => {
}
.el-tabs__content {
height: 100%;
+
.application-tab {
height: 100%;
padding: 20px 0;
@@ -925,6 +1187,36 @@ onBeforeUnmount(() => {
}
}
}
+
+ .acl-tab {
+ height: 100%;
+ padding: 20px;
+ padding-top: 10px;
+ .acl-header {
+ display: flex;
+ justify-content: flex-end;
+ padding-bottom: 10px;
+ height: 50px;
+ }
+ .acl-table {
+ height: calc(100% - 50px);
+ .el-table__header {
+ .el-table__cell {
+ padding: 6px 0;
+ }
+ }
+ .el-table__body {
+ .el-table__cell {
+ padding: 8px 0;
+ }
+ }
+ }
+ }
+
+ .terminal-tab {
+ padding: 20px;
+ }
+
.tcpdump-tab {
height: 100%;
padding: 50px 80px;
@@ -1042,14 +1334,6 @@ onBeforeUnmount(() => {
.environment_start-page {
padding: 0 !important;
.directory-dialog {
- padding: 0;
- .el-dialog__header {
- padding: 12px 20px;
- font-size: 16px;
- color: var(--text);
- font-weight: 600;
- border-bottom: 1px solid var(--border);
- }
.el-dialog__body {
padding: 0 20px;
.directory-dialog-name {
@@ -1081,16 +1365,7 @@ onBeforeUnmount(() => {
}
.capture-dialog {
- padding: 0;
- .el-dialog__header {
- padding: 12px 20px;
- font-size: 16px;
- color: var(--text);
- font-weight: 600;
- border-bottom: 1px solid var(--border);
- }
.el-dialog__body {
- padding: 0 20px;
padding-top: 20px;
.capture-dialog-switch {
display: flex;
@@ -1112,8 +1387,19 @@ onBeforeUnmount(() => {
}
}
}
- .el-dialog__footer {
- padding: 20px;
+ }
+
+ .acl-dialog {
+ .el-dialog__body {
+ padding-top: 15px;
+ .el-form-item__label {
+ margin-bottom: 6px;
+ font-weight: 600;
+ color: var(--text);
+ }
+ .el-form-item {
+ margin-bottom: 15px;
+ }
}
}
}