Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev

This commit is contained in:
JiangQiming 2023-03-16 10:20:58 +08:00
commit e9d3546197
29 changed files with 481 additions and 433 deletions

View File

@ -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 () => {

View File

@ -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 = {

View File

@ -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;

View File

@ -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>

View File

@ -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) => {

View File

@ -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) => {

View File

@ -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);
} }
}); });
}; };

View File

@ -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>

View File

@ -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',

View File

@ -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 {

View File

@ -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('操作失败');
} }

View File

@ -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;

View File

@ -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;
} }
} }
} }

View File

@ -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',

View File

@ -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

View File

@ -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',

View File

@ -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);

View File

@ -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

View File

@ -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,
})); }));

4
src/views/link/DashBoard/typings.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
export type DataType = {
type: string | undefined;
time: array<any>;
};

View File

@ -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>

View File

@ -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 },
); );

View File

@ -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"

8
src/views/link/Protocol/type.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
export interface FormDataType {
type: string | array<string>;
name: string;
configuration: {
location: string;
};
description: string;
}

View File

@ -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);
}); });
// portstype // portstype
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;
} }

View File

@ -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',

View File

@ -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,
})); }));

View File

@ -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;

View File

@ -260,14 +260,17 @@ function menuChange(
// //
if (row.buttons && row.buttons.length > 0) setStatus(row, 'buttons'); if (row.buttons && row.buttons.length > 0) setStatus(row, 'buttons');
else setStatus(row, 'children'); else setStatus(row, 'children');
// //
if (row.selectAccesses !== undefined) { if (row.accessSupport && row.accessSupport.value === 'support') {
if (!row.granted) { //
if (row.selectAccesses && !row.granted && !row.indeterminate)
row.selectAccesses = ''; row.selectAccesses = '';
} else if (row.selectAccesses === '') { // 'creator'
else if (!row.selectAccesses && (row.granted || row.indeterminate))
row.selectAccesses = 'creator'; row.selectAccesses = 'creator';
}
} }
// //
if (row.parentId) { if (row.parentId) {
// // // //
@ -401,3 +404,11 @@ type tableItemType = {
assetAccesses?: any[]; assetAccesses?: any[];
}; };
</script> </script>
<style lang="less" scoped>
.permiss-tree-container {
:deep(.ant-checkbox-wrapper) {
margin-left: 0;
}
}
</style>