feat: 通道管理 卡片列表、新增编辑等功能
This commit is contained in:
parent
dd1c05e0b8
commit
349f066870
|
@ -1,10 +1,27 @@
|
||||||
import server from '@/utils/request';
|
import server from '@/utils/request';
|
||||||
|
|
||||||
export const queryChannel = (type: string, data: any) =>
|
export const query = (data: any) =>
|
||||||
server.post(`/data-collect/channel/_query`, data);
|
server.post(`/data-collect/channel/_query`, data);
|
||||||
|
|
||||||
export const removeChannel = (type: string, data: any) =>
|
export const remove = (id: string) =>
|
||||||
server.remove(`/data-collect/channel//${id}`);
|
server.remove(`/data-collect/channel/${id}`);
|
||||||
|
|
||||||
export const updateChannel = (type: string, data: any) =>
|
export const save = (data: any) => server.post(`/data-collect/channel`, data);
|
||||||
server.patch(`/data-collect/channel//${id}`, data);
|
|
||||||
|
export const update = (id: string, data: any) =>
|
||||||
|
server.put(`/data-collect/channel/${id}`, data);
|
||||||
|
|
||||||
|
export const getProviders = () => server.get(`/gateway/device/providers`);
|
||||||
|
|
||||||
|
export const queryOptionsList = (type: strimg) =>
|
||||||
|
server.get(`/data-collect/opc/${type}`);
|
||||||
|
|
||||||
|
export const validateField = (data: any) =>
|
||||||
|
server.post(`/data-collect/opc/endpoint/_validate`, data, null, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/plain;charset=UTF-8',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const queryCertificateList = () =>
|
||||||
|
server.get(`/network/certificate/_query/no-paging?paging=false`, {});
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,315 @@
|
||||||
|
<template lang="">
|
||||||
|
<a-modal
|
||||||
|
:title="data.id ? '编辑' : '新增'"
|
||||||
|
:visible="true"
|
||||||
|
width="700px"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
class="form"
|
||||||
|
layout="vertical"
|
||||||
|
:model="formData"
|
||||||
|
name="basic"
|
||||||
|
autocomplete="off"
|
||||||
|
:rules="FormValidate"
|
||||||
|
ref="formRef"
|
||||||
|
>
|
||||||
|
<a-form-item label="通道名称" name="name">
|
||||||
|
<a-input
|
||||||
|
placeholder="请输入通道名称"
|
||||||
|
v-model:value="formData.name"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="通讯协议" name="provider">
|
||||||
|
<a-select
|
||||||
|
style="width: 100%"
|
||||||
|
v-model:value="formData.provider"
|
||||||
|
:options="providersList"
|
||||||
|
placeholder="请选择通讯协议"
|
||||||
|
allowClear
|
||||||
|
show-search
|
||||||
|
:filter-option="filterOption"
|
||||||
|
:disabled="!!id"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-if="formData.provider === 'MODBUS_TCP'"
|
||||||
|
:name="['configuration', 'host']"
|
||||||
|
:rules="FormValidate.host"
|
||||||
|
>
|
||||||
|
<div class="form-label">
|
||||||
|
Modbus主机IP
|
||||||
|
<span class="form-label-required">*</span>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>
|
||||||
|
<p>支持ipv4、ipv6、域名</p>
|
||||||
|
</template>
|
||||||
|
<AIcon type="QuestionCircleOutlined" />
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<a-input
|
||||||
|
placeholder="请输入Modbus主机IP"
|
||||||
|
v-model:value="formData.configuration.host"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-if="formData.provider === 'MODBUS_TCP'"
|
||||||
|
label="端口"
|
||||||
|
:name="['configuration', 'port']"
|
||||||
|
:rules="FormValidate.port"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
style="width: 100%"
|
||||||
|
placeholder="请输入端口"
|
||||||
|
v-model:value="formData.configuration.port"
|
||||||
|
:min="1"
|
||||||
|
:max="65535"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-if="formData.provider === 'OPC_UA'"
|
||||||
|
label="端点url"
|
||||||
|
:name="['configuration', 'endpoint']"
|
||||||
|
:rules="FormValidate.endpoint"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
placeholder="请输入端点url"
|
||||||
|
v-model:value="formData.configuration.endpoint"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-if="formData.provider === 'OPC_UA'"
|
||||||
|
label="安全策略"
|
||||||
|
:name="['configuration.securityPolicy']"
|
||||||
|
:rules="FormValidate.securityPolicy"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
style="width: 100%"
|
||||||
|
v-model:value="formData.configuration.securityPolicy"
|
||||||
|
:options="Options['security-policies']"
|
||||||
|
placeholder="请选择安全策略"
|
||||||
|
allowClear
|
||||||
|
show-search
|
||||||
|
:filter-option="filterOption"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-if="formData.provider === 'OPC_UA'"
|
||||||
|
label="安全模式"
|
||||||
|
:name="['configuration.securityMode']"
|
||||||
|
:rules="FormValidate.securityMode"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
style="width: 100%"
|
||||||
|
v-model:value="formData.configuration.securityMode"
|
||||||
|
:options="Options['security-modes']"
|
||||||
|
placeholder="请选择安全模式"
|
||||||
|
allowClear
|
||||||
|
show-search
|
||||||
|
:filter-option="filterOption"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-if="
|
||||||
|
formData.configuration.securityMode === 'SignAndEncrypt' ||
|
||||||
|
formData.configuration.securityMode === 'Sign'
|
||||||
|
"
|
||||||
|
label="证书"
|
||||||
|
:name="['configuration.certificate']"
|
||||||
|
:rules="FormValidate.certificate"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
style="width: 100%"
|
||||||
|
v-model:value="formData.configuration.certificate"
|
||||||
|
:options="certificateList"
|
||||||
|
placeholder="请选择证书"
|
||||||
|
allowClear
|
||||||
|
show-search
|
||||||
|
:filter-option="filterOption"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-if="formData.provider === 'OPC_UA'"
|
||||||
|
label="权限认证"
|
||||||
|
:name="['configuration.authType']"
|
||||||
|
:rules="FormValidate.authType"
|
||||||
|
>
|
||||||
|
<RadioCard
|
||||||
|
layout="horizontal"
|
||||||
|
:checkStyle="true"
|
||||||
|
:options="Options['auth-types']"
|
||||||
|
v-model="formData.configuration.authType"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-if="formData.configuration.authType === 'username'"
|
||||||
|
label="用户名"
|
||||||
|
:name="['configuration.username']"
|
||||||
|
:rules="FormValidate.username"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
placeholder="请输入用户名"
|
||||||
|
v-model:value="formData.configuration.username"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
v-if="formData.configuration.authType === 'username'"
|
||||||
|
label="密码"
|
||||||
|
:name="['configuration.password']"
|
||||||
|
:rules="FormValidate.password"
|
||||||
|
>
|
||||||
|
<a-input-password
|
||||||
|
placeholder="请输入密码"
|
||||||
|
v-model:value="formData.configuration.password"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="说明" name="description">
|
||||||
|
<a-textarea
|
||||||
|
placeholder="请输入说明"
|
||||||
|
v-model:value="formData.description"
|
||||||
|
:maxlength="200"
|
||||||
|
:rows="3"
|
||||||
|
showCount
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<template #footer>
|
||||||
|
<a-button key="back" @click="handleCancel">取消</a-button>
|
||||||
|
<PermissionButton
|
||||||
|
key="submit"
|
||||||
|
type="primary"
|
||||||
|
:loading="loading"
|
||||||
|
@click="handleOk"
|
||||||
|
style="margin-left: 8px"
|
||||||
|
:hasPermission="`DataCollect/Channel:${id ? 'update' : 'add'}`"
|
||||||
|
>
|
||||||
|
确认
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {
|
||||||
|
save,
|
||||||
|
update,
|
||||||
|
queryOptionsList,
|
||||||
|
queryCertificateList,
|
||||||
|
getProviders,
|
||||||
|
} from '@/api/data-collect/channel';
|
||||||
|
import { FormValidate, FormState } from '../data';
|
||||||
|
import type { FormInstance } from 'ant-design-vue';
|
||||||
|
import type { FormDataType } from '../type.d';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['change']);
|
||||||
|
const loading = ref(false);
|
||||||
|
const id = props.data.id;
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const certificateList = ref([]);
|
||||||
|
const providersList = ref([]);
|
||||||
|
const Options = ref({
|
||||||
|
'auth-types': [],
|
||||||
|
'security-modes': [],
|
||||||
|
'security-policies': [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const formData = ref<FormDataType>(FormState);
|
||||||
|
|
||||||
|
const handleOk = async () => {
|
||||||
|
const params = await formRef.value?.validate();
|
||||||
|
loading.value = true;
|
||||||
|
const response = !id
|
||||||
|
? await save(params)
|
||||||
|
: await update(id, { ...props.data, ...params });
|
||||||
|
if (response.status === 200) {
|
||||||
|
emit('change', true);
|
||||||
|
}
|
||||||
|
loading.value = false;
|
||||||
|
formRef.value?.resetFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
emit('change', false);
|
||||||
|
formRef.value?.resetFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
const filterOption = (input: string, option: any) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOptionsList = async () => {
|
||||||
|
for (let key in Options.value) {
|
||||||
|
const res = await queryOptionsList(key);
|
||||||
|
Options.value[key] = res.result.map((item) => ({
|
||||||
|
label: item?.text || item,
|
||||||
|
value: item?.value || item,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const getCertificateList = async () => {
|
||||||
|
const res = await queryCertificateList();
|
||||||
|
certificateList.value = res.result.map((item) => ({
|
||||||
|
value: item.id,
|
||||||
|
label: item.name,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getProvidersList = async () => {
|
||||||
|
const resp = await getProviders();
|
||||||
|
if (resp.status === 200) {
|
||||||
|
const list = [
|
||||||
|
{ label: 'OPC UA', value: 'OPC_UA' },
|
||||||
|
{ label: 'Modbus TCP', value: 'MODBUS_TCP' },
|
||||||
|
];
|
||||||
|
const arr = resp.result
|
||||||
|
.filter(
|
||||||
|
(item: any) => item.id === 'modbus-tcp' || item.id === 'opc-ua',
|
||||||
|
)
|
||||||
|
.map((it: any) => (it?.id === 'opc-ua' ? 'OPC_UA' : 'MODBUS_TCP'));
|
||||||
|
const providers = list.filter((item: any) => arr.includes(item.value));
|
||||||
|
providersList.value = providers;
|
||||||
|
if (arr.includes('OPC_UA')) {
|
||||||
|
getOptionsList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
getProvidersList();
|
||||||
|
getCertificateList();
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
(value) => {
|
||||||
|
if (value.id) formData.value = value;
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.form {
|
||||||
|
.form-radio-button {
|
||||||
|
width: 148px;
|
||||||
|
height: 80px;
|
||||||
|
padding: 0;
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.form-label {
|
||||||
|
height: 30px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
.form-label-required {
|
||||||
|
color: red;
|
||||||
|
margin: 0 4px 0 -2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,305 +1,39 @@
|
||||||
export const Configuration = {
|
import { validateField } from '@/api/data-collect/channel';
|
||||||
parserType: undefined,
|
import { FormDataType } from './type.d';
|
||||||
port: undefined,
|
|
||||||
host: undefined,
|
|
||||||
publicPort: '',
|
|
||||||
publicHost: '',
|
|
||||||
remoteHost: '',
|
|
||||||
remotePort: '',
|
|
||||||
secure: false,
|
|
||||||
username: '',
|
|
||||||
password: '',
|
|
||||||
topicPrefix: '',
|
|
||||||
maxMessageSize: 8192,
|
|
||||||
certId: undefined,
|
|
||||||
privateKeyAlias: '',
|
|
||||||
clientId: '',
|
|
||||||
parserConfiguration: {
|
|
||||||
delimited: '',
|
|
||||||
lang: '',
|
|
||||||
script: '',
|
|
||||||
size: '',
|
|
||||||
length: '4',
|
|
||||||
offset: '0',
|
|
||||||
little: 'false',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const FormStates = {
|
export const FormState: FormDataType = {
|
||||||
name: '',
|
name: '',
|
||||||
type: 'UDP',
|
provider: undefined,
|
||||||
shareCluster: true,
|
configuration: {
|
||||||
|
host: '',
|
||||||
|
port: '502',
|
||||||
|
endpoint: '',
|
||||||
|
securityPolicy: undefined,
|
||||||
|
securityMode: undefined,
|
||||||
|
certificate: undefined,
|
||||||
|
authType: undefined,
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
},
|
||||||
description: '',
|
description: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FormStates2 = {
|
export const StatusColorEnum = {
|
||||||
serverId: undefined,
|
running: 'success',
|
||||||
configuration: Configuration,
|
disabled: 'error',
|
||||||
|
partialError: 'processing',
|
||||||
|
failed: 'warning',
|
||||||
|
stopped: 'default',
|
||||||
};
|
};
|
||||||
|
export const updateStatus = {
|
||||||
export const TCPList = [
|
disabled: {
|
||||||
'TCP_SERVER',
|
state: 'enabled',
|
||||||
'WEB_SOCKET_SERVER',
|
runningState: 'running',
|
||||||
'HTTP_SERVER',
|
},
|
||||||
'MQTT_SERVER',
|
enabled: {
|
||||||
];
|
state: 'disabled',
|
||||||
export const UDPList = ['UDP', 'COAP_SERVER'];
|
runningState: 'stopped',
|
||||||
|
},
|
||||||
const VisibleMost = [
|
|
||||||
'COAP_SERVER',
|
|
||||||
'MQTT_SERVER',
|
|
||||||
'WEB_SOCKET_SERVER',
|
|
||||||
'TCP_SERVER',
|
|
||||||
'UDP',
|
|
||||||
'HTTP_SERVER',
|
|
||||||
];
|
|
||||||
|
|
||||||
export const VisibleData = {
|
|
||||||
parserType: ['TCP_SERVER'],
|
|
||||||
port: VisibleMost,
|
|
||||||
host: VisibleMost,
|
|
||||||
publicPort: VisibleMost,
|
|
||||||
publicHost: VisibleMost,
|
|
||||||
serverId: ['MQTT_CLIENT'],
|
|
||||||
remoteHost: ['MQTT_CLIENT'],
|
|
||||||
remotePort: ['MQTT_CLIENT'],
|
|
||||||
secure: ['TCP_SERVER', 'UDP', 'COAP_SERVER'],
|
|
||||||
username: ['MQTT_CLIENT'],
|
|
||||||
password: ['MQTT_CLIENT'],
|
|
||||||
topicPrefix: ['MQTT_CLIENT'],
|
|
||||||
maxMessageSize: ['MQTT_SERVER', 'MQTT_CLIENT'],
|
|
||||||
clientId: ['MQTT_CLIENT'],
|
|
||||||
delimited: ['DELIMITED'],
|
|
||||||
lang: ['SCRIPT'],
|
|
||||||
script: ['SCRIPT'],
|
|
||||||
size: ['FIXED_LENGTH'],
|
|
||||||
length: ['LENGTH_FIELD'],
|
|
||||||
offset: ['LENGTH_FIELD'],
|
|
||||||
little: ['LENGTH_FIELD'],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ParserTypeOptions = [
|
|
||||||
{ value: 'DIRECT', label: '不处理' },
|
|
||||||
{ value: 'DELIMITED', label: '分隔符' },
|
|
||||||
{ value: 'SCRIPT', label: '自定义脚本' },
|
|
||||||
{ value: 'FIXED_LENGTH', label: '固定长度' },
|
|
||||||
{ value: 'LENGTH_FIELD', label: '长度字段' },
|
|
||||||
];
|
|
||||||
export const LengthOptions = [
|
|
||||||
{ value: '1', label: '1' },
|
|
||||||
{ value: '2', label: '2' },
|
|
||||||
{ value: '3', label: '3' },
|
|
||||||
{ value: '4', label: '4' },
|
|
||||||
{ value: '8', label: '8' },
|
|
||||||
];
|
|
||||||
export const LittleOptions = [
|
|
||||||
{ label: '大端', value: 'false' },
|
|
||||||
{ label: '小端', value: 'true' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const isVisible = (
|
|
||||||
LastName: string,
|
|
||||||
dependencies: string | boolean | undefined,
|
|
||||||
) => VisibleData[LastName].includes(dependencies);
|
|
||||||
|
|
||||||
export const Validator = {
|
|
||||||
regIp: new RegExp(
|
|
||||||
/((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}/,
|
|
||||||
),
|
|
||||||
regDomain: new RegExp(
|
|
||||||
/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/,
|
|
||||||
),
|
|
||||||
regOnlyNumber: new RegExp(/^\d+$/),
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Rules = {
|
|
||||||
name: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入名称',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
max: 64,
|
|
||||||
message: '最大可输入64个字符',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
type: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请选择类型',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
shareCluster: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请选择集群',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
host: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请选择本地地址',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
port: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请选择本地端口',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
publicHost: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入公网地址',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: Validator.regIp || Validator.regDomain,
|
|
||||||
message: '请输入IP或者域名',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
publicPort: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入公网端口',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: Validator.regOnlyNumber,
|
|
||||||
message: '请输入1-65535之间的正整数',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
remoteHost: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入远程地址',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: Validator.regIp || Validator.regDomain,
|
|
||||||
message: '请输入IP或者域名',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
remotePort: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '输入远程端口',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: Validator.regOnlyNumber,
|
|
||||||
message: '请输入1-65535之间的正整数',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
clientId: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入ClientId',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
max: 64,
|
|
||||||
message: '最大可输入64个字符',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
username: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入用户名',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
max: 64,
|
|
||||||
message: '最大可输入64个字符',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
password: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入密码',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
max: 64,
|
|
||||||
message: '最大可输入64个字符',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
topicPrefix: [
|
|
||||||
{
|
|
||||||
max: 64,
|
|
||||||
message: '最大可输入64个字符',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
maxMessageSize: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入最大消息长度',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
secure: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
certId: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请选择证书',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
privateKeyAlias: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入私钥别名',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
max: 64,
|
|
||||||
message: '最大可输入64个字符',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
parserType: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请选择粘拆包规则',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
delimited: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入分隔符',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
max: 64,
|
|
||||||
message: '最大可输入64个字符',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
lang: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请选择脚本语言',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
max: 64,
|
|
||||||
message: '最大可输入64个字符',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
script: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入脚本',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
size: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入长度值',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
length: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请选择长度',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
offset: [
|
|
||||||
{
|
|
||||||
pattern: Validator.regOnlyNumber,
|
|
||||||
message: '请输入0-65535之间的正整数',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TiTlePermissionButtonStyle = {
|
export const TiTlePermissionButtonStyle = {
|
||||||
|
@ -313,3 +47,95 @@ export const TiTlePermissionButtonStyle = {
|
||||||
width: 'calc(100%-100px)',
|
width: 'calc(100%-100px)',
|
||||||
// width: '60%',
|
// width: '60%',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const regOnlyNumber = new RegExp(/^\d+$/);
|
||||||
|
|
||||||
|
export const regIP = new RegExp(
|
||||||
|
/^([0-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.([0-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.([0-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.([0-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$/,
|
||||||
|
);
|
||||||
|
export const regIPv6 = new RegExp(
|
||||||
|
/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/,
|
||||||
|
);
|
||||||
|
export const regDomain = new RegExp(
|
||||||
|
/([0-9a-z-]{2,}\.[0-9a-z-]{2,3}\.[0-9a-z-]{2,3}|[0-9a-z-]{2,}\.[0-9a-z-]{2,3})$/i,
|
||||||
|
);
|
||||||
|
export const checkEndpoint = (_rule: Rule, value: string): Promise<any> =>
|
||||||
|
new Promise(async (resolve, reject) => {
|
||||||
|
if (value) {
|
||||||
|
const res = await validateField(value);
|
||||||
|
return res.result.passed ? resolve('') : reject(res.result.reason);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
export const FormValidate = {
|
||||||
|
name: [
|
||||||
|
{ required: true, message: '请输入名称', trigger: 'blur' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
],
|
||||||
|
provider: [{ required: true, message: '请选择通讯协议' }],
|
||||||
|
host: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入Modbus主机IP',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: regIP || regIPv6 || regDomain,
|
||||||
|
message: '请输入正确格式的Modbus主机IP地址',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
port: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入端口',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: regOnlyNumber,
|
||||||
|
message: '请输入1-65535之间的正整数',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
endpoint: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入端点url',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: checkEndpoint,
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
securityPolicy: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择安全策略',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
securityMode: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择安全模式',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
certificate: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择证书',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
authType: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择权限认证',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
username: [
|
||||||
|
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
],
|
||||||
|
|
||||||
|
description: [{ max: 200, message: '最多可输入200个字符' }],
|
||||||
|
};
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
<div>
|
<div>
|
||||||
<Search :columns="columns" target="search" @search="handleSearch" />
|
<Search :columns="columns" target="search" @search="handleSearch" />
|
||||||
|
|
||||||
<JTable
|
<j-pro-table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
model="CARD"
|
model="CARD"
|
||||||
:gridColumn="3"
|
:gridColumn="3"
|
||||||
:request="queryChannel"
|
:request="query"
|
||||||
:defaultParams="{
|
:defaultParams="{
|
||||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||||
}"
|
}"
|
||||||
|
@ -30,12 +30,9 @@
|
||||||
:value="slotProps"
|
:value="slotProps"
|
||||||
:actions="getActions(slotProps, 'card')"
|
:actions="getActions(slotProps, 'card')"
|
||||||
v-bind="slotProps"
|
v-bind="slotProps"
|
||||||
:status="slotProps.state.value"
|
:status="getState(slotProps).value"
|
||||||
:statusText="slotProps.state.text"
|
:statusText="getState(slotProps).text"
|
||||||
:statusNames="{
|
:statusNames="StatusColorEnum"
|
||||||
enabled: 'success',
|
|
||||||
disabled: 'error',
|
|
||||||
}"
|
|
||||||
>
|
>
|
||||||
<template #img>
|
<template #img>
|
||||||
<slot name="img">
|
<slot name="img">
|
||||||
|
@ -47,7 +44,7 @@
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
type="link"
|
type="link"
|
||||||
@click="handlEye(slotProps.id)"
|
@click="handlEye(slotProps.id)"
|
||||||
hasPermission="link/Type:view"
|
hasPermission="DataCollect/Collector:view"
|
||||||
:style="TiTlePermissionButtonStyle"
|
:style="TiTlePermissionButtonStyle"
|
||||||
>
|
>
|
||||||
{{ slotProps.name }}
|
{{ slotProps.name }}
|
||||||
|
@ -99,7 +96,9 @@
|
||||||
...item.tooltip,
|
...item.tooltip,
|
||||||
}"
|
}"
|
||||||
@click="item.onClick"
|
@click="item.onClick"
|
||||||
:hasPermission="'link/Type:' + item.key"
|
:hasPermission="
|
||||||
|
'DataCollect/Channel:' + item.key
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<AIcon
|
<AIcon
|
||||||
type="DeleteOutlined"
|
type="DeleteOutlined"
|
||||||
|
@ -113,30 +112,31 @@
|
||||||
</template>
|
</template>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
</template>
|
</template>
|
||||||
</JTable>
|
</j-pro-table>
|
||||||
|
<Save v-if="visible" :data="current" @change="saveChange" />
|
||||||
</div>
|
</div>
|
||||||
</page-container>
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup name="TypePage">
|
<script lang="ts" setup name="TypePage">
|
||||||
import type { ActionsType } from '@/components/Table/index';
|
import type { ActionsType } from '@/components/Table/index';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
import {
|
import { query, remove, update } from '@/api/data-collect/channel';
|
||||||
queryChannel,
|
|
||||||
updateChannel,
|
|
||||||
removeChannel,
|
|
||||||
} from '@/api/data-collect/channel';
|
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import { TiTlePermissionButtonStyle } from './data';
|
import {
|
||||||
|
TiTlePermissionButtonStyle,
|
||||||
|
StatusColorEnum,
|
||||||
|
updateStatus,
|
||||||
|
} from './data';
|
||||||
import { useMenuStore } from 'store/menu';
|
import { useMenuStore } from 'store/menu';
|
||||||
|
import Save from './Save/index.vue';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
const menuStory = useMenuStore();
|
const menuStory = useMenuStore();
|
||||||
const tableRef = ref<Record<string, any>>({});
|
const tableRef = ref<Record<string, any>>({});
|
||||||
const params = ref<Record<string, any>>({});
|
const params = ref<Record<string, any>>({});
|
||||||
const options = ref([]);
|
const options = ref([]);
|
||||||
|
const visible = ref(false);
|
||||||
const statusMap = new Map();
|
const current = ref({});
|
||||||
statusMap.set('enabled', 'success');
|
|
||||||
statusMap.set('disabled', 'error');
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
|
@ -214,17 +214,6 @@ const getActions = (
|
||||||
const state = data.state.value;
|
const state = data.state.value;
|
||||||
const stateText = state === 'enabled' ? '禁用' : '启用';
|
const stateText = state === 'enabled' ? '禁用' : '启用';
|
||||||
const actions = [
|
const actions = [
|
||||||
// {
|
|
||||||
// key: 'view',
|
|
||||||
// text: '查看',
|
|
||||||
// tooltip: {
|
|
||||||
// title: '查看',
|
|
||||||
// },
|
|
||||||
// icon: 'EyeOutlined',
|
|
||||||
// onClick: async () => {
|
|
||||||
// handlEye(data.id);
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
key: 'update',
|
key: 'update',
|
||||||
text: '编辑',
|
text: '编辑',
|
||||||
|
@ -233,7 +222,7 @@ const getActions = (
|
||||||
},
|
},
|
||||||
icon: 'EditOutlined',
|
icon: 'EditOutlined',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
handlEdit(data.id);
|
handlEdit(data);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -246,16 +235,11 @@ const getActions = (
|
||||||
popConfirm: {
|
popConfirm: {
|
||||||
title: `确认${stateText}?`,
|
title: `确认${stateText}?`,
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
// let res =
|
const res = await update(data.id, updateStatus[state]);
|
||||||
// state === 'enabled'
|
if (res.success) {
|
||||||
// ? await shutdown(data.id)
|
message.success('操作成功');
|
||||||
// : await start(data.id);
|
tableRef.value?.reload();
|
||||||
// if (res.success) {
|
}
|
||||||
// message.success('操作成功');
|
|
||||||
// tableRef.value?.reload();
|
|
||||||
// } else {
|
|
||||||
// message.error('操作失败!');
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -270,13 +254,11 @@ const getActions = (
|
||||||
popConfirm: {
|
popConfirm: {
|
||||||
title: '确认删除?',
|
title: '确认删除?',
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
// const res = await remove(data.id);
|
const res = await remove(data.id);
|
||||||
// if (res.success) {
|
if (res.success) {
|
||||||
// message.success('操作成功');
|
message.success('操作成功');
|
||||||
// tableRef.value.reload();
|
tableRef.value.reload();
|
||||||
// } else {
|
}
|
||||||
// message.error('操作失败!');
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
icon: 'DeleteOutlined',
|
icon: 'DeleteOutlined',
|
||||||
|
@ -286,15 +268,38 @@ const getActions = (
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlAdd = () => {
|
const handlAdd = () => {
|
||||||
// menuStory.jumpPage(`link/Type/Detail`, { id: ':id' }, { view: false });
|
current.value = {};
|
||||||
|
visible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlEdit = (data: object) => {
|
||||||
|
current.value = _.cloneDeep(data);
|
||||||
|
visible.value = true;
|
||||||
|
};
|
||||||
const handlEye = (id: string) => {
|
const handlEye = (id: string) => {
|
||||||
// menuStory.jumpPage(`link/Type/Detail`, { id }, { view: true });
|
console.log(id);
|
||||||
};
|
};
|
||||||
|
const saveChange = (value: object) => {
|
||||||
const handlEdit = (id: string) => {
|
visible.value = false;
|
||||||
// menuStory.jumpPage(`link/Type/Detail`, { id }, { view: false });
|
current.value = {};
|
||||||
|
if (value) {
|
||||||
|
message.success('操作成功');
|
||||||
|
tableRef.value.reload();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const getState = (record: Partial<Record<string, any>>) => {
|
||||||
|
if (record) {
|
||||||
|
if (record?.state?.value === 'enabled') {
|
||||||
|
return { ...record?.runningState };
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
text: '禁用',
|
||||||
|
value: 'disabled',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,38 +1,19 @@
|
||||||
export interface ConfigurationType {
|
export interface ConfigurationType {
|
||||||
parserType: string | undefined;
|
|
||||||
port: string | undefined;
|
port: string | undefined;
|
||||||
host: string | undefined;;
|
host: string | undefined;;
|
||||||
publicPort: string;
|
|
||||||
publicHost: string;
|
|
||||||
remoteHost: string;
|
|
||||||
remotePort: string;
|
|
||||||
secure: boolean;
|
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
topicPrefix: string;
|
endpoint: string,
|
||||||
maxMessageSize: string | number;
|
securityPolicy: string | undefined,
|
||||||
certId: string | undefined;
|
securityMode: string | undefined,
|
||||||
privateKeyAlias: string;
|
certificate: string | undefined,
|
||||||
clientId: string;
|
authType: string | undefined,
|
||||||
parserConfiguration: {
|
|
||||||
delimited: string;
|
|
||||||
lang: string;
|
|
||||||
script: string;
|
|
||||||
size: string;
|
|
||||||
length: string;
|
|
||||||
offset: string;
|
|
||||||
little: string | boolean | undefined;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormDataType {
|
export interface FormDataType {
|
||||||
name: string;
|
name: string;
|
||||||
type: string;
|
provider: string | undefined,
|
||||||
shareCluster: boolean;
|
|
||||||
description: string;
|
|
||||||
}
|
|
||||||
export interface FormData2Type {
|
|
||||||
id?: number | string;
|
|
||||||
serverId?: string | undefined;
|
|
||||||
configuration: ConfigurationType;
|
configuration: ConfigurationType;
|
||||||
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue