Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev
This commit is contained in:
commit
e70b8d7812
|
@ -109,10 +109,7 @@
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
v-if="
|
v-if="isSecurityMode"
|
||||||
formData.configuration.securityMode === 'SignAndEncrypt' ||
|
|
||||||
formData.configuration.securityMode === 'Sign'
|
|
||||||
"
|
|
||||||
label="证书"
|
label="证书"
|
||||||
:name="['configuration', 'certificate']"
|
:name="['configuration', 'certificate']"
|
||||||
:rules="FormValidate.certificate"
|
:rules="FormValidate.certificate"
|
||||||
|
@ -141,7 +138,7 @@
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
v-if="formData.configuration.authType === 'username'"
|
v-if="isAuthType"
|
||||||
label="用户名"
|
label="用户名"
|
||||||
:name="['configuration', 'username']"
|
:name="['configuration', 'username']"
|
||||||
:rules="FormValidate.username"
|
:rules="FormValidate.username"
|
||||||
|
@ -152,10 +149,7 @@
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
v-if="
|
v-if="isAuthType"
|
||||||
formData.configuration.authType === 'username' ||
|
|
||||||
formData.configuration.authType === ['username']
|
|
||||||
"
|
|
||||||
label="密码"
|
label="密码"
|
||||||
:name="['configuration', 'password']"
|
:name="['configuration', 'password']"
|
||||||
:rules="FormValidate.password"
|
:rules="FormValidate.password"
|
||||||
|
@ -201,6 +195,7 @@ import {
|
||||||
import { FormValidate, FormState } from '../data';
|
import { FormValidate, FormState } from '../data';
|
||||||
import type { FormInstance } from 'ant-design-vue';
|
import type { FormInstance } from 'ant-design-vue';
|
||||||
import type { FormDataType } from '../type.d';
|
import type { FormDataType } from '../type.d';
|
||||||
|
import { isArray } from 'lodash-es';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
|
@ -243,6 +238,18 @@ const handleCancel = () => {
|
||||||
const changeAuthType = (value: Array<string>) => {
|
const changeAuthType = (value: Array<string>) => {
|
||||||
formData.value.configuration.authType = value[0];
|
formData.value.configuration.authType = value[0];
|
||||||
};
|
};
|
||||||
|
const isAuthType = computed(() => {
|
||||||
|
const { authType } = formData.value.configuration;
|
||||||
|
return isArray(authType)
|
||||||
|
? authType[0] === 'username'
|
||||||
|
: authType === 'username';
|
||||||
|
});
|
||||||
|
const isSecurityMode = computed(() => {
|
||||||
|
const { securityMode } = formData.value.configuration;
|
||||||
|
return securityMode === 'SignAndEncrypt' || securityMode === 'Sign'
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
});
|
||||||
|
|
||||||
const filterOption = (input: string, option: any) => {
|
const filterOption = (input: string, option: any) => {
|
||||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||||
|
@ -251,18 +258,22 @@ const filterOption = (input: string, option: any) => {
|
||||||
const getOptionsList = async () => {
|
const getOptionsList = async () => {
|
||||||
for (let key in Options.value) {
|
for (let key in Options.value) {
|
||||||
const res: any = await queryOptionsList(key);
|
const res: any = await queryOptionsList(key);
|
||||||
Options.value[key] = res.result.map((item: any) => ({
|
if (res.status === 200) {
|
||||||
label: item?.text || item,
|
Options.value[key] = res.result.map((item: any) => ({
|
||||||
value: item?.value || item,
|
label: item?.text || item,
|
||||||
}));
|
value: item?.value || item,
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const getCertificateList = async () => {
|
const getCertificateList = async () => {
|
||||||
const res: any = await queryCertificateList();
|
const res: any = await queryCertificateList();
|
||||||
certificateList.value = res.result.map((item: any) => ({
|
if (res.status === 200) {
|
||||||
value: item.id,
|
certificateList.value = res.result.map((item: any) => ({
|
||||||
label: item.name,
|
label: item.name,
|
||||||
}));
|
value: item.id,
|
||||||
|
}));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getProvidersList = async () => {
|
const getProvidersList = async () => {
|
||||||
|
|
|
@ -39,6 +39,7 @@ export const updateStatus = {
|
||||||
|
|
||||||
export const TiTlePermissionButtonStyle = {
|
export const TiTlePermissionButtonStyle = {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
|
'max-width': 'calc(100% - 90px)',
|
||||||
color: ' #1890ff !important',
|
color: ' #1890ff !important',
|
||||||
'font-weight': 700,
|
'font-weight': 700,
|
||||||
'font-size': '16px',
|
'font-size': '16px',
|
||||||
|
@ -62,7 +63,7 @@ export const regDomain = new RegExp(
|
||||||
);
|
);
|
||||||
export const checkEndpoint = (_rule: Rule, value: string): Promise<any> =>
|
export const checkEndpoint = (_rule: Rule, value: string): Promise<any> =>
|
||||||
new Promise(async (resolve, reject) => {
|
new Promise(async (resolve, reject) => {
|
||||||
const res = await validateField(value);
|
const res: any = await validateField(value);
|
||||||
return res.result.passed ? resolve('') : reject(res.result.reason);
|
return res.result.passed ? resolve('') : reject(res.result.reason);
|
||||||
});
|
});
|
||||||
export const FormValidate = {
|
export const FormValidate = {
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
:options="[
|
:options="[
|
||||||
{ label: '01线圈寄存器', value: 'Coils' },
|
{ label: '01线圈寄存器', value: 'Coils' },
|
||||||
{ label: '03保存寄存器', value: 'HoldingRegisters' },
|
{ label: '03保存寄存器', value: 'HoldingRegisters' },
|
||||||
{ label: '04输入寄存器', value: 'DiscreteInputs' },
|
{ label: '04输入寄存器', value: 'InputRegisters' },
|
||||||
]"
|
]"
|
||||||
placeholder="请选择所功能码"
|
placeholder="请选择所功能码"
|
||||||
allowClear
|
allowClear
|
||||||
|
@ -74,7 +74,10 @@
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
v-if="formData.configuration.function === 'HoldingRegisters'"
|
v-if="
|
||||||
|
formData.configuration.function === 'HoldingRegisters' ||
|
||||||
|
formData.configuration.function === 'InputRegisters'
|
||||||
|
"
|
||||||
label="数据类型"
|
label="数据类型"
|
||||||
:name="['configuration', 'codec', 'provider']"
|
:name="['configuration', 'codec', 'provider']"
|
||||||
:rules="[
|
:rules="[
|
||||||
|
@ -246,7 +249,8 @@ const props = defineProps({
|
||||||
|
|
||||||
const emit = defineEmits(['change']);
|
const emit = defineEmits(['change']);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const providerList = ref([]);
|
const providerListAll: any = ref([]);
|
||||||
|
const providerList: any = ref([]);
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
const id = props.data.id;
|
const id = props.data.id;
|
||||||
|
@ -322,7 +326,7 @@ const changeWriteByteCount = (value: Array<string>) => {
|
||||||
};
|
};
|
||||||
const changeFunction = (value: string) => {
|
const changeFunction = (value: string) => {
|
||||||
formData.value.accessModes =
|
formData.value.accessModes =
|
||||||
value === 'DiscreteInputs' ? ['read'] : ['read', 'write'];
|
value === 'InputRegisters' ? ['read'] : ['read', 'write'];
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkLength = (_rule: Rule, value: string): Promise<any> =>
|
const checkLength = (_rule: Rule, value: string): Promise<any> =>
|
||||||
|
@ -361,7 +365,7 @@ const filterOption = (input: string, option: any) => {
|
||||||
|
|
||||||
const getProviderList = async () => {
|
const getProviderList = async () => {
|
||||||
const res: any = await queryCodecProvider();
|
const res: any = await queryCodecProvider();
|
||||||
providerList.value = res.result
|
providerListAll.value = res.result
|
||||||
.filter((i: any) => i.id !== 'property')
|
.filter((i: any) => i.id !== 'property')
|
||||||
.map((item: any) => ({
|
.map((item: any) => ({
|
||||||
value: item.id,
|
value: item.id,
|
||||||
|
@ -377,11 +381,23 @@ watch(
|
||||||
},
|
},
|
||||||
{ immediate: true, deep: true },
|
{ immediate: true, deep: true },
|
||||||
);
|
);
|
||||||
|
watch(
|
||||||
|
() => formData.value.configuration.function,
|
||||||
|
(value) => {
|
||||||
|
providerList.value =
|
||||||
|
value === 'HoldingRegisters'
|
||||||
|
? providerListAll.value
|
||||||
|
: providerListAll.value.filter(
|
||||||
|
(item: any) => item.value !== 'bool',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
watch(
|
watch(
|
||||||
() => props.data,
|
() => props.data,
|
||||||
(value) => {
|
(value) => {
|
||||||
if (value.id && value.provider === 'MODBUS_TCP') {
|
if (value.id && value.provider === 'MODBUS_TCP') {
|
||||||
const _value = cloneDeep(value);
|
const _value: any = cloneDeep(value);
|
||||||
const { writeByteCount, byteCount } =
|
const { writeByteCount, byteCount } =
|
||||||
_value.configuration.parameter;
|
_value.configuration.parameter;
|
||||||
formData.value = _value;
|
formData.value = _value;
|
||||||
|
|
|
@ -190,22 +190,4 @@ watch(
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped></style>
|
||||||
.form {
|
|
||||||
.form-radio-button {
|
|
||||||
width: 148px;
|
|
||||||
height: 80px;
|
|
||||||
padding: 0;
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.form-upload-button {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
.form-submit {
|
|
||||||
background-color: @primary-color !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Search :columns="columns" target="search" @search="handleSearch" />
|
<pro-search :columns="columns" target="search" @search="handleSearch" />
|
||||||
<j-pro-table
|
<j-pro-table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
model="TABLE"
|
model="TABLE"
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
<j-descriptions-item label="请求耗时">
|
<j-descriptions-item label="请求耗时">
|
||||||
{{
|
{{
|
||||||
descriptionsData?.responseTime -
|
descriptionsData?.responseTime -
|
||||||
descriptionsData?.requestTime +
|
descriptionsData?.responseTime +
|
||||||
'ms'
|
'ms'
|
||||||
}}
|
}}
|
||||||
</j-descriptions-item>
|
</j-descriptions-item>
|
||||||
|
@ -115,11 +115,10 @@
|
||||||
</j-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup name="AccessLog">
|
<script lang="ts" setup name="AccessLog">
|
||||||
import type { ActionsType } from '@/components/Table/index.vue';
|
import type { ActionsType } from '@/components/Table/index';
|
||||||
import type { AccessLogItem } from '../typings';
|
import type { AccessLogItem } from '../typings';
|
||||||
import { queryAccess } from '@/api/link/log';
|
import { queryAccess } from '@/api/link/log';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
import { modifySearchColumnValue } from '@/utils/comm';
|
import { modifySearchColumnValue } from '@/utils/comm';
|
||||||
|
|
||||||
const tableRef = ref<Record<string, any>>({});
|
const tableRef = ref<Record<string, any>>({});
|
||||||
|
@ -214,7 +213,19 @@ const columns = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const descriptionsData = ref<AccessLogItem>();
|
const descriptionsData = ref({
|
||||||
|
url: '',
|
||||||
|
httpMethod: '',
|
||||||
|
action: '',
|
||||||
|
target: '',
|
||||||
|
method: '',
|
||||||
|
ip: '',
|
||||||
|
requestTime: 0,
|
||||||
|
responseTime: 0,
|
||||||
|
httpHeaders: '',
|
||||||
|
parameters: '',
|
||||||
|
exception: '',
|
||||||
|
});
|
||||||
const visible = ref<boolean>(false);
|
const visible = ref<boolean>(false);
|
||||||
|
|
||||||
const handleOk = (e: MouseEvent) => {
|
const handleOk = (e: MouseEvent) => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Search :columns="columns" target="search" @search="handleSearch" />
|
<pro-search :columns="columns" target="search" @search="handleSearch" />
|
||||||
<j-pro-table
|
<j-pro-table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
model="TABLE"
|
model="TABLE"
|
||||||
|
@ -105,7 +105,7 @@
|
||||||
</j-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup name="SystemLog">
|
<script lang="ts" setup name="SystemLog">
|
||||||
import type { ActionsType } from '@/components/Table/index.vue';
|
import type { ActionsType } from '@/components/Table/index';
|
||||||
import type { SystemLogItem } from '../typings';
|
import type { SystemLogItem } from '../typings';
|
||||||
import { querySystem } from '@/api/link/log';
|
import { querySystem } from '@/api/link/log';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
@ -196,7 +196,20 @@ const columns = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const descriptionsData = ref<SystemLogItem>();
|
const descriptionsData = ref<SystemLogItem>({
|
||||||
|
id: '',
|
||||||
|
threadName: '',
|
||||||
|
createTime: 0,
|
||||||
|
className: '',
|
||||||
|
level: '',
|
||||||
|
message: '',
|
||||||
|
exceptionStack: '',
|
||||||
|
context: '',
|
||||||
|
lineNumber: 0,
|
||||||
|
methodName: '',
|
||||||
|
name: '',
|
||||||
|
threadId: '',
|
||||||
|
});
|
||||||
const visible = ref<boolean>(false);
|
const visible = ref<boolean>(false);
|
||||||
|
|
||||||
const handleOk = (e: MouseEvent) => {
|
const handleOk = (e: MouseEvent) => {
|
||||||
|
|
|
@ -228,7 +228,6 @@ const getOnline = () => {
|
||||||
onlineYdata.reverse();
|
onlineYdata.reverse();
|
||||||
setOnlineChartOpition(x, onlineYdata);
|
setOnlineChartOpition(x, onlineYdata);
|
||||||
onlineFooter.value[0].value = y?.[1];
|
onlineFooter.value[0].value = y?.[1];
|
||||||
console.log(res.result);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,19 +41,21 @@
|
||||||
</j-descriptions>
|
</j-descriptions>
|
||||||
</j-card>
|
</j-card>
|
||||||
<!-- 编辑 -->
|
<!-- 编辑 -->
|
||||||
<Save ref="saveRef" :isAdd="isAdd" :title="title" />
|
<Save ref="saveRef" :isAdd="isAdd" :title="title" @success="refresh"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useProductStore } from '@/store/product';
|
import { useProductStore } from '@/store/product';
|
||||||
import Save from '../../Save/index.vue';
|
import Save from '../../Save/index.vue';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
import {
|
import {
|
||||||
EditOutlined,
|
EditOutlined,
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
const productStore = useProductStore();
|
const productStore = useProductStore();
|
||||||
|
const route = useRoute();
|
||||||
const saveRef = ref();
|
const saveRef = ref();
|
||||||
const isAdd = ref(2);
|
const isAdd = ref(2);
|
||||||
const title = ref('编辑');
|
const title = ref('编辑');
|
||||||
|
@ -69,4 +71,10 @@ const editConfig = () => {
|
||||||
const changeTables = () => {
|
const changeTables = () => {
|
||||||
productStore.tabActiveKey = 'Device';
|
productStore.tabActiveKey = 'Device';
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* 修改成功刷新
|
||||||
|
*/
|
||||||
|
const refresh = () =>{
|
||||||
|
productStore.refresh(route.params.id as string);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -151,7 +151,6 @@ watch(
|
||||||
() => route.params.id,
|
() => route.params.id,
|
||||||
(newId) => {
|
(newId) => {
|
||||||
if (newId) {
|
if (newId) {
|
||||||
console.log(newId);
|
|
||||||
productStore.tabActiveKey = 'Info';
|
productStore.tabActiveKey = 'Info';
|
||||||
productStore.refresh(newId as string);
|
productStore.refresh(newId as string);
|
||||||
}
|
}
|
||||||
|
@ -230,7 +229,7 @@ const getProtocol = async () => {
|
||||||
* 详情页跳转到设备页
|
* 详情页跳转到设备页
|
||||||
*/
|
*/
|
||||||
const jumpDevice = () => {
|
const jumpDevice = () => {
|
||||||
console.log(productStore.current?.id);
|
// console.log(productStore.current?.id);
|
||||||
const searchParams = {
|
const searchParams = {
|
||||||
column: 'productId',
|
column: 'productId',
|
||||||
termType: 'eq',
|
termType: 'eq',
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
<div class="product-title">产品创建成功</div>
|
<div class="product-title">产品创建成功</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex">
|
<div style="display: flex">
|
||||||
<div class="product-id">产品ID: {{ idValue.value }}</div>
|
<div class="product-id">产品ID: {{ idValue }}</div>
|
||||||
<div class="product-btn" @click="showDetail">查看详情</div>
|
<div class="product-btn" @click="showDetail" style="cursor: pointer;">查看详情</div>
|
||||||
</div>
|
</div>
|
||||||
<div>接下来推荐操作:</div>
|
<div>接下来推荐操作:</div>
|
||||||
<div class="product-main">1、配置产品接入方式</div>
|
<div class="product-main">1、配置产品接入方式</div>
|
||||||
|
@ -49,10 +49,12 @@
|
||||||
import { getImage } from '@/utils/comm.ts';
|
import { getImage } from '@/utils/comm.ts';
|
||||||
import { useProductStore } from '@/store/product';
|
import { useProductStore } from '@/store/product';
|
||||||
import { CheckCircleOutlined } from '@ant-design/icons-vue';
|
import { CheckCircleOutlined } from '@ant-design/icons-vue';
|
||||||
|
import { useMenuStore } from '@/store/menu';
|
||||||
const visible = ref<boolean>(false);
|
const visible = ref<boolean>(false);
|
||||||
const productStore = useProductStore();
|
const productStore = useProductStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const idValue = ref({});
|
const idValue = ref({});
|
||||||
|
const menuStore = useMenuStore();
|
||||||
/**
|
/**
|
||||||
* 弹窗关闭
|
* 弹窗关闭
|
||||||
*/
|
*/
|
||||||
|
@ -70,17 +72,12 @@ const show = (id: string) => {
|
||||||
* 查看详情
|
* 查看详情
|
||||||
*/
|
*/
|
||||||
const showDetail = () => {
|
const showDetail = () => {
|
||||||
jump(idValue.value);
|
menuStore.jumpPage('device/Product/Detail',{id:idValue.value})
|
||||||
};
|
|
||||||
/**
|
|
||||||
* 跳转页面
|
|
||||||
*/
|
|
||||||
const jump = (id: string) => {
|
|
||||||
router.push('/iot/device/product/detail/' + id);
|
|
||||||
};
|
};
|
||||||
defineExpose({
|
defineExpose({
|
||||||
show: show,
|
show: show,
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.product-tips {
|
.product-tips {
|
||||||
|
|
|
@ -395,7 +395,7 @@ const submitData = () => {
|
||||||
message.success('保存成功!');
|
message.success('保存成功!');
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
emit('success');
|
emit('success');
|
||||||
dialogRef.value.show(form.id);
|
dialogRef.value.show(res.result.id);
|
||||||
} else {
|
} else {
|
||||||
message.error('操作失败');
|
message.error('操作失败');
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,14 +55,14 @@ import { getProviders, detail } from '@/api/link/accessConfig';
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const id = route.params.id as string;
|
const id = route.params.id as string;
|
||||||
|
|
||||||
const dataSource = ref([]);
|
const dataSource: any = ref([]);
|
||||||
const type = ref(false);
|
const type = ref(false);
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
const provider = ref({});
|
const provider = ref({});
|
||||||
const data = ref({});
|
const data = ref({});
|
||||||
const showType = ref('');
|
const showType = ref('');
|
||||||
|
|
||||||
const goProviders = (param: object) => {
|
const goProviders = (param: any) => {
|
||||||
showType.value = param.type;
|
showType.value = param.type;
|
||||||
provider.value = param;
|
provider.value = param;
|
||||||
type.value = false;
|
type.value = false;
|
||||||
|
@ -80,7 +80,7 @@ const getTypeList = (result: Record<string, any>) => {
|
||||||
const cloud: any[] = [];
|
const cloud: any[] = [];
|
||||||
const channel: any[] = [];
|
const channel: any[] = [];
|
||||||
const edge: any[] = [];
|
const edge: any[] = [];
|
||||||
result.map((item) => {
|
result.map((item: any) => {
|
||||||
if (item.id === 'fixed-media' || item.id === 'gb28181-2016') {
|
if (item.id === 'fixed-media' || item.id === 'gb28181-2016') {
|
||||||
item.type = 'media';
|
item.type = 'media';
|
||||||
media.push(item);
|
media.push(item);
|
||||||
|
@ -138,33 +138,32 @@ const getTypeList = (result: Record<string, any>) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryProviders = async () => {
|
const queryProviders = async () => {
|
||||||
const resp = await getProviders();
|
const resp: any = await getProviders();
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
dataSource.value = getTypeList(resp.result);
|
dataSource.value = getTypeList(resp.result);
|
||||||
// dataSource.value = getTypeList(resp.result)[0].list.filter(
|
// dataSource.value = getTypeList(resp.result)[0].list.filter(
|
||||||
// (item) => item.name !== '插件设备接入',
|
// (item) => item.name !== '插件设备接入',
|
||||||
// );
|
// );
|
||||||
console.log(111, dataSource.value);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getProvidersData = async () => {
|
const getProvidersData = async () => {
|
||||||
if (id !== ':id') {
|
if (id !== ':id') {
|
||||||
getProviders().then((response) => {
|
getProviders().then((response: any) => {
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
const list = getTypeList(response.result);
|
const list = getTypeList(response.result);
|
||||||
dataSource.value = list.filter(
|
dataSource.value = list.filter(
|
||||||
(item) =>
|
(item: any) =>
|
||||||
item.channel === 'network' ||
|
item.channel === 'network' ||
|
||||||
item.channel === 'child-device',
|
item.channel === 'child-device',
|
||||||
);
|
);
|
||||||
detail(id).then((resp) => {
|
detail(id).then((resp: any) => {
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
const dt = response.result.find(
|
const dt = response.result.find(
|
||||||
(item) => item?.id === resp.result.provider,
|
(item: any) => item?.id === resp.result.provider,
|
||||||
);
|
);
|
||||||
|
|
||||||
response.result.forEach((item) => {
|
response.result.forEach((item: any) => {
|
||||||
if (item.id === resp.result.provider) {
|
if (item.id === resp.result.provider) {
|
||||||
resp.result.type = item.type;
|
resp.result.type = item.type;
|
||||||
showType.value = item.type;
|
showType.value = item.type;
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
v-for="item in networkList"
|
v-for="item in networkList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
>
|
>
|
||||||
<access-card
|
<AccessCard
|
||||||
@checkedChange="checkedChange"
|
@checkedChange="checkedChange"
|
||||||
:checked="networkCurrent"
|
:checked="networkCurrent"
|
||||||
:data="{
|
:data="{
|
||||||
|
@ -84,7 +84,7 @@
|
||||||
</j-tooltip>
|
</j-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</access-card>
|
</AccessCard>
|
||||||
</j-col>
|
</j-col>
|
||||||
</j-row>
|
</j-row>
|
||||||
<j-empty v-else description="暂无数据" />
|
<j-empty v-else description="暂无数据" />
|
||||||
|
@ -139,36 +139,34 @@
|
||||||
<j-row :gutter="[24, 24]">
|
<j-row :gutter="[24, 24]">
|
||||||
<j-col :span="12">
|
<j-col :span="12">
|
||||||
<title-component data="基本信息" />
|
<title-component data="基本信息" />
|
||||||
<div>
|
<j-form
|
||||||
<j-form
|
ref="formRef"
|
||||||
ref="formRef"
|
:model="formData"
|
||||||
:model="formData"
|
layout="vertical"
|
||||||
layout="vertical"
|
>
|
||||||
|
<j-form-item
|
||||||
|
label="名称"
|
||||||
|
v-bind="validateInfos.name"
|
||||||
>
|
>
|
||||||
<j-form-item
|
<j-input
|
||||||
label="名称"
|
v-model:value="formData.name"
|
||||||
v-bind="validateInfos.name"
|
allowClear
|
||||||
>
|
placeholder="请输入名称"
|
||||||
<j-input
|
/>
|
||||||
v-model:value="formData.name"
|
</j-form-item>
|
||||||
allowClear
|
<j-form-item
|
||||||
placeholder="请输入名称"
|
label="说明"
|
||||||
/>
|
v-bind="validateInfos.description"
|
||||||
</j-form-item>
|
>
|
||||||
<j-form-item
|
<j-textarea
|
||||||
label="说明"
|
placeholder="请输入说明"
|
||||||
v-bind="validateInfos.description"
|
:rows="4"
|
||||||
>
|
v-model:value="formData.description"
|
||||||
<j-textarea
|
show-count
|
||||||
placeholder="请输入说明"
|
:maxlength="200"
|
||||||
:rows="4"
|
/>
|
||||||
v-model:value="formData.description"
|
</j-form-item>
|
||||||
show-count
|
</j-form>
|
||||||
:maxlength="200"
|
|
||||||
/>
|
|
||||||
</j-form-item>
|
|
||||||
</j-form>
|
|
||||||
</div>
|
|
||||||
</j-col>
|
</j-col>
|
||||||
<j-col :span="12">
|
<j-col :span="12">
|
||||||
<div class="config-right">
|
<div class="config-right">
|
||||||
|
@ -256,9 +254,7 @@
|
||||||
'stream'
|
'stream'
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<span>{{
|
{{ getStream(record) }}
|
||||||
getStream(record)
|
|
||||||
}}</span>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</j-table>
|
</j-table>
|
||||||
|
@ -359,7 +355,7 @@ const stepCurrent = ref(0);
|
||||||
const steps = ref(['网络组件', '消息协议', '完成']);
|
const steps = ref(['网络组件', '消息协议', '完成']);
|
||||||
const networkList: any = ref([]);
|
const networkList: any = ref([]);
|
||||||
const allNetworkList: any = ref([]);
|
const allNetworkList: any = ref([]);
|
||||||
const procotolList = ref([]);
|
const procotolList: any = ref([]);
|
||||||
const allProcotolList = ref([]);
|
const allProcotolList = ref([]);
|
||||||
const networkCurrent: any = ref('');
|
const networkCurrent: any = ref('');
|
||||||
const procotolCurrent: any = ref('');
|
const procotolCurrent: any = ref('');
|
||||||
|
@ -390,11 +386,12 @@ const queryNetworkList = async (id: string, include: string, data = {}) => {
|
||||||
);
|
);
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
networkList.value = resp.result;
|
networkList.value = resp.result;
|
||||||
|
allNetworkList.value = resp.result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryProcotolList = async (id: string, params = {}) => {
|
const queryProcotolList = async (id: string, params = {}) => {
|
||||||
const resp = await getProtocolList(ProtocolMapping.get(id), {
|
const resp: any = await getProtocolList(ProtocolMapping.get(id), {
|
||||||
...params,
|
...params,
|
||||||
'sorts[0].name': 'createTime',
|
'sorts[0].name': 'createTime',
|
||||||
'sorts[0].order': 'desc',
|
'sorts[0].order': 'desc',
|
||||||
|
@ -407,12 +404,12 @@ const queryProcotolList = async (id: string, params = {}) => {
|
||||||
|
|
||||||
const addNetwork = () => {
|
const addNetwork = () => {
|
||||||
const url = menuStory.menus['link/Type/Detail']?.path;
|
const url = menuStory.menus['link/Type/Detail']?.path;
|
||||||
const tab = window.open(
|
const tab: any = window.open(
|
||||||
`${window.location.origin + window.location.pathname}#${url}?type=${
|
`${window.location.origin + window.location.pathname}#${url}?type=${
|
||||||
NetworkTypeMapping.get(props.provider?.id) || ''
|
NetworkTypeMapping.get(props.provider?.id) || ''
|
||||||
}`,
|
}`,
|
||||||
);
|
);
|
||||||
tab.onTabSaveSuccess = (value) => {
|
tab.onTabSaveSuccess = (value: any) => {
|
||||||
if (value.success) {
|
if (value.success) {
|
||||||
networkCurrent.value = value.result.id;
|
networkCurrent.value = value.result.id;
|
||||||
queryNetworkList(props.provider?.id, networkCurrent.value || '');
|
queryNetworkList(props.provider?.id, networkCurrent.value || '');
|
||||||
|
@ -422,10 +419,10 @@ const addNetwork = () => {
|
||||||
|
|
||||||
const addProcotol = () => {
|
const addProcotol = () => {
|
||||||
const url = menuStory.menus['link/Protocol']?.path;
|
const url = menuStory.menus['link/Protocol']?.path;
|
||||||
const tab = window.open(
|
const tab: any = window.open(
|
||||||
`${window.location.origin + window.location.pathname}#${url}?save=true`,
|
`${window.location.origin + window.location.pathname}#${url}?save=true`,
|
||||||
);
|
);
|
||||||
tab.onTabSaveSuccess = (value) => {
|
tab.onTabSaveSuccess = (value: any) => {
|
||||||
if (value.success) {
|
if (value.success) {
|
||||||
procotolCurrent.value = value.result?.id;
|
procotolCurrent.value = value.result?.id;
|
||||||
queryProcotolList(props.provider?.id);
|
queryProcotolList(props.provider?.id);
|
||||||
|
@ -434,15 +431,17 @@ const addProcotol = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNetworkCurrent = () =>
|
const getNetworkCurrent = () =>
|
||||||
networkList.value.find((i) => i.id === networkCurrent) &&
|
networkList.value.find((i: any) => i.id === networkCurrent) &&
|
||||||
(networkList.value.find((i) => i.id === networkCurrent).addresses || [])
|
(
|
||||||
.length > 0;
|
networkList.value.find((i: any) => i.id === networkCurrent).addresses ||
|
||||||
|
[]
|
||||||
|
).length > 0;
|
||||||
const getNetworkCurrentData = () =>
|
const getNetworkCurrentData = () =>
|
||||||
getNetworkCurrent()
|
getNetworkCurrent()
|
||||||
? networkList.value.find((i) => i.id === networkCurrent).addresses
|
? networkList.value.find((i: any) => i.id === networkCurrent).addresses
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const getColor = (i) => (i.health === -1 ? 'red' : 'green');
|
const getColor = (i: any) => (i.health === -1 ? 'red' : 'green');
|
||||||
|
|
||||||
const getStream = (record: any) => {
|
const getStream = (record: any) => {
|
||||||
let stream = '';
|
let stream = '';
|
||||||
|
@ -457,15 +456,15 @@ const checkedChange = (id: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const networkSearch = (value: string) => {
|
const networkSearch = (value: string) => {
|
||||||
if (value) {
|
networkList.value = value
|
||||||
networkList.value = allNetworkList.value.filter(
|
? allNetworkList.value.filter(
|
||||||
(i: any) =>
|
(i: any) =>
|
||||||
i.name &&
|
i.name &&
|
||||||
i.name.toLocaleLowerCase().includes(value.toLocaleLowerCase()),
|
i.name
|
||||||
);
|
.toLocaleLowerCase()
|
||||||
} else {
|
.includes(value.toLocaleLowerCase()),
|
||||||
networkList.value = allNetworkList.value;
|
)
|
||||||
}
|
: allNetworkList.value;
|
||||||
};
|
};
|
||||||
const procotolChange = (id: string) => {
|
const procotolChange = (id: string) => {
|
||||||
if (!props.data.id) {
|
if (!props.data.id) {
|
||||||
|
@ -474,17 +473,15 @@ const procotolChange = (id: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const procotolSearch = (value: string) => {
|
const procotolSearch = (value: string) => {
|
||||||
if (value) {
|
procotolList.value = value
|
||||||
const list = allProcotolList.value.filter((i: any) => {
|
? allProcotolList.value.filter(
|
||||||
return (
|
(i: any) =>
|
||||||
i.name &&
|
i.name &&
|
||||||
i.name.toLocaleLowerCase().includes(value.toLocaleLowerCase())
|
i.name
|
||||||
);
|
.toLocaleLowerCase()
|
||||||
});
|
.includes(value.toLocaleLowerCase()),
|
||||||
procotolList.value = list;
|
)
|
||||||
} else {
|
: allProcotolList.value;
|
||||||
procotolList.value = allProcotolList.value;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveData = () => {
|
const saveData = () => {
|
||||||
|
@ -538,8 +535,6 @@ const next = async () => {
|
||||||
: await getChildConfigView(procotolCurrent.value);
|
: await getChildConfigView(procotolCurrent.value);
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
config.value = resp.result;
|
config.value = resp.result;
|
||||||
console.log(222, config.value);
|
|
||||||
|
|
||||||
current.value = current.value + 1;
|
current.value = current.value + 1;
|
||||||
const Group = {
|
const Group = {
|
||||||
title: '分组',
|
title: '分组',
|
||||||
|
@ -567,8 +562,8 @@ const next = async () => {
|
||||||
return obj;
|
return obj;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
columnsMQTT.value = [Group, ...ColumnsMQTT];
|
columnsMQTT.value = [Group, ...ColumnsMQTT] as TableColumnType;
|
||||||
columnsHTTP.value = [Group, ...ColumnsHTTP];
|
columnsHTTP.value = [Group, ...ColumnsHTTP] as TableColumnType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,39 +95,29 @@ const ColumnsMQTT = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const ColumnsHTTP = [
|
const ColumnsHTTP = [
|
||||||
// {
|
|
||||||
// title: '分组',
|
|
||||||
// dataIndex: 'group',
|
|
||||||
// key: 'group',
|
|
||||||
// ellipsis: true,
|
|
||||||
// width: 100,
|
|
||||||
// scopedSlots: { customRender: 'group' },
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
title: '地址',
|
title: '地址',
|
||||||
dataIndex: 'address',
|
dataIndex: 'address',
|
||||||
key: 'address',
|
key: 'address',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
// scopedSlots: { customRender: 'address' },
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '示例',
|
title: '示例',
|
||||||
dataIndex: 'example',
|
dataIndex: 'example',
|
||||||
key: 'example',
|
key: 'example',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
// scopedSlots: { customRender: 'example' },
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '说明',
|
title: '说明',
|
||||||
dataIndex: 'description',
|
dataIndex: 'description',
|
||||||
key: 'description',
|
key: 'description',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
// scopedSlots: { customRender: 'description' },
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const TiTlePermissionButtonStyle = {
|
const TiTlePermissionButtonStyle = {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
|
'max-width': 'calc(100% - 90px)',
|
||||||
color: ' #1890ff !important',
|
color: ' #1890ff !important',
|
||||||
'font-weight': 700,
|
'font-weight': 700,
|
||||||
'font-size': '16px',
|
'font-size': '16px',
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<div>
|
<div>
|
||||||
<Search :columns="columns" target="search" @search="handleSearch" />
|
<pro-search
|
||||||
|
:columns="columns"
|
||||||
|
target="search"
|
||||||
|
@search="handleSearch"
|
||||||
|
/>
|
||||||
|
|
||||||
<j-pro-table
|
<j-pro-table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
|
@ -16,18 +20,14 @@
|
||||||
:params="params"
|
:params="params"
|
||||||
>
|
>
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<j-space>
|
<PermissionButton
|
||||||
<PermissionButton
|
type="primary"
|
||||||
type="primary"
|
@click="handlAdd"
|
||||||
@click="handlAdd"
|
hasPermission="link/AccessConfig:add"
|
||||||
hasPermission="link/AccessConfig:add"
|
>
|
||||||
>
|
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||||
<template #icon
|
新增
|
||||||
><AIcon type="PlusOutlined"
|
</PermissionButton>
|
||||||
/></template>
|
|
||||||
新增
|
|
||||||
</PermissionButton>
|
|
||||||
</j-space>
|
|
||||||
</template>
|
</template>
|
||||||
<template #card="slotProps">
|
<template #card="slotProps">
|
||||||
<CardBox
|
<CardBox
|
||||||
|
|
|
@ -33,7 +33,17 @@
|
||||||
>
|
>
|
||||||
</j-range-picker>
|
</j-range-picker>
|
||||||
</div>
|
</div>
|
||||||
<div ref="chartRef" style="width: 100%; height: 300px"></div>
|
<div>
|
||||||
|
<j-empty
|
||||||
|
v-if="isEmpty"
|
||||||
|
style="height: 200px; margin-top: 100px"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
ref="chartRef"
|
||||||
|
style="width: 100%; height: 300px"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</j-spin>
|
</j-spin>
|
||||||
</template>
|
</template>
|
||||||
|
@ -50,26 +60,27 @@ import {
|
||||||
areaStyleCpu,
|
areaStyleCpu,
|
||||||
typeDataLine,
|
typeDataLine,
|
||||||
} from './tool.ts';
|
} from './tool.ts';
|
||||||
|
import { DataType } from '../typings';
|
||||||
|
|
||||||
const chartRef = ref<Record<string, any>>({});
|
const chartRef = ref<Record<string, any>>({});
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const data = ref({
|
const data = ref<DataType>({
|
||||||
type: 'hour',
|
type: 'hour',
|
||||||
time: [null, null],
|
time: [null, null],
|
||||||
});
|
});
|
||||||
|
const isEmpty = ref(false);
|
||||||
const pickerTimeChange = () => {
|
const pickerTimeChange = () => {
|
||||||
data.value.type = undefined;
|
data.value.type = undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCPUEcharts = async (val) => {
|
const getCPUEcharts = async (val: any) => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const res = await dashboard(defulteParamsData('cpu', val));
|
const res: any = await dashboard(defulteParamsData('cpu', val));
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
const _cpuOptions = {};
|
const _cpuOptions = {};
|
||||||
const _cpuXAxis = new Set();
|
const _cpuXAxis = new Set();
|
||||||
if (res.result?.length) {
|
if (res.result?.length) {
|
||||||
res.result.forEach((item) => {
|
res.result.forEach((item: any) => {
|
||||||
const value = item.data.value;
|
const value = item.data.value;
|
||||||
const nodeID = item.data.clusterNodeId;
|
const nodeID = item.data.clusterNodeId;
|
||||||
_cpuXAxis.add(
|
_cpuXAxis.add(
|
||||||
|
@ -84,15 +95,18 @@ const getCPUEcharts = async (val) => {
|
||||||
Number(value.cpuSystemUsage).toFixed(2),
|
Number(value.cpuSystemUsage).toFixed(2),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
handleCpuOptions(_cpuOptions, [..._cpuXAxis.keys()]);
|
||||||
|
} else {
|
||||||
|
handleCpuOptions([], []);
|
||||||
|
isEmpty.value = true;
|
||||||
}
|
}
|
||||||
handleCpuOptions(_cpuOptions, [..._cpuXAxis.keys()]);
|
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}, 300);
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setOptions = (optionsData, key) => ({
|
const setOptions = (optionsData: any, key: string) => ({
|
||||||
data: arrayReverse(optionsData[key]),
|
data: arrayReverse(optionsData[key]),
|
||||||
name: key,
|
name: key,
|
||||||
type: 'line',
|
type: 'line',
|
||||||
|
@ -101,8 +115,9 @@ const setOptions = (optionsData, key) => ({
|
||||||
areaStyle: areaStyleCpu,
|
areaStyle: areaStyleCpu,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleCpuOptions = (optionsData, xAxis) => {
|
const handleCpuOptions = (optionsData: any, xAxis: any) => {
|
||||||
const chart = chartRef.value;
|
if (optionsData.length === 0 && xAxis.length === 0) return;
|
||||||
|
const chart: any = chartRef.value;
|
||||||
if (chart) {
|
if (chart) {
|
||||||
const myChart = echarts.init(chart);
|
const myChart = echarts.init(chart);
|
||||||
const dataKeys = Object.keys(optionsData);
|
const dataKeys = Object.keys(optionsData);
|
||||||
|
@ -114,7 +129,7 @@ const handleCpuOptions = (optionsData, xAxis) => {
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
valueFormatter: (value) => `${value}%`,
|
valueFormatter: (value: any) => `${value}%`,
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
|
|
|
@ -33,7 +33,17 @@
|
||||||
>
|
>
|
||||||
</j-range-picker>
|
</j-range-picker>
|
||||||
</div>
|
</div>
|
||||||
<div ref="chartRef" style="width: 100%; height: 300px"></div>
|
<div>
|
||||||
|
<j-empty
|
||||||
|
v-if="isEmpty"
|
||||||
|
style="height: 200px; margin-top: 100px"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
ref="chartRef"
|
||||||
|
style="width: 100%; height: 300px"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</j-spin>
|
</j-spin>
|
||||||
</template>
|
</template>
|
||||||
|
@ -50,26 +60,27 @@ import {
|
||||||
areaStyleJvm,
|
areaStyleJvm,
|
||||||
defulteParamsData,
|
defulteParamsData,
|
||||||
} from './tool.ts';
|
} from './tool.ts';
|
||||||
|
import { DataType } from '../typings';
|
||||||
|
|
||||||
const chartRef = ref<Record<string, any>>({});
|
const chartRef = ref<Record<string, any>>({});
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const data = ref({
|
const data = ref<DataType>({
|
||||||
type: 'hour',
|
type: 'hour',
|
||||||
time: [null, null],
|
time: [null, null],
|
||||||
});
|
});
|
||||||
|
const isEmpty = ref(false);
|
||||||
const pickerTimeChange = () => {
|
const pickerTimeChange = () => {
|
||||||
data.value.type = undefined;
|
data.value.type = undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getJVMEcharts = async (val) => {
|
const getJVMEcharts = async (val: any) => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const res = await dashboard(defulteParamsData('jvm', val));
|
const res: any = await dashboard(defulteParamsData('jvm', val));
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
const _jvmOptions = {};
|
const _jvmOptions = {};
|
||||||
const _jvmXAxis = new Set();
|
const _jvmXAxis = new Set();
|
||||||
if (res.result?.length) {
|
if (res.result?.length) {
|
||||||
res.result.forEach((item) => {
|
res.result.forEach((item: any) => {
|
||||||
const value = item.data.value;
|
const value = item.data.value;
|
||||||
const memoryJvmHeapFree = value.memoryJvmHeapFree;
|
const memoryJvmHeapFree = value.memoryJvmHeapFree;
|
||||||
const memoryJvmHeapTotal = value.memoryJvmHeapTotal;
|
const memoryJvmHeapTotal = value.memoryJvmHeapTotal;
|
||||||
|
@ -90,15 +101,18 @@ const getJVMEcharts = async (val) => {
|
||||||
);
|
);
|
||||||
_jvmOptions[nodeID].push(_value);
|
_jvmOptions[nodeID].push(_value);
|
||||||
});
|
});
|
||||||
|
handleJVMOptions(_jvmOptions, [..._jvmXAxis.keys()]);
|
||||||
|
} else {
|
||||||
|
handleJVMOptions([], []);
|
||||||
|
isEmpty.value = true;
|
||||||
}
|
}
|
||||||
handleJVMOptions(_jvmOptions, [..._jvmXAxis.keys()]);
|
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}, 300);
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setOptions = (optionsData, key) => ({
|
const setOptions = (optionsData: any, key: string) => ({
|
||||||
data: arrayReverse(optionsData[key]),
|
data: arrayReverse(optionsData[key]),
|
||||||
name: key,
|
name: key,
|
||||||
type: 'line',
|
type: 'line',
|
||||||
|
@ -106,8 +120,9 @@ const setOptions = (optionsData, key) => ({
|
||||||
symbol: 'none',
|
symbol: 'none',
|
||||||
areaStyle: areaStyleJvm,
|
areaStyle: areaStyleJvm,
|
||||||
});
|
});
|
||||||
const handleJVMOptions = (optionsData, xAxis) => {
|
const handleJVMOptions = (optionsData: any, xAxis: any) => {
|
||||||
const chart = chartRef.value;
|
if (optionsData.length === 0 && xAxis.length === 0) return;
|
||||||
|
const chart: any = chartRef.value;
|
||||||
if (chart) {
|
if (chart) {
|
||||||
const myChart = echarts.init(chart);
|
const myChart = echarts.init(chart);
|
||||||
const dataKeys = Object.keys(optionsData);
|
const dataKeys = Object.keys(optionsData);
|
||||||
|
|
|
@ -43,7 +43,15 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div ref="chartRef" style="width: 100%; height: 350px"></div>
|
<j-empty
|
||||||
|
v-if="isEmpty"
|
||||||
|
style="height: 250px; margin-top: 100px"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
ref="chartRef"
|
||||||
|
style="width: 100%; height: 350px"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</j-spin>
|
</j-spin>
|
||||||
|
@ -59,33 +67,34 @@ import {
|
||||||
} from './tool.ts';
|
} from './tool.ts';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
|
import { DataType } from '../typings.d';
|
||||||
|
|
||||||
const chartRef = ref<Record<string, any>>({});
|
const chartRef = ref<Record<string, any>>({});
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const data = ref({
|
const data = ref<DataType>({
|
||||||
type: 'bytesRead',
|
type: 'bytesRead',
|
||||||
time: {
|
time: {
|
||||||
type: 'today',
|
type: 'hour',
|
||||||
time: [null, null],
|
time: [null, null],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const isEmpty = ref(false);
|
||||||
const pickerTimeChange = () => {
|
const pickerTimeChange = () => {
|
||||||
data.value.time.type = undefined;
|
data.value.time.type = undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNetworkEcharts = async (val) => {
|
const getNetworkEcharts = async (val: any) => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const resp = await dashboard(networkParams(val));
|
const resp: any = await dashboard(networkParams(val));
|
||||||
if (resp.success) {
|
if (resp.success) {
|
||||||
const _networkOptions = {};
|
const _networkOptions = {};
|
||||||
const _networkXAxis = new Set();
|
const _networkXAxis = new Set();
|
||||||
if (resp.result.length) {
|
if (resp.result.length) {
|
||||||
resp.result.forEach((item) => {
|
resp.result.forEach((item: any) => {
|
||||||
const value = item.data.value;
|
const value = item.data.value;
|
||||||
const _data = [];
|
const _data: Array<any> = [];
|
||||||
const nodeID = item.data.clusterNodeId;
|
const nodeID = item.data.clusterNodeId;
|
||||||
value.forEach((item) => {
|
value.forEach((item: any) => {
|
||||||
_data.push(item.value);
|
_data.push(item.value);
|
||||||
_networkXAxis.add(item.timeString);
|
_networkXAxis.add(item.timeString);
|
||||||
});
|
});
|
||||||
|
@ -98,13 +107,14 @@ const getNetworkEcharts = async (val) => {
|
||||||
handleNetworkOptions(_networkOptions, [..._networkXAxis.keys()]);
|
handleNetworkOptions(_networkOptions, [..._networkXAxis.keys()]);
|
||||||
} else {
|
} else {
|
||||||
handleNetworkOptions([], []);
|
handleNetworkOptions([], []);
|
||||||
|
isEmpty.value = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}, 300);
|
}, 300);
|
||||||
};
|
};
|
||||||
const networkValueRender = (obj) => {
|
const networkValueRender = (obj: any) => {
|
||||||
const { value } = obj;
|
const { value } = obj;
|
||||||
let _data = '';
|
let _data = '';
|
||||||
if (value >= 1024 && value < 1024 * 1024) {
|
if (value >= 1024 && value < 1024 * 1024) {
|
||||||
|
@ -117,7 +127,7 @@ const networkValueRender = (obj) => {
|
||||||
return `${obj?.axisValueLabel}<br />${obj?.marker}${obj?.seriesName}: ${_data}`;
|
return `${obj?.axisValueLabel}<br />${obj?.marker}${obj?.seriesName}: ${_data}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const setOptions = (data, key) => ({
|
const setOptions = (data: any, key: string) => ({
|
||||||
data: data[key]._data, // .map((item) => Number((item / 1024 / 1024).toFixed(2))),
|
data: data[key]._data, // .map((item) => Number((item / 1024 / 1024).toFixed(2))),
|
||||||
name: key,
|
name: key,
|
||||||
type: 'line',
|
type: 'line',
|
||||||
|
@ -125,8 +135,9 @@ const setOptions = (data, key) => ({
|
||||||
areaStyle,
|
areaStyle,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleNetworkOptions = (optionsData, xAxis) => {
|
const handleNetworkOptions = (optionsData: any, xAxis: any) => {
|
||||||
const chart = chartRef.value;
|
if (optionsData.length === 0 && xAxis.length === 0) return;
|
||||||
|
const chart: any = chartRef.value;
|
||||||
if (chart) {
|
if (chart) {
|
||||||
const myChart = echarts.init(chart);
|
const myChart = echarts.init(chart);
|
||||||
const dataKeys = Object.keys(optionsData);
|
const dataKeys = Object.keys(optionsData);
|
||||||
|
@ -145,7 +156,7 @@ const handleNetworkOptions = (optionsData, xAxis) => {
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
formatter: (_value) => networkValueRender(_value[0]),
|
formatter: (_value: any) => networkValueRender(_value[0]),
|
||||||
},
|
},
|
||||||
color: ['#979AFF'],
|
color: ['#979AFF'],
|
||||||
series: dataKeys.length
|
series: dataKeys.length
|
||||||
|
|
|
@ -49,7 +49,7 @@ import { getWebSocket } from '@/utils/websocket';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
const serverId = ref();
|
const serverId = ref();
|
||||||
const serverNodeOptions = ref([]);
|
const serverNodeOptions = ref<Array<any>>([]);
|
||||||
const topValues = ref({
|
const topValues = ref({
|
||||||
cpu: 0,
|
cpu: 0,
|
||||||
jvm: 0,
|
jvm: 0,
|
||||||
|
@ -73,7 +73,7 @@ const getData = () => {
|
||||||
interval: '1s',
|
interval: '1s',
|
||||||
agg: 'avg',
|
agg: 'avg',
|
||||||
})
|
})
|
||||||
.pipe(map((res) => res.payload))
|
.pipe(map((res: any) => res.payload))
|
||||||
.subscribe((payload) => {
|
.subscribe((payload) => {
|
||||||
const {
|
const {
|
||||||
value: { cpu, memory, disk },
|
value: { cpu, memory, disk },
|
||||||
|
@ -103,9 +103,9 @@ const getData = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
serverNode().then((resp) => {
|
serverNode().then((resp: any) => {
|
||||||
if (resp.success) {
|
if (resp.success) {
|
||||||
serverNodeOptions.value = resp.result.map((item) => ({
|
serverNodeOptions.value = resp.result.map((item: any) => ({
|
||||||
label: item.name,
|
label: item.name,
|
||||||
value: item.id,
|
value: item.id,
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
export type DataType = {
|
||||||
|
type: string | undefined;
|
||||||
|
time: array<any>;
|
||||||
|
};
|
|
@ -58,7 +58,7 @@ const handleChange = async (info: UploadChangeParam) => {
|
||||||
if (info.file.status === 'done') {
|
if (info.file.status === 'done') {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
const result = info.file.response?.result;
|
const result = info.file.response?.result;
|
||||||
const api = await querySystemApi(['paths']);
|
const api: any = await querySystemApi(['paths']);
|
||||||
const path = api.result[0]?.properties
|
const path = api.result[0]?.properties
|
||||||
? api.result[0]?.properties['base-path']
|
? api.result[0]?.properties['base-path']
|
||||||
: '';
|
: '';
|
||||||
|
@ -86,6 +86,8 @@ const handleChange = async (info: UploadChangeParam) => {
|
||||||
.upload-box {
|
.upload-box {
|
||||||
:deep(.ant-btn) {
|
:deep(.ant-btn) {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -11,25 +11,45 @@
|
||||||
:model="formData"
|
:model="formData"
|
||||||
name="basic"
|
name="basic"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
|
ref="formRef"
|
||||||
>
|
>
|
||||||
<j-form-item label="名称" v-bind="validateInfos.name">
|
<j-form-item
|
||||||
|
label="名称"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入名称', trigger: 'blur' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
]"
|
||||||
|
>
|
||||||
<j-input
|
<j-input
|
||||||
placeholder="请输入名称"
|
placeholder="请输入名称"
|
||||||
v-model:value="formData.name"
|
v-model:value="formData.name"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item label="类型" v-bind="validateInfos.type">
|
<j-form-item
|
||||||
<RadioCard
|
label="类型"
|
||||||
|
name="type"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请选择类型', trigger: 'blur' },
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-card-select
|
||||||
:disabled="!!id"
|
:disabled="!!id"
|
||||||
layout="horizontal"
|
v-model:value="formData.type"
|
||||||
:checkStyle="true"
|
|
||||||
:options="options"
|
:options="options"
|
||||||
v-model="formData.type"
|
@change="changeType"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
label="文件地址"
|
label="文件地址"
|
||||||
v-bind="validateInfos['configuration.location']"
|
:name="['configuration', 'location']"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入文件地址',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
<j-input
|
<j-input
|
||||||
v-if="formData.type === 'local'"
|
v-if="formData.type === 'local'"
|
||||||
|
@ -41,7 +61,7 @@
|
||||||
v-model:modelValue="formData.configuration.location"
|
v-model:modelValue="formData.configuration.location"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item label="说明" v-bind="validateInfos.description">
|
<j-form-item label="说明" name="description">
|
||||||
<j-textarea
|
<j-textarea
|
||||||
placeholder="请输入说明"
|
placeholder="请输入说明"
|
||||||
v-model:value="formData.description"
|
v-model:value="formData.description"
|
||||||
|
@ -67,16 +87,16 @@
|
||||||
</j-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { message, Form } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
import type { UploadChangeParam } from 'ant-design-vue';
|
import type { UploadChangeParam, FormInstance } from 'ant-design-vue';
|
||||||
import FileUpload from './FileUpload.vue';
|
import FileUpload from './FileUpload.vue';
|
||||||
import { save, update } from '@/api/link/protocol';
|
import { save, update } from '@/api/link/protocol';
|
||||||
|
import { FormDataType } from '../type.d';
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const fileLoading = ref(false);
|
const fileLoading = ref(false);
|
||||||
const useForm = Form.useForm;
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -91,16 +111,16 @@ const options = [
|
||||||
{
|
{
|
||||||
label: 'Jar',
|
label: 'Jar',
|
||||||
value: 'jar',
|
value: 'jar',
|
||||||
logo: getImage('/jar.png'),
|
iconUrl: getImage('/jar.png'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Local',
|
label: 'Local',
|
||||||
value: 'local',
|
value: 'local',
|
||||||
logo: getImage('/local.png'),
|
iconUrl: getImage('/local.png'),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const formData = ref({
|
const formData = ref<FormDataType>({
|
||||||
type: 'jar',
|
type: 'jar',
|
||||||
name: '',
|
name: '',
|
||||||
configuration: {
|
configuration: {
|
||||||
|
@ -108,38 +128,20 @@ const formData = ref({
|
||||||
},
|
},
|
||||||
description: '',
|
description: '',
|
||||||
});
|
});
|
||||||
|
const changeType = (value: Array<string>) => {
|
||||||
|
formData.value.type = value[0];
|
||||||
|
};
|
||||||
|
|
||||||
const { resetFields, validate, validateInfos } = useForm(
|
const onSubmit = async () => {
|
||||||
formData,
|
const data: any = await formRef.value?.validate();
|
||||||
reactive({
|
loading.value = true;
|
||||||
type: [{ required: true, message: '请选择类型', trigger: 'blur' }],
|
const response = !id
|
||||||
name: [
|
? await save(data).catch(() => {})
|
||||||
{ required: true, message: '请输入名称', trigger: 'blur' },
|
: await update({ ...props.data, ...data }).catch(() => {});
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
if (response?.status === 200) {
|
||||||
],
|
emit('change', response?.status === 200);
|
||||||
'configuration.location': [
|
}
|
||||||
{ required: true, message: '请输入文件地址', trigger: 'blur' },
|
loading.value = false;
|
||||||
],
|
|
||||||
description: [{ max: 200, message: '最多可输入200个字符' }],
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
const onSubmit = () => {
|
|
||||||
validate()
|
|
||||||
.then(async (res) => {
|
|
||||||
const params = toRaw(formData.value);
|
|
||||||
|
|
||||||
loading.value = true;
|
|
||||||
const response = !id
|
|
||||||
? await save(params)
|
|
||||||
: await update({ ...props.data, ...params });
|
|
||||||
if (response.status === 200) {
|
|
||||||
emit('change', true);
|
|
||||||
}
|
|
||||||
loading.value = false;
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
loading.value = false;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (info: UploadChangeParam) => {
|
const handleChange = (info: UploadChangeParam) => {
|
||||||
|
@ -168,7 +170,12 @@ watch(
|
||||||
watch(
|
watch(
|
||||||
() => props.data,
|
() => props.data,
|
||||||
(value) => {
|
(value) => {
|
||||||
if (value.id) formData.value = value;
|
if (value.id) {
|
||||||
|
formData.value = value as FormDataType;
|
||||||
|
if (!!value.type[0]?.value) {
|
||||||
|
formData.value.type = value.type.map((i: any) => i.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true, deep: true },
|
{ immediate: true, deep: true },
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<div>
|
<div>
|
||||||
<Search :columns="columns" target="search" @search="handleSearch" />
|
<pro-search
|
||||||
|
:columns="columns"
|
||||||
|
target="search"
|
||||||
|
@search="handleSearch"
|
||||||
|
/>
|
||||||
|
|
||||||
<j-pro-table
|
<j-pro-table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
export interface FormDataType {
|
||||||
|
type: string | array<string>;
|
||||||
|
name: string;
|
||||||
|
configuration: {
|
||||||
|
location: string;
|
||||||
|
};
|
||||||
|
description: string;
|
||||||
|
}
|
|
@ -44,21 +44,17 @@
|
||||||
name="shareCluster"
|
name="shareCluster"
|
||||||
:rules="Rules.shareCluster"
|
:rules="Rules.shareCluster"
|
||||||
>
|
>
|
||||||
<div class="form-label">
|
<template #label>
|
||||||
集群
|
集群
|
||||||
<span class="form-label-required">*</span>
|
<j-tooltip
|
||||||
<j-tooltip>
|
title="共享配置:集群下所有节点共用同一配置,独立配置:集群下不同节点使用不同配置"
|
||||||
<template #title>
|
>
|
||||||
<p>
|
<AIcon
|
||||||
共享配置:集群下所有节点共用同一配置
|
type="QuestionCircleOutlined"
|
||||||
</p>
|
style="margin-left: 2px"
|
||||||
<p>
|
/>
|
||||||
独立配置:集群下不同节点使用不同配置
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
<AIcon type="QuestionCircleOutlined" />
|
|
||||||
</j-tooltip>
|
</j-tooltip>
|
||||||
</div>
|
</template>
|
||||||
<j-radio-group
|
<j-radio-group
|
||||||
v-model:value="formData.shareCluster"
|
v-model:value="formData.shareCluster"
|
||||||
button-style="solid"
|
button-style="solid"
|
||||||
|
@ -78,7 +74,6 @@
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-col>
|
</j-col>
|
||||||
</j-row>
|
</j-row>
|
||||||
|
|
||||||
<j-form
|
<j-form
|
||||||
ref="formRef2"
|
ref="formRef2"
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
|
@ -113,14 +108,9 @@
|
||||||
index,
|
index,
|
||||||
'serverId',
|
'serverId',
|
||||||
]"
|
]"
|
||||||
|
label="节点名称"
|
||||||
|
:rules="Rules.serverId"
|
||||||
>
|
>
|
||||||
<div class="form-label">
|
|
||||||
节点名称
|
|
||||||
<span
|
|
||||||
class="form-label-required"
|
|
||||||
>*</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<j-select
|
<j-select
|
||||||
v-model:value="
|
v-model:value="
|
||||||
cluster.serverId
|
cluster.serverId
|
||||||
|
@ -159,24 +149,19 @@
|
||||||
]"
|
]"
|
||||||
:rules="Rules.host"
|
:rules="Rules.host"
|
||||||
>
|
>
|
||||||
<div class="form-label">
|
<template #label>
|
||||||
本地地址
|
本地地址
|
||||||
<span
|
<j-tooltip
|
||||||
class="form-label-required"
|
title="绑定到服务器上的网卡地址,绑定到所有网卡:0.0.0.0"
|
||||||
>*</span
|
|
||||||
>
|
>
|
||||||
<j-tooltip>
|
|
||||||
<template #title>
|
|
||||||
<p>
|
|
||||||
绑定到服务器上的网卡地址,绑定到所有网卡:0.0.0.0
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
|
style="
|
||||||
|
margin-left: 2px;
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</j-tooltip>
|
</j-tooltip>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<j-select
|
<j-select
|
||||||
v-model:value="
|
v-model:value="
|
||||||
cluster.configuration
|
cluster.configuration
|
||||||
|
@ -220,23 +205,19 @@
|
||||||
]"
|
]"
|
||||||
:rules="Rules.port"
|
:rules="Rules.port"
|
||||||
>
|
>
|
||||||
<div class="form-label">
|
<template #label>
|
||||||
本地端口
|
本地端口
|
||||||
<span
|
<j-tooltip
|
||||||
class="form-label-required"
|
title="监听指定端口的请求"
|
||||||
>*</span
|
|
||||||
>
|
>
|
||||||
<j-tooltip>
|
|
||||||
<template #title>
|
|
||||||
<p>
|
|
||||||
监听指定端口的请求
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
|
style="
|
||||||
|
margin-left: 2px;
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</j-tooltip>
|
</j-tooltip>
|
||||||
</div>
|
</template>
|
||||||
<j-select
|
<j-select
|
||||||
v-model:value="
|
v-model:value="
|
||||||
cluster.configuration
|
cluster.configuration
|
||||||
|
@ -273,23 +254,19 @@
|
||||||
]"
|
]"
|
||||||
:rules="Rules.publicHost"
|
:rules="Rules.publicHost"
|
||||||
>
|
>
|
||||||
<div class="form-label">
|
<template #label>
|
||||||
公网地址
|
公网地址
|
||||||
<span
|
<j-tooltip
|
||||||
class="form-label-required"
|
title="对外提供访问的地址,内网环境时填写服务器的内网IP地址"
|
||||||
>*</span
|
|
||||||
>
|
>
|
||||||
<j-tooltip>
|
|
||||||
<template #title>
|
|
||||||
<p>
|
|
||||||
对外提供访问的地址,内网环境时填写服务器的内网IP地址
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
|
style="
|
||||||
|
margin-left: 2px;
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</j-tooltip>
|
</j-tooltip>
|
||||||
</div>
|
</template>
|
||||||
<j-input
|
<j-input
|
||||||
v-model:value="
|
v-model:value="
|
||||||
cluster.configuration
|
cluster.configuration
|
||||||
|
@ -318,24 +295,19 @@
|
||||||
]"
|
]"
|
||||||
:rules="Rules.publicPort"
|
:rules="Rules.publicPort"
|
||||||
>
|
>
|
||||||
<div class="form-label">
|
<template #label>
|
||||||
公网端口
|
公网端口
|
||||||
<span
|
<j-tooltip
|
||||||
class="form-label-required"
|
title="对外提供访问的端口"
|
||||||
>*</span
|
|
||||||
>
|
>
|
||||||
<j-tooltip>
|
|
||||||
<template #title>
|
|
||||||
<p>
|
|
||||||
对外提供访问的端口
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
|
style="
|
||||||
|
margin-left: 2px;
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</j-tooltip>
|
</j-tooltip>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<j-input-number
|
<j-input-number
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
placeholder="请输入端口"
|
placeholder="请输入端口"
|
||||||
|
@ -365,15 +337,8 @@
|
||||||
'remoteHost',
|
'remoteHost',
|
||||||
]"
|
]"
|
||||||
:rules="Rules.remoteHost"
|
:rules="Rules.remoteHost"
|
||||||
|
label="远程地址"
|
||||||
>
|
>
|
||||||
<div class="form-label">
|
|
||||||
远程地址
|
|
||||||
<span
|
|
||||||
class="form-label-required"
|
|
||||||
>*</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<j-input
|
<j-input
|
||||||
placeholder="请输入远程地址"
|
placeholder="请输入远程地址"
|
||||||
v-model:value="
|
v-model:value="
|
||||||
|
@ -517,19 +482,19 @@
|
||||||
]"
|
]"
|
||||||
:rules="Rules.topicPrefix"
|
:rules="Rules.topicPrefix"
|
||||||
>
|
>
|
||||||
<div class="form-label">
|
<template #label>
|
||||||
订阅前缀
|
订阅前缀
|
||||||
<j-tooltip>
|
<j-tooltip
|
||||||
<template #title>
|
title="当连接的服务为EMQ时,可能需要使用共享的订阅前缀,如:$queue或$share"
|
||||||
<p>
|
>
|
||||||
当连接的服务为EMQ时,可能需要使用共享的订阅前缀,如:$queue或$share
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
|
style="
|
||||||
|
margin-left: 2px;
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</j-tooltip>
|
</j-tooltip>
|
||||||
</div>
|
</template>
|
||||||
<j-input
|
<j-input
|
||||||
v-model:value="
|
v-model:value="
|
||||||
cluster.configuration
|
cluster.configuration
|
||||||
|
@ -558,23 +523,19 @@
|
||||||
]"
|
]"
|
||||||
:rules="Rules.maxMessageSize"
|
:rules="Rules.maxMessageSize"
|
||||||
>
|
>
|
||||||
<div class="form-label">
|
<template #label>
|
||||||
最大消息长度
|
最大消息长度
|
||||||
<span
|
<j-tooltip
|
||||||
class="form-label-required"
|
title="单次收发消息的最大长度,单位:字节;设置过大可能会影响性能"
|
||||||
>*</span
|
|
||||||
>
|
>
|
||||||
<j-tooltip>
|
|
||||||
<template #title>
|
|
||||||
<p>
|
|
||||||
单次收发消息的最大长度,单位:字节;设置过大可能会影响性能
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
|
style="
|
||||||
|
margin-left: 2px;
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</j-tooltip>
|
</j-tooltip>
|
||||||
</div>
|
</template>
|
||||||
<j-input-number
|
<j-input-number
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
v-model:value="
|
v-model:value="
|
||||||
|
@ -691,23 +652,19 @@
|
||||||
]"
|
]"
|
||||||
:rules="Rules.parserType"
|
:rules="Rules.parserType"
|
||||||
>
|
>
|
||||||
<div class="form-label">
|
<template #label>
|
||||||
粘拆包规则
|
粘拆包规则
|
||||||
<span
|
<j-tooltip
|
||||||
class="form-label-required"
|
title="处理TCP粘拆包的方式"
|
||||||
>*</span
|
|
||||||
>
|
>
|
||||||
<j-tooltip>
|
|
||||||
<template #title>
|
|
||||||
<p>
|
|
||||||
处理TCP粘拆包的方式
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
<AIcon
|
<AIcon
|
||||||
type="QuestionCircleOutlined"
|
type="QuestionCircleOutlined"
|
||||||
|
style="
|
||||||
|
margin-left: 2px;
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
</j-tooltip>
|
</j-tooltip>
|
||||||
</div>
|
</template>
|
||||||
<j-select
|
<j-select
|
||||||
style="width: 48.5%"
|
style="width: 48.5%"
|
||||||
v-model:value="
|
v-model:value="
|
||||||
|
@ -1037,7 +994,7 @@ import {
|
||||||
UDPList,
|
UDPList,
|
||||||
} from '../data';
|
} from '../data';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
import type { FormData2Type } from '../type';
|
import type { FormData2Type, FormDataType } from '../type';
|
||||||
import { Store } from 'jetlinks-store';
|
import { Store } from 'jetlinks-store';
|
||||||
import MonacoEditor from '@/components/MonacoEditor/index.vue';
|
import MonacoEditor from '@/components/MonacoEditor/index.vue';
|
||||||
|
|
||||||
|
@ -1051,8 +1008,9 @@ const formRef1 = ref<FormInstance>();
|
||||||
const formRef2 = ref<FormInstance>();
|
const formRef2 = ref<FormInstance>();
|
||||||
const shareCluster = ref(true);
|
const shareCluster = ref(true);
|
||||||
|
|
||||||
const formData = ref(FormStates);
|
const formData = ref<FormDataType>({
|
||||||
|
...FormStates,
|
||||||
|
});
|
||||||
const hostOptionsIndex: any = ref([]);
|
const hostOptionsIndex: any = ref([]);
|
||||||
const clustersListIndex: any = ref([]);
|
const clustersListIndex: any = ref([]);
|
||||||
const typeOptions = ref([]);
|
const typeOptions = ref([]);
|
||||||
|
@ -1088,7 +1046,7 @@ const filterPortOption = (input: string, option: any) => {
|
||||||
return JSON.stringify(option.label).indexOf(input) >= 0;
|
return JSON.stringify(option.label).indexOf(input) >= 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterConfigByType = (data, type: string) => {
|
const filterConfigByType = (data: any, type: string) => {
|
||||||
let _temp = type;
|
let _temp = type;
|
||||||
if (TCPList.includes(type)) {
|
if (TCPList.includes(type)) {
|
||||||
_temp = 'TCP';
|
_temp = 'TCP';
|
||||||
|
@ -1096,12 +1054,12 @@ const filterConfigByType = (data, type: string) => {
|
||||||
_temp = 'UDP';
|
_temp = 'UDP';
|
||||||
}
|
}
|
||||||
// 只保留ports 包含type的数据
|
// 只保留ports 包含type的数据
|
||||||
const _config = data?.filter((item) => {
|
const _config = data?.filter((item: any) => {
|
||||||
return Object.keys(item.ports).includes(_temp);
|
return Object.keys(item.ports).includes(_temp);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 只保留ports的type数据
|
// 只保留ports的type数据
|
||||||
return _config?.map((i) => {
|
return _config?.map((i: any) => {
|
||||||
i.ports = i.ports[_temp];
|
i.ports = i.ports[_temp];
|
||||||
return i;
|
return i;
|
||||||
});
|
});
|
||||||
|
@ -1112,8 +1070,8 @@ const getPortOptions = (portOptions: object, index = 0) => {
|
||||||
const type = formData.value.type;
|
const type = formData.value.type;
|
||||||
const host = dynamicValidateForm.cluster[index].configuration.host;
|
const host = dynamicValidateForm.cluster[index].configuration.host;
|
||||||
const _port = filterConfigByType(cloneDeep(portOptions), type);
|
const _port = filterConfigByType(cloneDeep(portOptions), type);
|
||||||
const _host = _port.find((item) => item.host === host);
|
const _host = _port.find((item: any) => item.host === host);
|
||||||
portOptionsIndex.value[index] = _host?.ports?.map((p) => ({
|
portOptionsIndex.value[index] = _host?.ports?.map((p: any) => ({
|
||||||
label: p,
|
label: p,
|
||||||
value: p,
|
value: p,
|
||||||
}));
|
}));
|
||||||
|
@ -1137,11 +1095,11 @@ const updateClustersListIndex = () => {
|
||||||
const { cluster } = dynamicValidateForm;
|
const { cluster } = dynamicValidateForm;
|
||||||
const filters = cluster?.map((item) => item.serverId);
|
const filters = cluster?.map((item) => item.serverId);
|
||||||
const newConfigRef = Store.get('configRef')?.filter(
|
const newConfigRef = Store.get('configRef')?.filter(
|
||||||
(item) => !filters.includes(item.clusterNodeId),
|
(item: any) => !filters.includes(item.clusterNodeId),
|
||||||
);
|
);
|
||||||
cluster.forEach((item, index) => {
|
cluster.forEach((item, index) => {
|
||||||
!item.serverId &&
|
!item.serverId &&
|
||||||
(clustersListIndex.value[index] = newConfigRef?.map((i) => ({
|
(clustersListIndex.value[index] = newConfigRef?.map((i: any) => ({
|
||||||
value: i.clusterNodeId,
|
value: i.clusterNodeId,
|
||||||
lable: i.clusterNodeId,
|
lable: i.clusterNodeId,
|
||||||
})));
|
})));
|
||||||
|
@ -1153,9 +1111,9 @@ const changeServerId = (value: string | undefined, index: number) => {
|
||||||
configuration.host = undefined;
|
configuration.host = undefined;
|
||||||
configuration.port = undefined;
|
configuration.port = undefined;
|
||||||
const checked = cloneDeep(portOptionsConst).find(
|
const checked = cloneDeep(portOptionsConst).find(
|
||||||
(i) => i.clusterNodeId === value,
|
(i: any) => i.clusterNodeId === value,
|
||||||
);
|
);
|
||||||
const checkedHost = [{ value: checked.host, lable: checked.host }];
|
const checkedHost = [{ value: checked?.host, lable: checked?.host }];
|
||||||
hostOptionsIndex.value[index] = checked ? checkedHost : [];
|
hostOptionsIndex.value[index] = checked ? checkedHost : [];
|
||||||
updateClustersListIndex();
|
updateClustersListIndex();
|
||||||
};
|
};
|
||||||
|
@ -1167,7 +1125,7 @@ const changeHost = (
|
||||||
const { configuration } = dynamicValidateForm.cluster[index];
|
const { configuration } = dynamicValidateForm.cluster[index];
|
||||||
configuration.port = undefined;
|
configuration.port = undefined;
|
||||||
const checked = cloneDeep(portOptionsConst).find(
|
const checked = cloneDeep(portOptionsConst).find(
|
||||||
(i) => i.clusterNodeId === serverId && i.host === value,
|
(i: any) => i.clusterNodeId === serverId && i.host === value,
|
||||||
);
|
);
|
||||||
checked && getPortOptions([checked], index);
|
checked && getPortOptions([checked], index);
|
||||||
};
|
};
|
||||||
|
@ -1189,8 +1147,10 @@ const saveData = async () => {
|
||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const resp =
|
const resp =
|
||||||
id === ':id' ? await save(params) : await update({ ...params, id });
|
id === ':id'
|
||||||
if (resp.status === 200) {
|
? await save(params).catch(() => {})
|
||||||
|
: await update({ ...params, id }).catch(() => {});
|
||||||
|
if (resp?.status === 200) {
|
||||||
message.success('操作成功!');
|
message.success('操作成功!');
|
||||||
history.back();
|
history.back();
|
||||||
}
|
}
|
||||||
|
@ -1198,19 +1158,23 @@ const saveData = async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSupports = async () => {
|
const getSupports = async () => {
|
||||||
const res = await supports();
|
const res: any = await supports();
|
||||||
typeOptions.value = res.result.map((item) => ({
|
if (res.status === 200) {
|
||||||
label: item.name,
|
typeOptions.value = res.result.map((item: any) => ({
|
||||||
value: item.id,
|
label: item.name,
|
||||||
}));
|
value: item.id,
|
||||||
|
}));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCertificates = async () => {
|
const getCertificates = async () => {
|
||||||
const resp = await certificates();
|
const resp: any = await certificates();
|
||||||
certIdOptions.value = resp.result.map((i) => ({
|
if (resp.status === 200) {
|
||||||
value: i.id,
|
certIdOptions.value = resp.result.map((i: any) => ({
|
||||||
label: i.name,
|
value: i.id,
|
||||||
}));
|
label: i.name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getResourcesCurrent = () => {
|
const getResourcesCurrent = () => {
|
||||||
|
@ -1227,18 +1191,21 @@ const getDetail = () => {
|
||||||
if (id !== ':id') {
|
if (id !== ':id') {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
detail(id).then((resp) => {
|
detail(id).then((resp) => {
|
||||||
const result = resp.result;
|
if (resp.status === 200) {
|
||||||
const { configuration, cluster } = result;
|
const result: any = resp.result;
|
||||||
formData.value = { ...result };
|
const { configuration, cluster } = result;
|
||||||
shareCluster.value = result.shareCluster;
|
formData.value = { ...result };
|
||||||
activeKey.value = ['1'];
|
shareCluster.value = result.shareCluster;
|
||||||
if (result.shareCluster) {
|
activeKey.value = ['1'];
|
||||||
dynamicValidateForm.cluster[0].configuration = configuration;
|
if (result.shareCluster) {
|
||||||
} else {
|
dynamicValidateForm.cluster[0].configuration =
|
||||||
dynamicValidateForm.cluster = cluster;
|
configuration;
|
||||||
|
} else {
|
||||||
|
dynamicValidateForm.cluster = cluster;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
loading.value = false;
|
|
||||||
});
|
});
|
||||||
|
loading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1258,7 +1225,7 @@ watch(
|
||||||
updateClustersListIndex();
|
updateClustersListIndex();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ deep: true },
|
{ deep: true, immediate: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
@ -1296,36 +1263,12 @@ watch(
|
||||||
.container {
|
.container {
|
||||||
width: 70%;
|
width: 70%;
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
.card-item {
|
|
||||||
padding-right: 5px;
|
|
||||||
max-height: 480px;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
.card-last {
|
|
||||||
padding-right: 5px;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.footer {
|
.footer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-item1 {
|
|
||||||
border: 1px solid #d9d9d9;
|
|
||||||
padding: 10px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.form-label {
|
|
||||||
height: 30px;
|
|
||||||
padding-bottom: 8px;
|
|
||||||
.form-label-required {
|
|
||||||
color: red;
|
|
||||||
margin: 0 4px 0 -2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.collapse {
|
.collapse {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,6 +135,12 @@ export const Rules = {
|
||||||
message: '请选择集群',
|
message: '请选择集群',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
serverId: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择节点名称',
|
||||||
|
},
|
||||||
|
],
|
||||||
host: [
|
host: [
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -304,6 +310,7 @@ export const Rules = {
|
||||||
|
|
||||||
export const TiTlePermissionButtonStyle = {
|
export const TiTlePermissionButtonStyle = {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
|
'max-width': 'calc(100% - 90px)',
|
||||||
color: ' #1890ff !important',
|
color: ' #1890ff !important',
|
||||||
'font-weight': 700,
|
'font-weight': 700,
|
||||||
'font-size': '16px',
|
'font-size': '16px',
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<div>
|
<div>
|
||||||
<Search :columns="columns" target="search" @search="handleSearch" />
|
<pro-search
|
||||||
|
:columns="columns"
|
||||||
|
target="search"
|
||||||
|
@search="handleSearch"
|
||||||
|
/>
|
||||||
|
|
||||||
<j-pro-table
|
<j-pro-table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
|
@ -297,8 +301,6 @@ const getActions = (
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
message.success('操作成功');
|
message.success('操作成功');
|
||||||
tableRef.value?.reload();
|
tableRef.value?.reload();
|
||||||
} else {
|
|
||||||
message.error('操作失败!');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -318,8 +320,6 @@ const getActions = (
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
message.success('操作成功');
|
message.success('操作成功');
|
||||||
tableRef.value.reload();
|
tableRef.value.reload();
|
||||||
} else {
|
|
||||||
message.error('操作失败!');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -361,8 +361,8 @@ const getDetails = (slotProps: Partial<Record<string, any>>) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSupports = async () => {
|
const getSupports = async () => {
|
||||||
const res = await supports();
|
const res: any = await supports();
|
||||||
options.value = res.result.map((item) => ({
|
options.value = res.result.map((item: any) => ({
|
||||||
value: item.id,
|
value: item.id,
|
||||||
label: item.name,
|
label: item.name,
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
export interface ConfigurationType {
|
export interface ConfigurationType {
|
||||||
parserType: string | undefined;
|
parserType: string | undefined;
|
||||||
port: string | undefined;
|
port: string | undefined;
|
||||||
host: string | undefined;;
|
host: string | undefined;
|
||||||
publicPort: string;
|
publicPort: string;
|
||||||
publicHost: string;
|
publicHost: string;
|
||||||
remoteHost: string;
|
remoteHost: string;
|
||||||
|
|
Loading…
Reference in New Issue