feat: 新增Bacnet数采;新增通道选择协议BacNet
* feat: 场景联动数组条件 * fix: bug#23054、22282 * fix: bug#23085 * feat: 新增Bacnet数采 * fix: bug#23160、23158 * fix: 网管子设备边缘端映射传值 * feat: 新增通道选择协议BacNet * fix: 系统管理背景图上传bug * fix: 校验条件设置为常量
This commit is contained in:
parent
e40af1855b
commit
f1aeeaed43
|
@ -68,4 +68,20 @@ export const getSnapTypes = () => server.get('/s7/client/s7codecs/list')
|
||||||
|
|
||||||
export const getArea = () => server.get('/s7/client/s7area/list')
|
export const getArea = () => server.get('/s7/client/s7area/list')
|
||||||
|
|
||||||
export const exportTemplate = (provider: string, format: string) =>server.get(`/data-collect/point/${provider}/template.${format}`, {}, {responseType: 'blob'})
|
export const exportTemplate = (provider: string, format: string) =>server.get(`/data-collect/point/${provider}/template.${format}`, {}, {responseType: 'blob'})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BACNet协议扫描对象
|
||||||
|
* @param channelId 通道id
|
||||||
|
* @param instanceNumber 设备实例号
|
||||||
|
*/
|
||||||
|
export const getBacnetObjectList = (channelId: string, instanceNumber: string) => server.get(`/collect/bacnet/${channelId}/${instanceNumber}/objects`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询未使用的属性id
|
||||||
|
* @param data 采集器Id
|
||||||
|
*/
|
||||||
|
export const getBacnetPropertyIdNotUse = (data: any) => server.post(`/collect/bacnet/${data.collectorId}/unused/ids`, data)
|
||||||
|
|
||||||
|
/**查询bacnet值类型*/
|
||||||
|
export const getBacnetValueType = () => server.get(`/collect/bacnet/value/types`)
|
|
@ -96,6 +96,7 @@ const handleRadio = (item: any) => {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
gap: 24px;
|
||||||
.disabled {
|
.disabled {
|
||||||
>div {
|
>div {
|
||||||
color: rgba(0, 0, 0, 0.25);
|
color: rgba(0, 0, 0, 0.25);
|
||||||
|
@ -105,7 +106,7 @@ const handleRadio = (item: any) => {
|
||||||
|
|
||||||
}
|
}
|
||||||
&-item {
|
&-item {
|
||||||
width: 49%;
|
flex:1;
|
||||||
height: 70px;
|
height: 70px;
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
@ -113,7 +114,7 @@ const handleRadio = (item: any) => {
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 24px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
.img {
|
.img {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
|
|
|
@ -40,7 +40,7 @@ const defaultOwnParams = [
|
||||||
column: "options"
|
column: "options"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
type:'or'
|
type:'and'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,5 +58,6 @@ export const protocolList = [
|
||||||
{ label: 'MODBUS_TCP', value: 'MODBUS_TCP', alias: 'Modbus/TCP' },
|
{ label: 'MODBUS_TCP', value: 'MODBUS_TCP', alias: 'Modbus/TCP' },
|
||||||
{ label: 'COLLECTOR_GATEWAY', value: 'COLLECTOR_GATEWAY', alias: 'GATEWAY' },
|
{ label: 'COLLECTOR_GATEWAY', value: 'COLLECTOR_GATEWAY', alias: 'GATEWAY' },
|
||||||
{ label: 'S7', value: 'snap7', alias: 'snap7' },
|
{ label: 'S7', value: 'snap7', alias: 'snap7' },
|
||||||
{ label: 'IEC104', value: 'iec104', alias: 'IEC104' }
|
{ label: 'IEC104', value: 'iec104', alias: 'IEC104' },
|
||||||
|
{ label: 'BACNetIp', value: 'BACNetIp', alias: 'BACNet/IP' }
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<template lang="">
|
<template>
|
||||||
<j-modal
|
<j-modal
|
||||||
:title="data.id ? '编辑' : '新增'"
|
:title="data.id ? '编辑' : '新增'"
|
||||||
:visible="true"
|
:visible="true"
|
||||||
|
@ -94,14 +94,14 @@
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
v-if="formData.provider === 'COLLECTOR_GATEWAY'"
|
v-if="formData.provider === 'COLLECTOR_GATEWAY'"
|
||||||
:name="['configuration','deviceId']"
|
:name="['configuration', 'deviceId']"
|
||||||
:rules="[{ required: true, message: '请选择网关设备'}]"
|
:rules="[{ required: true, message: '请选择网关设备' }]"
|
||||||
label="选择网关设备"
|
label="选择网关设备"
|
||||||
>
|
>
|
||||||
<GateWayFormItem
|
<GateWayFormItem
|
||||||
v-model:name="formData.configuration.deviceName"
|
v-model:name="formData.configuration.deviceName"
|
||||||
v-model:value="formData.configuration.deviceId"
|
v-model:value="formData.configuration.deviceId"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
v-if="formData.provider === 'OPC_UA'"
|
v-if="formData.provider === 'OPC_UA'"
|
||||||
|
@ -171,6 +171,50 @@
|
||||||
v-model:value="formData.configuration.password"
|
v-model:value="formData.configuration.password"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
|
<template v-if="formData.provider === 'BACNetIp'">
|
||||||
|
<j-form-item label="BACNet实例号" :name="['configuration', 'instanceNumber']" :rules="{
|
||||||
|
required: true,
|
||||||
|
trigger:'blur',
|
||||||
|
validator: validate,
|
||||||
|
}">
|
||||||
|
<j-input-number
|
||||||
|
style="width: 100%"
|
||||||
|
v-model:value="formData.configuration.instanceNumber"
|
||||||
|
:min="0"
|
||||||
|
:precision="0"
|
||||||
|
:max="999999999999"
|
||||||
|
placeholder="请输入BACNet实例号"
|
||||||
|
></j-input-number>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item
|
||||||
|
label="网卡"
|
||||||
|
:name="['configuration', 'overIp', 'localBindAddress']"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
trigger:'blur',
|
||||||
|
message: '请选择网卡'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
v-model:value="formData.configuration.overIp.localBindAddress"
|
||||||
|
>
|
||||||
|
</j-input>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="广播端口" :name="['configuration', 'overIp', 'port']" :rules="{
|
||||||
|
required: true,
|
||||||
|
trigger: 'blur',
|
||||||
|
message: '请输入广播端口'
|
||||||
|
}">
|
||||||
|
<j-input-number
|
||||||
|
v-model:value="formData.configuration.overIp.port"
|
||||||
|
style="width: 100%"
|
||||||
|
:min="0"
|
||||||
|
:max="65535"
|
||||||
|
:precision="0"
|
||||||
|
placeholder="请输入广播端口"
|
||||||
|
></j-input-number>
|
||||||
|
</j-form-item>
|
||||||
|
</template>
|
||||||
<!-- <j-form-item
|
<!-- <j-form-item
|
||||||
v-if="formData.provider === 'snap7'"
|
v-if="formData.provider === 'snap7'"
|
||||||
:name="['configuration', 'connect']"
|
:name="['configuration', 'connect']"
|
||||||
|
@ -215,7 +259,7 @@ import type { FormInstance } from 'ant-design-vue';
|
||||||
import type { FormDataType } from '../type.d';
|
import type { FormDataType } from '../type.d';
|
||||||
import { cloneDeep, isArray } from 'lodash-es';
|
import { cloneDeep, isArray } from 'lodash-es';
|
||||||
import { protocolList } from '@/utils/consts';
|
import { protocolList } from '@/utils/consts';
|
||||||
import GateWayFormItem from "@/views/DataCollect/Channel/Save/GateWayFormItem.vue";
|
import GateWayFormItem from '@/views/DataCollect/Channel/Save/GateWayFormItem.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
|
@ -242,20 +286,20 @@ const formData = ref<FormDataType>(cloneDeep(FormState));
|
||||||
const handleOk = async () => {
|
const handleOk = async () => {
|
||||||
const params: any = await formRef.value?.validate();
|
const params: any = await formRef.value?.validate();
|
||||||
if (params?.provider === 'COLLECTOR_GATEWAY') {
|
if (params?.provider === 'COLLECTOR_GATEWAY') {
|
||||||
params.configuration.deviceName = formData.value.configuration.deviceName
|
params.configuration.deviceName =
|
||||||
|
formData.value.configuration.deviceName;
|
||||||
}
|
}
|
||||||
|
if (params?.provider === 'snap7') {
|
||||||
if(params?.provider === 'snap7'){
|
params.configuration = {
|
||||||
params.configuration={
|
connect: false,
|
||||||
connect : false
|
};
|
||||||
}
|
|
||||||
} else if (params?.provider === 'iec104') {
|
} else if (params?.provider === 'iec104') {
|
||||||
params.configuration = {}
|
params.configuration = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
params.circuitBreaker = {
|
params.circuitBreaker = {
|
||||||
type: 'Ignore'
|
type: 'Ignore',
|
||||||
}
|
};
|
||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const response = !id
|
const response = !id
|
||||||
|
@ -266,6 +310,18 @@ const handleOk = async () => {
|
||||||
formRef.value?.resetFields();
|
formRef.value?.resetFields();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const validate = async (_rule: any, value: string) => {
|
||||||
|
if (!value) {
|
||||||
|
return Promise.reject('请输入BACnet实例号');
|
||||||
|
} else {
|
||||||
|
const reg = new RegExp(/^[0-9]*$/)
|
||||||
|
if(!reg.test(value) || parseInt(value) < 0) {
|
||||||
|
return Promise.reject('请输入正确的BACnet实例号');
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
emit('change', false);
|
emit('change', false);
|
||||||
formRef.value?.resetFields();
|
formRef.value?.resetFields();
|
||||||
|
@ -317,8 +373,15 @@ const getProvidersList = async () => {
|
||||||
const resp: any = await getProviders();
|
const resp: any = await getProviders();
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
const arr = resp.result
|
const arr = resp.result
|
||||||
.filter(
|
.filter((item: any) =>
|
||||||
(item: any) => ['GATEWAY', 'Modbus/TCP', 'opc-ua','snap7', 'IEC104'].includes(item.name),
|
[
|
||||||
|
'GATEWAY',
|
||||||
|
'Modbus/TCP',
|
||||||
|
'opc-ua',
|
||||||
|
'snap7',
|
||||||
|
'IEC104',
|
||||||
|
'BACNet/IP',
|
||||||
|
].includes(item.name),
|
||||||
)
|
)
|
||||||
.map((it: any) => it.name);
|
.map((it: any) => it.name);
|
||||||
const providers: any = protocolList.filter((item: any) =>
|
const providers: any = protocolList.filter((item: any) =>
|
||||||
|
@ -337,7 +400,7 @@ watch(
|
||||||
() => props.data,
|
() => props.data,
|
||||||
(value) => {
|
(value) => {
|
||||||
if (value.id) {
|
if (value.id) {
|
||||||
formData.value = value as FormDataType;
|
formData.value = value as FormDataType;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true, deep: true },
|
{ immediate: true, deep: true },
|
||||||
|
|
|
@ -18,6 +18,11 @@ export const FormState: FormDataType = {
|
||||||
deviceId: undefined,
|
deviceId: undefined,
|
||||||
deviceName: undefined,
|
deviceName: undefined,
|
||||||
connect:false,
|
connect:false,
|
||||||
|
instanceNumber: undefined,
|
||||||
|
overIp: {
|
||||||
|
localBindAddress: '0.0.0.0',
|
||||||
|
port: 47808
|
||||||
|
}
|
||||||
},
|
},
|
||||||
description: '',
|
description: '',
|
||||||
};
|
};
|
||||||
|
|
|
@ -195,7 +195,7 @@ const columns = [
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
const arr = resp.result
|
const arr = resp.result
|
||||||
.filter(
|
.filter(
|
||||||
(item: any) => ['GATEWAY', 'Modbus/TCP', 'opc-ua'].includes(item.name),
|
(item: any) => ['GATEWAY', 'Modbus/TCP', 'opc-ua','snap7', 'IEC104','BACNetIp'].includes(item.name),
|
||||||
)
|
)
|
||||||
.map((it: any) => it.name);
|
.map((it: any) => it.name);
|
||||||
const providers: any = protocolList.filter((item: any) =>
|
const providers: any = protocolList.filter((item: any) =>
|
||||||
|
|
|
@ -11,6 +11,11 @@ export interface ConfigurationType {
|
||||||
deviceId: string | undefined,
|
deviceId: string | undefined,
|
||||||
deviceName: string | undefined,
|
deviceName: string | undefined,
|
||||||
connect:boolean | undefined
|
connect:boolean | undefined
|
||||||
|
instanceNumber?:number
|
||||||
|
overIp: {
|
||||||
|
localBindAddress: string,
|
||||||
|
port: number
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormDataType {
|
export interface FormDataType {
|
||||||
|
|
|
@ -0,0 +1,301 @@
|
||||||
|
<template>
|
||||||
|
<j-modal :title="data.id ? '编辑' : '新增'" visible @cancel="handleCancel">
|
||||||
|
<j-form :model="formData" layout="vertical" ref="formRef">
|
||||||
|
<j-form-item label="点位名称" name="name" :rules="rules.name">
|
||||||
|
<j-input
|
||||||
|
placeholder="请输入点位名称"
|
||||||
|
v-model:value="formData.name"
|
||||||
|
:maxlength="64"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item
|
||||||
|
label="对象ID"
|
||||||
|
:name="['configuration', 'ObjectId']"
|
||||||
|
>
|
||||||
|
<j-card>
|
||||||
|
<j-form-item label="对象类型">
|
||||||
|
<j-input
|
||||||
|
v-model:value="formData.configuration.objectId.type"
|
||||||
|
disabled
|
||||||
|
></j-input>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="对象实例号">
|
||||||
|
<j-input
|
||||||
|
v-model:value="
|
||||||
|
formData.configuration.objectId.instanceNumber
|
||||||
|
"
|
||||||
|
disabled
|
||||||
|
></j-input>
|
||||||
|
</j-form-item>
|
||||||
|
</j-card>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item
|
||||||
|
label="属性ID"
|
||||||
|
:name="['configuration', 'propertyId']"
|
||||||
|
:rules="rules.configuration.pointAddress"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
v-model:value="formData.configuration.propertyId"
|
||||||
|
disabled
|
||||||
|
></j-input>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="值类型" :name="['configuration', 'valueType']">
|
||||||
|
<j-select
|
||||||
|
v-model:value="formData.configuration.valueType"
|
||||||
|
>
|
||||||
|
<j-select-option v-for="item in bacnetValueType" :key="item" :value="item">{{ item }}</j-select-option>
|
||||||
|
</j-select>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item
|
||||||
|
label="访问类型"
|
||||||
|
name="accessModes"
|
||||||
|
:rules="rules.accessModes"
|
||||||
|
>
|
||||||
|
<j-card-select
|
||||||
|
multiple
|
||||||
|
:showImage="false"
|
||||||
|
v-model:value="formData.accessModes"
|
||||||
|
:options="[
|
||||||
|
{ label: '读', value: 'read' },
|
||||||
|
{ label: '写', value: 'write' },
|
||||||
|
{ label: '订阅', value: 'subscribe' },
|
||||||
|
]"
|
||||||
|
:column="3"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item
|
||||||
|
:name="['configuration', 'terms']"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
validator: Area,
|
||||||
|
trigger: 'change',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<j-space>
|
||||||
|
<span>点位死区</span
|
||||||
|
><span class="explain"
|
||||||
|
>点位死区范围内的异常数据将被过滤(请勿配置非数值类型)</span
|
||||||
|
>
|
||||||
|
</j-space>
|
||||||
|
</template>
|
||||||
|
<DeathArea v-model:value="formData.configuration.terms" />
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item
|
||||||
|
label="轮询任务"
|
||||||
|
:name="['configuration', 'interval']"
|
||||||
|
:rules="rules.configuration.interval"
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
采集频率<span
|
||||||
|
style="
|
||||||
|
margin-left: 5px;
|
||||||
|
color: #9d9ea1;
|
||||||
|
font-size: 12px;
|
||||||
|
"
|
||||||
|
>采集频率为0时不执行轮询任务</span
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<j-input-number
|
||||||
|
style="width: 100%"
|
||||||
|
placeholder="请输入采集频率"
|
||||||
|
v-model:value="formData.configuration.interval"
|
||||||
|
addon-after="ms"
|
||||||
|
:max="2147483648"
|
||||||
|
:min="0"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item name="features">
|
||||||
|
<j-checkbox-group v-model:value="formData.features">
|
||||||
|
<j-checkbox value="changedOnly"
|
||||||
|
>只推送变化的数据</j-checkbox
|
||||||
|
>
|
||||||
|
</j-checkbox-group>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="说明" name="description">
|
||||||
|
<j-textarea
|
||||||
|
placeholder="请输入说明"
|
||||||
|
v-model:value="formData.description"
|
||||||
|
:maxlength="200"
|
||||||
|
:rows="3"
|
||||||
|
showCount
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
</j-form>
|
||||||
|
<template #footer>
|
||||||
|
<j-button @click="handleCancel">取消</j-button>
|
||||||
|
<PermissionButton
|
||||||
|
key="submit"
|
||||||
|
type="primary"
|
||||||
|
:loading="loading"
|
||||||
|
@click="handleOk"
|
||||||
|
style="margin-left: 8px"
|
||||||
|
:hasPermission="`DataCollect/Collector:${
|
||||||
|
data.id ? 'update' : 'add'
|
||||||
|
}`"
|
||||||
|
>
|
||||||
|
确认
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { savePoint, updatePoint, getBacnetValueType } from '@/api/data-collect/collector';
|
||||||
|
import { randomString } from '@/utils/utils';
|
||||||
|
import DeathArea from './DeathArea.vue';
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['change']);
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const formRef = ref();
|
||||||
|
const formData = ref({
|
||||||
|
name: props.data.name,
|
||||||
|
configuration: props.data.configuration || {
|
||||||
|
valueType: undefined,
|
||||||
|
pointAddress: '',
|
||||||
|
interval: 3000,
|
||||||
|
},
|
||||||
|
accessModes: [],
|
||||||
|
features: [],
|
||||||
|
description: props.data.description || '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
name: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入名称',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
configuration: {
|
||||||
|
typeIdentifierName: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入类型标识',
|
||||||
|
trigger: 'change',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointAddress: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入地址',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
interval: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入采集频率',
|
||||||
|
trigger: 'change',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
accessModes: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择访问类型',
|
||||||
|
trigger: 'change',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const Area = (_: any, value: any): Promise<any> =>
|
||||||
|
new Promise(async (resolve, reject) => {
|
||||||
|
if (!value) {
|
||||||
|
return resolve('');
|
||||||
|
}
|
||||||
|
if (value?.length === 0) {
|
||||||
|
return resolve('');
|
||||||
|
} else if (value?.length === 1) {
|
||||||
|
return value[0].value && value[0].termType
|
||||||
|
? resolve('')
|
||||||
|
: reject('请配置点位死区');
|
||||||
|
} else {
|
||||||
|
if (value?.[0].column === 'currentValue') {
|
||||||
|
// value.forEach((item:any) => {
|
||||||
|
// if(item.termType && item.value){
|
||||||
|
// return resolve('')
|
||||||
|
// }else{
|
||||||
|
// return reject('请配置点位死区')
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
const pass = value.every(
|
||||||
|
(item: any) => item.termType && item.value,
|
||||||
|
);
|
||||||
|
return pass ? resolve('') : reject('请配置点位死区');
|
||||||
|
} else {
|
||||||
|
value.forEach((item: any) => {
|
||||||
|
if (
|
||||||
|
item.column ===
|
||||||
|
`this['currentValue'] - this['lastValue']*init/100`
|
||||||
|
) {
|
||||||
|
return reject('请配置点位死区');
|
||||||
|
} else {
|
||||||
|
return resolve('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const bacnetValueType = ref<string[]>([])
|
||||||
|
|
||||||
|
const getIdAndType = async () => {
|
||||||
|
// const res = await getBacnetPropertyId()
|
||||||
|
// if(res.success) {
|
||||||
|
// bacnetPropertyId.value = res.result
|
||||||
|
// }
|
||||||
|
const resp: any = await getBacnetValueType()
|
||||||
|
if(resp.success) {
|
||||||
|
bacnetValueType.value = resp.result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getIdAndType()
|
||||||
|
const handleOk = async () => {
|
||||||
|
const res: any = await formRef.value?.validate();
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
...res,
|
||||||
|
configuration: {
|
||||||
|
...res.configuration,
|
||||||
|
objectId: props.data.configuration?.objectId,
|
||||||
|
},
|
||||||
|
inheritBreaker: true,
|
||||||
|
pointKey: props.data.pointKey || randomString(9),
|
||||||
|
provider: props.data.provider,
|
||||||
|
collectorId: props.data.collectorId,
|
||||||
|
accessModes: res?.accessModes.filter((item: any) => item),
|
||||||
|
};
|
||||||
|
loading.value = true;
|
||||||
|
const response = !props.data.id
|
||||||
|
? await savePoint(params).catch(() => {})
|
||||||
|
: await updatePoint(props.data.id, { ...props.data, ...params }).catch(
|
||||||
|
() => {},
|
||||||
|
);
|
||||||
|
emit('change', response?.status === 200);
|
||||||
|
loading.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
emit('change', false);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
formData.value.features = props.data.features?.map(
|
||||||
|
(item: any) => item.value,
|
||||||
|
);
|
||||||
|
if (props.data.accessModes?.length !== 0) {
|
||||||
|
formData.value.accessModes = props.data.accessModes?.map(
|
||||||
|
(item: any) => item.value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -1,4 +1,4 @@
|
||||||
<template lang="">
|
<template>
|
||||||
<j-modal title="扫描" :visible="true" width="95%" @cancel="handleCancel">
|
<j-modal title="扫描" :visible="true" width="95%" @cancel="handleCancel">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<Tree
|
<Tree
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<j-select v-model:value="propertyId" show-search style="width: 80%" placeholder="请选择">
|
||||||
|
<j-select-option
|
||||||
|
v-for="item in propertyIdListInUse"
|
||||||
|
:key="item"
|
||||||
|
:value="item"
|
||||||
|
>{{ item }}</j-select-option
|
||||||
|
>
|
||||||
|
</j-select>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts" name="propertyId">
|
||||||
|
import { getBacnetPropertyIdNotUse } from '@/api/data-collect/collector';
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
collectorId: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
objectId: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
checkedList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
index: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
valueTypeList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const emit = defineEmits(['update:value']);
|
||||||
|
|
||||||
|
const propertyId = computed({
|
||||||
|
get: () => {
|
||||||
|
return props.value;
|
||||||
|
},
|
||||||
|
set: (val) => {
|
||||||
|
emit('update:value', val);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const propertyIdList = ref<Record<string, any>[]>([]);
|
||||||
|
const getBacnetProperty = async () => {
|
||||||
|
const params = {
|
||||||
|
collectorId: props.collectorId,
|
||||||
|
objectId: props.objectId,
|
||||||
|
};
|
||||||
|
const res: any = await getBacnetPropertyIdNotUse(params);
|
||||||
|
if (res.success) {
|
||||||
|
propertyIdList.value = res.result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getBacnetProperty();
|
||||||
|
|
||||||
|
const propertyIdListInUse = computed(() => {
|
||||||
|
const result = props.checkedList
|
||||||
|
.filter((item: any, index: number) => {
|
||||||
|
return (
|
||||||
|
item.nodeId ==
|
||||||
|
`${props.objectId.type}:${props.objectId.instanceNumber}` &&
|
||||||
|
props.index != index
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.map((item: any) => item.propertyId);
|
||||||
|
return propertyIdList.value.filter((item: any) => {
|
||||||
|
return !result.includes(item);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,394 @@
|
||||||
|
<template>
|
||||||
|
<j-form class="table" ref="formTableRef" :model="modelRef">
|
||||||
|
<j-table
|
||||||
|
v-if="modelRef.dataSource.length !== 0"
|
||||||
|
:dataSource="modelRef.dataSource"
|
||||||
|
:columns="BacnetFormTableColumns"
|
||||||
|
:scroll="{ y: 580 }"
|
||||||
|
:pagination="false"
|
||||||
|
>
|
||||||
|
<template #headerCell="{ column }">
|
||||||
|
<template
|
||||||
|
v-if="column.key === 'nodeId' || column.key === 'action'"
|
||||||
|
>
|
||||||
|
<span> {{ column.title }} </span>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span> {{ column.title }} </span>
|
||||||
|
<span style="margin-left: 5px; color: red">*</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template #bodyCell="{ column: { dataIndex }, record, index }">
|
||||||
|
<template v-if="dataIndex === 'name'">
|
||||||
|
<j-form-item
|
||||||
|
:name="['dataSource', index, 'name']"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: checkLength,
|
||||||
|
trigger: 'change',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
v-model:value="record[dataIndex]"
|
||||||
|
placeholder="请输入"
|
||||||
|
style="width: 80%"
|
||||||
|
allowClear
|
||||||
|
></j-input>
|
||||||
|
</j-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-if="dataIndex === 'type'">
|
||||||
|
<j-form-item>
|
||||||
|
{{ record.objectId?.type }}
|
||||||
|
</j-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-if="dataIndex === 'instanceNumber'">
|
||||||
|
<j-form-item>
|
||||||
|
{{ record.objectId?.instanceNumber }}
|
||||||
|
</j-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-if="dataIndex === 'propertyId'">
|
||||||
|
<j-form-item
|
||||||
|
:name="['dataSource', index, 'propertyId']"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请选择',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<PropertyId
|
||||||
|
v-model:value="record[dataIndex]"
|
||||||
|
:valueTypeList="valueTypeList"
|
||||||
|
:collectorId="collectorData.collectorId"
|
||||||
|
:objectId="record.objectId"
|
||||||
|
:checkedList="modelRef.dataSource"
|
||||||
|
:index="index"
|
||||||
|
></PropertyId>
|
||||||
|
</j-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-if="dataIndex === 'valueType'">
|
||||||
|
<j-form-item
|
||||||
|
:name="['dataSource', index, 'valueType']"
|
||||||
|
:rules="{
|
||||||
|
required: true,
|
||||||
|
message: '请选择',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<j-select
|
||||||
|
v-model:value="record[dataIndex]"
|
||||||
|
style="width: 80%"
|
||||||
|
placeholder="请选择"
|
||||||
|
>
|
||||||
|
<j-select-option
|
||||||
|
v-for="item in valueTypeList"
|
||||||
|
:key="item"
|
||||||
|
:value="item"
|
||||||
|
>{{ item }}</j-select-option
|
||||||
|
>
|
||||||
|
</j-select>
|
||||||
|
</j-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-if="dataIndex === 'accessModes'">
|
||||||
|
<j-form-item
|
||||||
|
class="form-item"
|
||||||
|
:name="['dataSource', index, 'accessModes', 'value']"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-select
|
||||||
|
style="width: 75%"
|
||||||
|
v-model:value="record[dataIndex].value"
|
||||||
|
placeholder="请选择"
|
||||||
|
allowClear
|
||||||
|
mode="multiple"
|
||||||
|
:filter-option="filterOption"
|
||||||
|
:options="[
|
||||||
|
{ label: '读', value: 'read' },
|
||||||
|
{ label: '写', value: 'write' },
|
||||||
|
{ label: '订阅', value: 'subscribe' },
|
||||||
|
]"
|
||||||
|
:disabled="index !== 0 && record[dataIndex].check"
|
||||||
|
@change="changeValue(index, dataIndex)"
|
||||||
|
>
|
||||||
|
</j-select>
|
||||||
|
<j-checkbox
|
||||||
|
style="margin-left: 5px"
|
||||||
|
v-if="index !== 0"
|
||||||
|
v-model:checked="record[dataIndex].check"
|
||||||
|
@click="changeCheckbox(index, dataIndex)"
|
||||||
|
>同上</j-checkbox
|
||||||
|
>
|
||||||
|
</j-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-if="dataIndex === 'interval'">
|
||||||
|
<j-form-item
|
||||||
|
class="form-item"
|
||||||
|
:name="[
|
||||||
|
'dataSource',
|
||||||
|
index,
|
||||||
|
'configuration',
|
||||||
|
'interval',
|
||||||
|
'value',
|
||||||
|
]"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: regOnlyNumber,
|
||||||
|
message: '请输入0或者正整数',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input-number
|
||||||
|
style="width: 60%"
|
||||||
|
v-model:value="
|
||||||
|
record.configuration[dataIndex].value
|
||||||
|
"
|
||||||
|
placeholder="请输入"
|
||||||
|
allowClear
|
||||||
|
addon-after="ms"
|
||||||
|
:max="2147483647"
|
||||||
|
:min="0"
|
||||||
|
:disabled="
|
||||||
|
index !== 0 &&
|
||||||
|
record.configuration[dataIndex].check
|
||||||
|
"
|
||||||
|
@blur="changeValue(index, dataIndex)"
|
||||||
|
></j-input-number>
|
||||||
|
<j-checkbox
|
||||||
|
style="margin-left: 5px; margin-top: 5px"
|
||||||
|
v-show="index !== 0"
|
||||||
|
v-model:checked="
|
||||||
|
record.configuration[dataIndex].check
|
||||||
|
"
|
||||||
|
@click="changeCheckbox(index, dataIndex)"
|
||||||
|
>同上</j-checkbox
|
||||||
|
>
|
||||||
|
</j-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-if="dataIndex === 'features'">
|
||||||
|
<j-form-item
|
||||||
|
class="form-item"
|
||||||
|
:name="['dataSource', index, 'features', 'value']"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-select
|
||||||
|
style="width: 40%"
|
||||||
|
v-model:value="record[dataIndex].value"
|
||||||
|
placeholder="请选择"
|
||||||
|
allowClear
|
||||||
|
:filter-option="filterOption"
|
||||||
|
:options="[
|
||||||
|
{
|
||||||
|
label: '是',
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '否',
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:disabled="index !== 0 && record[dataIndex].check"
|
||||||
|
@change="changeValue(index, dataIndex)"
|
||||||
|
>
|
||||||
|
</j-select>
|
||||||
|
|
||||||
|
<j-checkbox
|
||||||
|
style="margin-left: 5px"
|
||||||
|
v-show="index !== 0"
|
||||||
|
v-model:checked="record[dataIndex].check"
|
||||||
|
@click="changeCheckbox(index, dataIndex)"
|
||||||
|
>同上</j-checkbox
|
||||||
|
>
|
||||||
|
</j-form-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-if="dataIndex === 'action'">
|
||||||
|
<j-tooltip title="删除">
|
||||||
|
<j-popconfirm
|
||||||
|
title="确认删除"
|
||||||
|
@confirm="clickDelete(record.nodeId, index)"
|
||||||
|
>
|
||||||
|
<a style="color: red"
|
||||||
|
><AIcon type="DeleteOutlined"
|
||||||
|
/></a>
|
||||||
|
</j-popconfirm>
|
||||||
|
</j-tooltip>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</j-table>
|
||||||
|
<j-empty v-else style="margin-top: 10%" />
|
||||||
|
</j-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { getBacnetValueType } from '@/api/data-collect/collector';
|
||||||
|
import { BacnetFormTableColumns, regOnlyNumber } from '../../data';
|
||||||
|
import { Rule } from 'ant-design-vue/lib/form';
|
||||||
|
import PropertyId from './PropertyId.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
collectorData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const emits = defineEmits(['change']);
|
||||||
|
|
||||||
|
const formTableRef = ref();
|
||||||
|
const defaultType = ['accessModes', 'interval', 'features'];
|
||||||
|
const modelRef: any = reactive({
|
||||||
|
dataSource: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const checkLength = (_rule: Rule, value: string): Promise<any> =>
|
||||||
|
new Promise(async (resolve, reject) => {
|
||||||
|
if (value) {
|
||||||
|
return String(value).length > 64
|
||||||
|
? reject('最多可输入64个字符')
|
||||||
|
: resolve('');
|
||||||
|
} else {
|
||||||
|
resolve('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const filterOption = (input: string, option: any) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickDelete = (value: string, index: number) => {
|
||||||
|
emits('change', value, index);
|
||||||
|
|
||||||
|
// 删除时需要做同上操作 todo
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTargetData = (index: number, type: string) => {
|
||||||
|
const { dataSource } = modelRef;
|
||||||
|
const Interval = type === 'interval';
|
||||||
|
return !Interval
|
||||||
|
? dataSource[index][type]
|
||||||
|
: dataSource[index].configuration[type];
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeValue = (index: number, type: string) => {
|
||||||
|
const { dataSource } = modelRef;
|
||||||
|
const originData = getTargetData(index, type);
|
||||||
|
for (let i = index + 1; i < dataSource.length; i++) {
|
||||||
|
const targetType = getTargetData(i, type);
|
||||||
|
if (!targetType.check) return;
|
||||||
|
targetType.value = originData.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeCheckbox = async (index: number, type: string) => {
|
||||||
|
//Dom未更新完成,需要用 setTimeout 或者 await nextTick() 处理
|
||||||
|
setTimeout(() => {
|
||||||
|
let startIndex = 0;
|
||||||
|
const { dataSource } = modelRef;
|
||||||
|
const currentCheck = getTargetData(index, type).check;
|
||||||
|
if (!currentCheck) return;
|
||||||
|
for (let i = index; i >= 0; i--) {
|
||||||
|
const preDataCheck = getTargetData(i, type).check;
|
||||||
|
if (!preDataCheck) {
|
||||||
|
startIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const originData = getTargetData(startIndex, type);
|
||||||
|
for (let i = startIndex; i < dataSource.length - 1; i++) {
|
||||||
|
const targetType = getTargetData(i + 1, type);
|
||||||
|
if (!targetType.check) return;
|
||||||
|
targetType.value = originData.value;
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const valueTypeList = ref([]);
|
||||||
|
const getValueTypeData = async () => {
|
||||||
|
const res: any = await getBacnetValueType();
|
||||||
|
if (res.success) {
|
||||||
|
valueTypeList.value = res.result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getValueTypeData();
|
||||||
|
|
||||||
|
const validate = () => {
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
formTableRef.value
|
||||||
|
.validate()
|
||||||
|
.then(() => {
|
||||||
|
res(modelRef.dataSource);
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
rej(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
defineExpose({
|
||||||
|
validate,
|
||||||
|
});
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
(value, preValue) => {
|
||||||
|
modelRef.dataSource = value;
|
||||||
|
// 有新增时同上数据
|
||||||
|
const vlength = value.length,
|
||||||
|
plength = preValue.length;
|
||||||
|
if (plength !== 0 && plength < vlength) {
|
||||||
|
defaultType.forEach((type) => {
|
||||||
|
vlength === 2
|
||||||
|
? changeValue(0, type)
|
||||||
|
: changeCheckbox(vlength - 1, type);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.table {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 600px;
|
||||||
|
:deep(.ant-table-header) {
|
||||||
|
.ant-table-cell {
|
||||||
|
padding: 16px 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-table-tbody) {
|
||||||
|
.ant-table-cell {
|
||||||
|
padding: 24px 0 0 0;
|
||||||
|
}
|
||||||
|
.ant-table-cell-fix-right-first {
|
||||||
|
padding: 0 0 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.ant-pagination) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,244 @@
|
||||||
|
<template>
|
||||||
|
<div class="tree-content">
|
||||||
|
<div class="tree-header">
|
||||||
|
<div>数据源</div>
|
||||||
|
<!-- <j-checkbox v-model:checked="isSelected">隐藏已有节点</j-checkbox> -->
|
||||||
|
</div>
|
||||||
|
{{ unSelectKeys }}
|
||||||
|
<j-spin :spinning="spinning">
|
||||||
|
<j-tree
|
||||||
|
v-if="!!treeData"
|
||||||
|
:tree-data="treeData"
|
||||||
|
v-model:checkedKeys="checkedKeys"
|
||||||
|
:selectable="false"
|
||||||
|
@check="onCheck"
|
||||||
|
:height="600"
|
||||||
|
>
|
||||||
|
<template #title="{ data, name, key }">
|
||||||
|
<j-space>
|
||||||
|
<j-ellipsis>
|
||||||
|
<span
|
||||||
|
:class="[
|
||||||
|
selectKeys.includes(key)
|
||||||
|
? 'tree-selected'
|
||||||
|
: 'tree-title',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
{{ name }}
|
||||||
|
</span>
|
||||||
|
</j-ellipsis>
|
||||||
|
<j-button type="link" @click="onCheck(data)">
|
||||||
|
<AIcon type="ArrowRightOutlined"/>
|
||||||
|
</j-button>
|
||||||
|
</j-space>
|
||||||
|
</template>
|
||||||
|
</j-tree>
|
||||||
|
<j-empty v-else style="margin-top: 22%" />
|
||||||
|
</j-spin>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { TreeProps } from 'ant-design-vue';
|
||||||
|
import {
|
||||||
|
scanOpcUAList,
|
||||||
|
queryPointNoPaging,
|
||||||
|
getBacnetObjectList,
|
||||||
|
} from '@/api/data-collect/collector';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
unSelectKeys: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const emits = defineEmits(['change']);
|
||||||
|
|
||||||
|
const channelId = props.data?.channelId;
|
||||||
|
const instanceNumber = props.data?.configuration?.instanceNumber;
|
||||||
|
const checkedKeys = ref<string[]>([]);
|
||||||
|
const selectKeys = ref<string[]>([]);
|
||||||
|
const spinning = ref(false);
|
||||||
|
|
||||||
|
const isSelected = ref(false);
|
||||||
|
const treeData = ref<TreeProps['treeData']>();
|
||||||
|
const treeAllData = ref<TreeProps['treeData']>();
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
spinning.value = true
|
||||||
|
const resp: any = await getBacnetObjectList(channelId, instanceNumber).finally(() => spinning.value = false);
|
||||||
|
if (resp.status === 200) {
|
||||||
|
treeData.value = resp.result.map((item: any) => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
key: item.id,
|
||||||
|
title: item.name,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleData = (arr: any): any[] => {
|
||||||
|
const data = arr.filter((item: any) => {
|
||||||
|
return (
|
||||||
|
(isSelected && !selectKeys.value.includes(item.id)) || !isSelected
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return data.map((item: any) => {
|
||||||
|
if (item.children && item.children?.length) {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
children: handleData(item.children),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCheck = ( info: any) => {
|
||||||
|
const one: any = { ...info };
|
||||||
|
const list: any = [];
|
||||||
|
const last: any = list.length ? list[list.length - 1] : undefined;
|
||||||
|
if (list.map((i: any) => i?.id).includes(one.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const item = {
|
||||||
|
features: {
|
||||||
|
value: last
|
||||||
|
? last?.features?.value
|
||||||
|
: (one?.features || []).includes('changedOnly'),
|
||||||
|
check: true,
|
||||||
|
},
|
||||||
|
nodeId: `${one?.objectId.type}:${one?.objectId.instanceNumber}` || '',
|
||||||
|
objectId: one?.objectId || {},
|
||||||
|
name: one?.name || '',
|
||||||
|
propertyId: one?.propertyId || '',
|
||||||
|
valueType: one?.valueType || '',
|
||||||
|
accessModes: {
|
||||||
|
value: last ? last?.accessModes?.value : one?.accessModes || [],
|
||||||
|
check: true,
|
||||||
|
},
|
||||||
|
type: one?.type,
|
||||||
|
configuration: {
|
||||||
|
...one?.configuration,
|
||||||
|
interval: {
|
||||||
|
value: last
|
||||||
|
? last?.configuration?.interval?.value
|
||||||
|
: one?.configuration?.interval || 3000,
|
||||||
|
check: true,
|
||||||
|
},
|
||||||
|
nodeId: one?.id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
emits('change', item);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateTreeData = (list: any, key: string, children: any[]): any[] => {
|
||||||
|
const arr = list.map((node: any) => {
|
||||||
|
if (node.key === key) {
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
children,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (node?.children && node?.children?.length) {
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
children: updateTreeData(node.children, key, children),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
return arr;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPoint = async () => {
|
||||||
|
const res: any = await queryPointNoPaging({
|
||||||
|
paging: false,
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
column: 'collectorId',
|
||||||
|
value: props.data?.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (res.status === 200) {
|
||||||
|
selectKeys.value = res.result.map((item: any) => item.pointKey);
|
||||||
|
}
|
||||||
|
// getScanOpcUAList();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getPoint();
|
||||||
|
onLoadData();
|
||||||
|
});
|
||||||
|
|
||||||
|
const getScanOpcUAList = async () => {
|
||||||
|
spinning.value = true;
|
||||||
|
const res: any = await getBacnetObjectList(channelId, instanceNumber);
|
||||||
|
treeAllData.value = res.result.map((item: any) => ({
|
||||||
|
...item,
|
||||||
|
key: item.id,
|
||||||
|
title: item.name,
|
||||||
|
disabled: item?.folder || false,
|
||||||
|
}));
|
||||||
|
spinning.value = false;
|
||||||
|
};
|
||||||
|
// getScanOpcUAList();
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => isSelected.value,
|
||||||
|
(value) => {
|
||||||
|
treeData.value = value
|
||||||
|
? handleData(treeAllData.value)
|
||||||
|
: treeAllData.value;
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => treeAllData.value,
|
||||||
|
(value) => {
|
||||||
|
treeData.value = isSelected.value ? handleData(value) : value;
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.unSelectKeys,
|
||||||
|
(value) => {
|
||||||
|
checkedKeys.value = checkedKeys.value.filter((i) => i !== value);
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.tree-content {
|
||||||
|
padding: 16px;
|
||||||
|
padding-left: 0;
|
||||||
|
min-width: 180px;
|
||||||
|
.tree-header {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.tree-selected {
|
||||||
|
padding: 2px 5px;
|
||||||
|
background-color: #d6e4ff;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.tree-title {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,105 @@
|
||||||
|
<template>
|
||||||
|
<j-modal title="扫描" :visible="true" width="95%" @cancel="handleCancel">
|
||||||
|
<div class="content">
|
||||||
|
<Tree
|
||||||
|
:data="treeData"
|
||||||
|
class="tree"
|
||||||
|
@change="changeTree"
|
||||||
|
:unSelectKeys="unSelectKeys"
|
||||||
|
></Tree>
|
||||||
|
<Table
|
||||||
|
:data="tableData"
|
||||||
|
:collectorData="treeData"
|
||||||
|
class="table"
|
||||||
|
@change="changeTable"
|
||||||
|
ref="formTableRef"
|
||||||
|
></Table>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<j-button key="back" @click="handleCancel">取消</j-button>
|
||||||
|
<PermissionButton
|
||||||
|
key="submit"
|
||||||
|
type="primary"
|
||||||
|
:loading="loading"
|
||||||
|
@click="handleOk"
|
||||||
|
style="margin-left: 8px"
|
||||||
|
:hasPermission="`DataCollect/Collector:update`"
|
||||||
|
>
|
||||||
|
确认
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { FormInstance } from 'ant-design-vue';
|
||||||
|
import { savePointBatch } from '@/api/data-collect/collector';
|
||||||
|
|
||||||
|
import Table from './Table.vue';
|
||||||
|
import Tree from './Tree.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const treeData = ref(props.data);
|
||||||
|
|
||||||
|
const emit = defineEmits(['change']);
|
||||||
|
const loading = ref(false);
|
||||||
|
const formTableRef = ref<FormInstance>();
|
||||||
|
const tableData = ref<any[]>([]);
|
||||||
|
const tableDataMap = new Map();
|
||||||
|
const unSelectKeys = ref();
|
||||||
|
|
||||||
|
const handleOk = async () => {
|
||||||
|
const data: any = await formTableRef.value?.validate().catch(() => {});
|
||||||
|
if (!data) return;
|
||||||
|
const list: any = data.map((item: any) => {
|
||||||
|
return {
|
||||||
|
name: item.name,
|
||||||
|
provider: 'BACNetIp',
|
||||||
|
collectorId: props.data?.id,
|
||||||
|
collectorName: props.data?.name,
|
||||||
|
pointKey: item.id,
|
||||||
|
configuration: {
|
||||||
|
interval: item.configuration?.interval?.value,
|
||||||
|
valueType: item.valueType,
|
||||||
|
propertyId: item.propertyId,
|
||||||
|
objectId: item.objectId,
|
||||||
|
},
|
||||||
|
pointKey: `${item.objectId.type}:${item.objectId.instanceNumber}:${item.propertyId}`,
|
||||||
|
features: !item.features?.value ? [] : ['changedOnly'],
|
||||||
|
accessModes: item.accessModes?.value || [],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
loading.value = true;
|
||||||
|
const resp = await savePointBatch([...list]).catch(() => {});
|
||||||
|
emit('change', resp?.status === 200);
|
||||||
|
loading.value = false;
|
||||||
|
};
|
||||||
|
const handleCancel = () => {
|
||||||
|
emit('change', false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeTree = (row: any) => {
|
||||||
|
tableData.value.push(row)
|
||||||
|
};
|
||||||
|
const changeTable = (value: string, index: number) => {
|
||||||
|
tableData.value.splice(index, 1)
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
min-height: 600px;
|
||||||
|
.tree {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
.table {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -8,7 +8,7 @@
|
||||||
>
|
>
|
||||||
<div class="sizeText">
|
<div class="sizeText">
|
||||||
将批量修改
|
将批量修改
|
||||||
{{ data.length }} 条数据的访问类型、采集频率、只推送变化的数据
|
{{ data.length }} 条数据的【{{ labelName.join(',') }}】
|
||||||
</div>
|
</div>
|
||||||
<j-form
|
<j-form
|
||||||
class="form"
|
class="form"
|
||||||
|
@ -18,6 +18,15 @@
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
>
|
>
|
||||||
|
<j-form-item label="值类型" v-if="provider === 'BACNetIp'">
|
||||||
|
<j-select
|
||||||
|
v-model:value="formData.valueType"
|
||||||
|
allowClear
|
||||||
|
placeholder="请选择值类型"
|
||||||
|
>
|
||||||
|
<j-select-option v-for="item in bacnetValueType" :key="item" :value="item">{{ item }}</j-select-option>
|
||||||
|
</j-select>
|
||||||
|
</j-form-item>
|
||||||
<j-form-item label="访问类型" name="accessModes">
|
<j-form-item label="访问类型" name="accessModes">
|
||||||
<j-card-select
|
<j-card-select
|
||||||
multiple
|
multiple
|
||||||
|
@ -86,7 +95,7 @@
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { FormInstance } from 'ant-design-vue';
|
import type { FormInstance } from 'ant-design-vue';
|
||||||
import { savePointBatch } from '@/api/data-collect/collector';
|
import { savePointBatch, getBacnetValueType } from '@/api/data-collect/collector';
|
||||||
import { cloneDeep, isObject } from 'lodash-es';
|
import { cloneDeep, isObject } from 'lodash-es';
|
||||||
import { regOnlyNumber } from '../../../data';
|
import { regOnlyNumber } from '../../../data';
|
||||||
|
|
||||||
|
@ -95,6 +104,10 @@ const props = defineProps({
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
|
provider: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['change']);
|
const emit = defineEmits(['change']);
|
||||||
|
@ -105,16 +118,28 @@ const formData = ref({
|
||||||
accessModes: [],
|
accessModes: [],
|
||||||
interval: undefined,
|
interval: undefined,
|
||||||
features: [],
|
features: [],
|
||||||
|
valueType: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const bacnetValueType = ref<string[]>([])
|
||||||
|
|
||||||
|
const getIdAndType = async () => {
|
||||||
|
const resp: any = await getBacnetValueType()
|
||||||
|
if(resp.success) {
|
||||||
|
bacnetValueType.value = resp.result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleOk = async () => {
|
const handleOk = async () => {
|
||||||
const data = cloneDeep(formData.value);
|
const data = cloneDeep(formData.value);
|
||||||
const { accessModes, features, interval } = data;
|
const { accessModes, features, interval, valueType } = data;
|
||||||
const ischange =
|
const ischange =
|
||||||
accessModes.length !== 0 ||
|
accessModes.length !== 0 ||
|
||||||
features.length !== 0 ||
|
features.length !== 0 ||
|
||||||
Number(interval) === 0 ||
|
Number(interval) === 0 ||
|
||||||
!!interval;
|
!!interval || !!valueType;
|
||||||
if (ischange) {
|
if (ischange) {
|
||||||
const params = cloneDeep(props.data);
|
const params = cloneDeep(props.data);
|
||||||
params.forEach((i: any) => {
|
params.forEach((i: any) => {
|
||||||
|
@ -139,6 +164,12 @@ const handleOk = async () => {
|
||||||
interval: data.interval,
|
interval: data.interval,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if(data.valueType) {
|
||||||
|
i.configuration = {
|
||||||
|
...i.configuration,
|
||||||
|
valueType: data.valueType
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const response = await savePointBatch(params).catch(() => {});
|
const response = await savePointBatch(params).catch(() => {});
|
||||||
|
@ -152,6 +183,32 @@ const handleOk = async () => {
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
emit('change', false);
|
emit('change', false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(() => props.provider, () => {
|
||||||
|
if(props.provider === 'BACNetIp') {
|
||||||
|
getIdAndType()
|
||||||
|
}
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
|
const labelName = computed(() => {
|
||||||
|
const arr = [];
|
||||||
|
if (formData.value.accessModes.length) {
|
||||||
|
arr.push('访问类型');
|
||||||
|
}
|
||||||
|
if (!!formData.value.interval) {
|
||||||
|
arr.push('采集频率');
|
||||||
|
}
|
||||||
|
if (formData.value.features.length) {
|
||||||
|
arr.push('只推送变化的数据');
|
||||||
|
}
|
||||||
|
if (formData.value.type) {
|
||||||
|
arr.push('数据类型');
|
||||||
|
}
|
||||||
|
if (formData.value.valueType) {
|
||||||
|
arr.push('值类型');
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
批量导入
|
批量导入
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
v-if="data?.provider === 'OPC_UA'"
|
v-if="data?.provider === 'OPC_UA' || data?.provider === 'BACNetIp'"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handlScan"
|
@click="handlScan"
|
||||||
hasPermission="DataCollect/Collector:add"
|
hasPermission="DataCollect/Collector:add"
|
||||||
|
@ -53,8 +53,7 @@
|
||||||
扫描
|
扫描
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
<j-dropdown
|
<j-dropdown
|
||||||
v-if="data?.provider === 'OPC_UA'"
|
v-if="data?.provider === 'OPC_UA' || data?.provider === 'BACNetIp'"
|
||||||
:trigger="['click']"
|
|
||||||
>
|
>
|
||||||
<j-button @click.prevent="clickBatch"
|
<j-button @click.prevent="clickBatch"
|
||||||
>批量操作 <AIcon type="DownOutlined"
|
>批量操作 <AIcon type="DownOutlined"
|
||||||
|
@ -92,7 +91,7 @@
|
||||||
</j-dropdown>
|
</j-dropdown>
|
||||||
</j-space>
|
</j-space>
|
||||||
<div
|
<div
|
||||||
v-if="data?.provider === 'OPC_UA'"
|
v-if="data?.provider === 'OPC_UA' || data?.provider === 'BACNetIp'"
|
||||||
style="margin-top: 15px"
|
style="margin-top: 15px"
|
||||||
>
|
>
|
||||||
<j-checkbox
|
<j-checkbox
|
||||||
|
@ -321,11 +320,14 @@
|
||||||
<BatchUpdate
|
<BatchUpdate
|
||||||
v-if="visible.batchUpdate"
|
v-if="visible.batchUpdate"
|
||||||
:data="current"
|
:data="current"
|
||||||
|
:provider="data.provider"
|
||||||
@change="saveChange"
|
@change="saveChange"
|
||||||
/>
|
/>
|
||||||
<SaveS7 v-if="visible.saveS7" :data="current" @change="saveChange"/>
|
<SaveS7 v-if="visible.saveS7" :data="current" @change="saveChange"/>
|
||||||
<SaveIEC104 v-if="visible.saveIEC104" :data="current" @change="saveChange"/>
|
<SaveIEC104 v-if="visible.saveIEC104" :data="current" @change="saveChange"/>
|
||||||
|
<SaveBACNet v-if="visible.saveBACNet" :data="current" @change="saveChange"/>
|
||||||
<Scan v-if="visible.scan" :data="current" @change="saveChange" />
|
<Scan v-if="visible.scan" :data="current" @change="saveChange" />
|
||||||
|
<ScanBacnet v-if="visible.scanBacnet" :data="current" @change="saveChange" />
|
||||||
<Import v-if="visible.import" :data="current" @close-import="closeImport"/>
|
<Import v-if="visible.import" :data="current" @close-import="closeImport"/>
|
||||||
</j-spin>
|
</j-spin>
|
||||||
</template>
|
</template>
|
||||||
|
@ -346,6 +348,8 @@ import BatchUpdate from './components/BatchUpdate/index.vue';
|
||||||
import SaveModBus from './Save/SaveModBus.vue';
|
import SaveModBus from './Save/SaveModBus.vue';
|
||||||
import SaveOPCUA from './Save/SaveOPCUA.vue';
|
import SaveOPCUA from './Save/SaveOPCUA.vue';
|
||||||
import Scan from './Scan/index.vue';
|
import Scan from './Scan/index.vue';
|
||||||
|
import ScanBacnet from './ScanBacnet/index.vue';
|
||||||
|
import SaveBACNet from './Save/SaveBACNet.vue';
|
||||||
import { colorMap } from '../data';
|
import { colorMap } from '../data';
|
||||||
import { cloneDeep, isBoolean, isNumber, throttle } from 'lodash-es';
|
import { cloneDeep, isBoolean, isNumber, throttle } from 'lodash-es';
|
||||||
import { getWebSocket } from '@/utils/websocket';
|
import { getWebSocket } from '@/utils/websocket';
|
||||||
|
@ -385,7 +389,9 @@ const visible = reactive({
|
||||||
scan: false,
|
scan: false,
|
||||||
saveS7:false,
|
saveS7:false,
|
||||||
import:false,
|
import:false,
|
||||||
saveIEC104: false
|
saveIEC104: false,
|
||||||
|
scanBacnet: false,
|
||||||
|
saveBACNet: false,
|
||||||
});
|
});
|
||||||
const current: any = ref({});
|
const current: any = ref({});
|
||||||
const accessModesOption = ref();
|
const accessModesOption = ref();
|
||||||
|
@ -527,12 +533,14 @@ const handlEdit = (data: any) => {
|
||||||
visible.saveS7 = true
|
visible.saveS7 = true
|
||||||
} else if(data?.provider === 'iec104') {
|
} else if(data?.provider === 'iec104') {
|
||||||
visible.saveIEC104 = true
|
visible.saveIEC104 = true
|
||||||
|
} else if (data?.provider === 'BACNetIp') {
|
||||||
|
visible.saveBACNet = true
|
||||||
} else {
|
} else {
|
||||||
visible.saveModBus = true;
|
visible.saveModBus = true;
|
||||||
}
|
}
|
||||||
current.value = cloneDeep({
|
current.value = cloneDeep({
|
||||||
...data,
|
...data,
|
||||||
deviceType:props.data?.configuration.type,
|
deviceType:props.data?.configuration?.type || props.data?.configuration?.valueType,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -563,7 +571,11 @@ const handlBatchUpdate = () => {
|
||||||
visible.batchUpdate = true;
|
visible.batchUpdate = true;
|
||||||
};
|
};
|
||||||
const handlScan = () => {
|
const handlScan = () => {
|
||||||
visible.scan = true;
|
if(props.data?.provider === 'OPC_UA'){
|
||||||
|
visible.scan = true;
|
||||||
|
} else if(props.data?.provider === 'BACNetIp'){
|
||||||
|
visible.scanBacnet = true
|
||||||
|
}
|
||||||
current.value = cloneDeep(props.data);
|
current.value = cloneDeep(props.data);
|
||||||
};
|
};
|
||||||
const handleImport = () =>{
|
const handleImport = () =>{
|
||||||
|
@ -657,7 +669,7 @@ const cancelSelect = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = (dt: any) => {
|
const handleClick = (dt: any) => {
|
||||||
if (props.data?.provider !== 'OPC_UA') return;
|
if (props.data?.provider !== 'OPC_UA' && props.data?.provider !== 'BACNetIp') return;
|
||||||
if (_selectedRowKeys.value.includes(dt.id)) {
|
if (_selectedRowKeys.value.includes(dt.id)) {
|
||||||
const _index = _selectedRowKeys.value.findIndex((i) => i === dt.id);
|
const _index = _selectedRowKeys.value.findIndex((i) => i === dt.id);
|
||||||
_selectedRowKeys.value.splice(_index, 1);
|
_selectedRowKeys.value.splice(_index, 1);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<template lang="">
|
<template>
|
||||||
<j-modal
|
<j-modal
|
||||||
:title="data.id ? '编辑' : '新增'"
|
:title="data.id ? '编辑' : '新增'"
|
||||||
:visible="true"
|
:visible="true"
|
||||||
|
@ -112,6 +112,32 @@
|
||||||
:max="255"
|
:max="255"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
|
<template v-if="provider === 'BACNetIp'">
|
||||||
|
<j-form-item
|
||||||
|
label="设备实例号"
|
||||||
|
:name="['configuration', 'instanceNumber']"
|
||||||
|
:rules="[{ required: true, trigger: 'change' }]"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
type="number"
|
||||||
|
style="width: 100%"
|
||||||
|
v-model:value="formData.configuration.instanceNumber"
|
||||||
|
placeholder="请输入设备实例号"
|
||||||
|
:maxlength="64"
|
||||||
|
:disabled="route.query.id ? true : false"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="地址" :name="['configuration', 'address']">
|
||||||
|
<j-input
|
||||||
|
style="width: 100%"
|
||||||
|
v-model:value="formData.configuration.address"
|
||||||
|
:maxlength="64"
|
||||||
|
type="tel"
|
||||||
|
placeholder="请输入地址"
|
||||||
|
>
|
||||||
|
</j-input>
|
||||||
|
</j-form-item>
|
||||||
|
</template>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
v-if="provider !== 'COLLECTOR_GATEWAY'"
|
v-if="provider !== 'COLLECTOR_GATEWAY'"
|
||||||
:name="['configuration', 'inheritBreakerSpec', 'type']"
|
:name="['configuration', 'inheritBreakerSpec', 'type']"
|
||||||
|
@ -222,6 +248,8 @@ import type { FormInstance } from 'ant-design-vue';
|
||||||
import {cloneDeep, omit} from "lodash-es";
|
import {cloneDeep, omit} from "lodash-es";
|
||||||
import {protocolList} from "@/utils/consts";
|
import {protocolList} from "@/utils/consts";
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const visibleEndian = ref(false);
|
const visibleEndian = ref(false);
|
||||||
const visibleUnitId = ref(false);
|
const visibleUnitId = ref(false);
|
||||||
|
|
|
@ -317,4 +317,59 @@ export const FormTableColumns = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const BacnetFormTableColumns = [
|
||||||
|
{
|
||||||
|
title: '名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
width: 140,
|
||||||
|
fixed: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '对象类型',
|
||||||
|
dataIndex: 'type',
|
||||||
|
key: 'type',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '对象号',
|
||||||
|
dataIndex: 'instanceNumber',
|
||||||
|
key: 'instanceNumber',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '属性ID',
|
||||||
|
dataIndex: 'propertyId',
|
||||||
|
key: 'propertyId',
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '值类型',
|
||||||
|
dataIndex: 'valueType',
|
||||||
|
key: 'valueType',
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '访问类型',
|
||||||
|
dataIndex: 'accessModes',
|
||||||
|
key: 'accessModes',
|
||||||
|
width: 260,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '采集频率',
|
||||||
|
key: 'interval',
|
||||||
|
dataIndex: 'interval',
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '只推送变化的数据',
|
||||||
|
key: 'features',
|
||||||
|
dataIndex: 'features',
|
||||||
|
width: 140,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
dataIndex: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 50,
|
||||||
|
},
|
||||||
|
]
|
|
@ -8,6 +8,7 @@
|
||||||
visible
|
visible
|
||||||
@ok="submitData"
|
@ok="submitData"
|
||||||
@cancel="close"
|
@cancel="close"
|
||||||
|
:confirmLoading="loading"
|
||||||
okText="确定"
|
okText="确定"
|
||||||
cancelText="取消"
|
cancelText="取消"
|
||||||
v-bind="layout"
|
v-bind="layout"
|
||||||
|
@ -86,6 +87,7 @@ const arr = ref([]);
|
||||||
const updateObj = ref({});
|
const updateObj = ref({});
|
||||||
const addObj = ref({});
|
const addObj = ref({});
|
||||||
const addParams = ref({});
|
const addParams = ref({});
|
||||||
|
const loading = ref(false)
|
||||||
/**
|
/**
|
||||||
* 表单数据
|
* 表单数据
|
||||||
*/
|
*/
|
||||||
|
@ -118,6 +120,7 @@ const { resetFields, validate, validateInfos } = useForm(
|
||||||
*/
|
*/
|
||||||
const submitData = async () => {
|
const submitData = async () => {
|
||||||
formRef.value.validate().then(async () => {
|
formRef.value.validate().then(async () => {
|
||||||
|
loading.value = true
|
||||||
addParams.value = {};
|
addParams.value = {};
|
||||||
if (props.isAdd === 0) {
|
if (props.isAdd === 0) {
|
||||||
if (props.isChild === 1) {
|
if (props.isChild === 1) {
|
||||||
|
@ -139,7 +142,9 @@ const submitData = async () => {
|
||||||
// sortIndex: arr.value[arr.value.length - 1].sortIndex + 1,
|
// sortIndex: arr.value[arr.value.length - 1].sortIndex + 1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const res = await saveTree(addParams.value);
|
const res = await saveTree(addParams.value).finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
onlyMessage('操作成功!');
|
onlyMessage('操作成功!');
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
|
@ -155,7 +160,9 @@ const submitData = async () => {
|
||||||
key: updateObj.value.key,
|
key: updateObj.value.key,
|
||||||
parentId: updateObj.value.parentId,
|
parentId: updateObj.value.parentId,
|
||||||
};
|
};
|
||||||
const res = await updateTree(id, updateParams);
|
const res = await updateTree(id, updateParams).finally(()=>{
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
onlyMessage('操作成功!');
|
onlyMessage('操作成功!');
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
|
|
|
@ -90,6 +90,7 @@ const title = ref('');
|
||||||
const isAdd = ref(0);
|
const isAdd = ref(0);
|
||||||
const isChild = ref(0);
|
const isChild = ref(0);
|
||||||
const tableLoading = ref(false);
|
const tableLoading = ref(false);
|
||||||
|
const addSortId = ref()
|
||||||
// 筛选
|
// 筛选
|
||||||
const query = reactive({
|
const query = reactive({
|
||||||
columns: [
|
columns: [
|
||||||
|
@ -182,6 +183,7 @@ const getActions = (
|
||||||
}
|
}
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
modifyRef.value.show(data);
|
modifyRef.value.show(data);
|
||||||
|
addSortId.value = data.id
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -257,6 +259,10 @@ const table = reactive({
|
||||||
* 刷新表格数据
|
* 刷新表格数据
|
||||||
*/
|
*/
|
||||||
refresh: () => {
|
refresh: () => {
|
||||||
|
if(isAdd.value === 0 && isChild.value !==3){
|
||||||
|
expandedRowKeys.value.push(addSortId.value);
|
||||||
|
}
|
||||||
|
console.log(expandedRowKeys.value)
|
||||||
tableRef.value.reload();
|
tableRef.value.reload();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,8 +23,12 @@
|
||||||
</template>
|
</template>
|
||||||
<template #bodyCell="{ column, record, index }">
|
<template #bodyCell="{ column, record, index }">
|
||||||
<template v-if="column.dataIndex === 'metadataName'">
|
<template v-if="column.dataIndex === 'metadataName'">
|
||||||
<span v-if="record.metadataName">{{ record.metadataName }}</span>
|
<span v-if="record.metadataName">{{
|
||||||
<span v-else style="color: red;">{{ record.metadataId }}</span>
|
record.metadataName
|
||||||
|
}}</span>
|
||||||
|
<span v-else style="color: red">{{
|
||||||
|
record.metadataId
|
||||||
|
}}</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="column.dataIndex === 'channelId'">
|
<template v-if="column.dataIndex === 'channelId'">
|
||||||
<j-form-item
|
<j-form-item
|
||||||
|
@ -96,11 +100,7 @@
|
||||||
status="success"
|
status="success"
|
||||||
text="启用"
|
text="启用"
|
||||||
/>
|
/>
|
||||||
<j-badge
|
<j-badge v-else status="warning" text="禁用" />
|
||||||
v-else
|
|
||||||
status="warning"
|
|
||||||
text="禁用"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<j-badge v-else status="error" text="未绑定" />
|
<j-badge v-else status="error" text="未绑定" />
|
||||||
</template>
|
</template>
|
||||||
|
@ -251,7 +251,7 @@ const columns = [
|
||||||
|
|
||||||
const permissionStore = usePermissionStore();
|
const permissionStore = usePermissionStore();
|
||||||
|
|
||||||
const data:any = ref([])
|
const data: any = ref([]);
|
||||||
const isPermission = permissionStore.hasPermission('device/Instance:update');
|
const isPermission = permissionStore.hasPermission('device/Instance:update');
|
||||||
|
|
||||||
const current = ref<number>(1);
|
const current = ref<number>(1);
|
||||||
|
@ -292,18 +292,17 @@ const getChannel = async () => {
|
||||||
|
|
||||||
const handleSearch = async () => {
|
const handleSearch = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const _metadataMap = new Map ()
|
const _metadataMap = new Map();
|
||||||
const _metadata: any[] = metadata.properties.map((item: any) => {
|
const _metadata: any[] = metadata.properties.map((item: any) => {
|
||||||
const value = {
|
const value = {
|
||||||
metadataId: item.id,
|
metadataId: item.id,
|
||||||
metadataName: `${item.name}(${item.id})`,
|
metadataName: `${item.name}(${item.id})`,
|
||||||
metadataType: 'property',
|
metadataType: 'property',
|
||||||
name: item.name,
|
name: item.name,
|
||||||
}
|
};
|
||||||
_metadataMap.set(item.id,value)
|
_metadataMap.set(item.id, value);
|
||||||
return value
|
return value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if (_metadata && _metadata.length) {
|
if (_metadata && _metadata.length) {
|
||||||
const resp: any = await getEdgeMap(
|
const resp: any = await getEdgeMap(
|
||||||
|
@ -330,12 +329,20 @@ const handleSearch = async () => {
|
||||||
// array.push(item)
|
// array.push(item)
|
||||||
// }
|
// }
|
||||||
// })
|
// })
|
||||||
|
|
||||||
resp.result?.[0].forEach((item:any)=>{
|
resp.result?.[0]?.forEach((item: any) => {
|
||||||
_metadataMap.has(item.metadataId) ? _metadataMap.set(item.metadataId,Object.assign(_metadataMap.get(item.metadataId),item)) : _metadataMap.set(item.metadataId,item)
|
_metadataMap.has(item.metadataId)
|
||||||
})
|
? _metadataMap.set(
|
||||||
data.value = [..._metadataMap.values()]
|
item.metadataId,
|
||||||
onPageChange()
|
Object.assign(
|
||||||
|
_metadataMap.get(item.metadataId),
|
||||||
|
item,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: _metadataMap.set(item.metadataId, item);
|
||||||
|
});
|
||||||
|
data.value = [..._metadataMap.values()];
|
||||||
|
onPageChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
@ -372,25 +379,21 @@ const onChannelChange = (_index: number, type: 'collector' | 'channel') => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getChannel();
|
|
||||||
handleSearch();
|
|
||||||
});
|
|
||||||
|
|
||||||
const onPageChange = () => {
|
const onPageChange = () => {
|
||||||
const _cur = current.value >= 1 ? current.value : 1;
|
formRef.value?.validate().then(() => {
|
||||||
const _pageSize = pageSize.value
|
const _cur = current.value >= 1 ? current.value : 1;
|
||||||
const array = data.value.slice((_cur - 1) * _pageSize, _cur * _pageSize) || [];
|
const _pageSize = pageSize.value;
|
||||||
modelRef.dataSource = array;
|
const array =
|
||||||
|
data.value.slice((_cur - 1) * _pageSize, _cur * _pageSize) || [];
|
||||||
|
modelRef.dataSource = array;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSave = () => {
|
const onSave = () => {
|
||||||
formRef.value
|
formRef.value
|
||||||
.validate()
|
.validate()
|
||||||
.then(async (_data: any) => {
|
.then(async (_data: any) => {
|
||||||
const arr = toRaw(modelRef).dataSource.filter(
|
const arr = toRaw(data.value).filter((i: any) => i.channelId);
|
||||||
(i: any) => i.channelId,
|
|
||||||
);
|
|
||||||
if (arr && arr.length !== 0) {
|
if (arr && arr.length !== 0) {
|
||||||
const submitData = {
|
const submitData = {
|
||||||
deviceId: instanceStore.current.id,
|
deviceId: instanceStore.current.id,
|
||||||
|
@ -469,6 +472,26 @@ const onRefresh = async () => {
|
||||||
}
|
}
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => modelRef.dataSource,
|
||||||
|
(val) => {
|
||||||
|
const dataMap = new Map();
|
||||||
|
val.forEach((item: any) => {
|
||||||
|
dataMap.set(item.metadataId, item);
|
||||||
|
});
|
||||||
|
data.value.forEach((item: any, index: number) => {
|
||||||
|
dataMap.has(item.metadataId) ? (data.value[index] = item) : '';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
onMounted(() => {
|
||||||
|
getChannel();
|
||||||
|
handleSearch();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
@ -482,4 +505,4 @@ const onRefresh = async () => {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -143,15 +143,15 @@ const instanceStore = useInstanceStore();
|
||||||
const visible = ref<boolean>(false);
|
const visible = ref<boolean>(false);
|
||||||
const config = ref<ConfigMetadata[]>([]);
|
const config = ref<ConfigMetadata[]>([]);
|
||||||
|
|
||||||
watchEffect(() => {
|
watch(()=>instanceStore.current.id,(val) => {
|
||||||
if (instanceStore.current.id) {
|
if (val) {
|
||||||
getConfigMetadata(instanceStore.current.id).then((resp) => {
|
getConfigMetadata(val).then((resp) => {
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
config.value = resp?.result as ConfigMetadata[];
|
config.value = resp?.result as ConfigMetadata[];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
},{ immediate: true });
|
||||||
|
|
||||||
const isExit = (property: string) => {
|
const isExit = (property: string) => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="device-instance-log"
|
target="device-instance-log"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
type="simple"
|
|
||||||
class="device-log-search"
|
class="device-log-search"
|
||||||
/>
|
/>
|
||||||
<JProTable
|
<JProTable
|
||||||
|
@ -13,7 +12,7 @@
|
||||||
model="TABLE"
|
model="TABLE"
|
||||||
:defaultParams="{ sorts: [{ name: 'timestamp', order: 'desc' }] }"
|
:defaultParams="{ sorts: [{ name: 'timestamp', order: 'desc' }] }"
|
||||||
:params="params"
|
:params="params"
|
||||||
:bodyStyle="{ padding: 0 }"
|
:bodyStyle="{ padding: 0 , minHeight: 'auto' }"
|
||||||
>
|
>
|
||||||
<template #type="slotProps">
|
<template #type="slotProps">
|
||||||
{{ slotProps?.type?.text }}
|
{{ slotProps?.type?.text }}
|
||||||
|
@ -144,7 +143,7 @@ const handleSearch = (_params: any) => {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less" scoped>
|
||||||
.device-log-search {
|
.device-log-search {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<pro-search
|
<pro-search
|
||||||
class="device-running-search"
|
class="device-running-search"
|
||||||
type="simple"
|
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="device-instance-running-events"
|
target="device-instance-running-events"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
</template>
|
</template>
|
||||||
</JProTable>
|
</JProTable>
|
||||||
<j-modal :width="600" v-model:visible="visible" title="详情" class="device-running-event-modal">
|
<j-modal :width="600" v-model:visible="visible" title="详情" class="device-running-event-modal">
|
||||||
<JsonViewer :value="info" />
|
<JsonViewer :value="info" style="max-height: calc(100vh - 400px);overflow: auto;"/>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<j-button type="primary" @click="visible = false">关闭</j-button>
|
<j-button type="primary" @click="visible = false">关闭</j-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -60,9 +60,9 @@ const instanceStore = useInstanceStore();
|
||||||
const options = ref({});
|
const options = ref({});
|
||||||
|
|
||||||
const _type = computed(() => {
|
const _type = computed(() => {
|
||||||
const flag = list.includes(prop.data?.valueType?.type || '')
|
const flag = list.includes(prop.data?.valueType?.type || '');
|
||||||
cycle.value = flag ? '*' : '1m'
|
cycle.value = flag ? '*' : '1m';
|
||||||
return flag
|
return flag;
|
||||||
});
|
});
|
||||||
|
|
||||||
const queryChartsAggList = async () => {
|
const queryChartsAggList = async () => {
|
||||||
|
@ -81,11 +81,9 @@ const queryChartsAggList = async () => {
|
||||||
from: prop.time[0],
|
from: prop.time[0],
|
||||||
to: prop.time[1],
|
to: prop.time[1],
|
||||||
},
|
},
|
||||||
}).finally(
|
}).finally(() => {
|
||||||
()=>{
|
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
});
|
||||||
)
|
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
const dataList: any[] = [
|
const dataList: any[] = [
|
||||||
{
|
{
|
||||||
|
@ -130,19 +128,11 @@ const queryChartsList = async () => {
|
||||||
],
|
],
|
||||||
sorts: [{ name: 'timestamp', order: 'asc' }],
|
sorts: [{ name: 'timestamp', order: 'asc' }],
|
||||||
},
|
},
|
||||||
).finally(
|
).finally(() => {
|
||||||
()=>{
|
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
});
|
||||||
)
|
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
const dataList: any[] = [
|
const dataList: any[] = [];
|
||||||
{
|
|
||||||
year: prop.time[0],
|
|
||||||
value: undefined,
|
|
||||||
type: prop.data?.name || '',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
(resp.result as any)?.forEach((i: any) => {
|
(resp.result as any)?.forEach((i: any) => {
|
||||||
dataList.push({
|
dataList.push({
|
||||||
...i,
|
...i,
|
||||||
|
@ -151,12 +141,27 @@ const queryChartsList = async () => {
|
||||||
type: prop.data?.name || '',
|
type: prop.data?.name || '',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
dataList.push({
|
const beginTimeExist = dataList.find((i: any) => {
|
||||||
year: prop.time[1],
|
return i.year === prop.time[0];
|
||||||
value: undefined,
|
|
||||||
type: prop.data?.name || '',
|
|
||||||
});
|
});
|
||||||
chartsList.value = dataList || [];
|
const endTimeExist = dataList.find((i: any) => {
|
||||||
|
return i.year === prop.time[1];
|
||||||
|
});
|
||||||
|
if (!beginTimeExist) {
|
||||||
|
dataList.unshift({
|
||||||
|
year: prop.time[0],
|
||||||
|
value: undefined,
|
||||||
|
type: prop.data?.name || '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!endTimeExist) {
|
||||||
|
dataList.push({
|
||||||
|
year: prop.time[1],
|
||||||
|
value: undefined,
|
||||||
|
type: prop.data?.name || '',
|
||||||
|
});
|
||||||
|
chartsList.value = dataList || [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -191,7 +196,7 @@ const getOptions = (arr: any[]) => {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
position: function (pt: any) {
|
position: function (pt: any) {
|
||||||
const left = pt[0] - 80
|
const left = pt[0] - 80;
|
||||||
return [left, '10%'];
|
return [left, '10%'];
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -193,8 +193,10 @@ const getStatus = (id: string) => {
|
||||||
{
|
{
|
||||||
deviceId: id,
|
deviceId: id,
|
||||||
},
|
},
|
||||||
).subscribe(() => {
|
).subscribe((message:any) => {
|
||||||
instanceStore.refresh(id);
|
if(message.payload?.value?.type !== instanceStore.current?.state.value){
|
||||||
|
instanceStore.refresh(id);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -826,8 +826,9 @@ const updateAccessData = async (id: string, values: any) => {
|
||||||
id: id,
|
id: id,
|
||||||
configuration: { ...extra },
|
configuration: { ...extra },
|
||||||
storePolicy: storePolicy,
|
storePolicy: storePolicy,
|
||||||
});
|
}).finally(()=>{
|
||||||
submitLoading.value = false
|
submitLoading.value = false
|
||||||
|
})
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
onlyMessage('操作成功!');
|
onlyMessage('操作成功!');
|
||||||
productStore.current!.storePolicy = storePolicy;
|
productStore.current!.storePolicy = storePolicy;
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-col>
|
</j-col>
|
||||||
<j-col flex="auto">
|
<j-col flex="auto">
|
||||||
<j-form-item name="id">
|
<j-form-item name="id" :validateFirst="true">
|
||||||
<template #label>
|
<template #label>
|
||||||
<span>ID</span>
|
<span>ID</span>
|
||||||
<j-tooltip
|
<j-tooltip
|
||||||
|
@ -148,10 +148,10 @@ const photoValue = ref('/images/device-product.png');
|
||||||
const imageTypes = reactive([
|
const imageTypes = reactive([
|
||||||
'image/jpeg',
|
'image/jpeg',
|
||||||
'image/png',
|
'image/png',
|
||||||
'image/jpg',
|
// 'image/jpg',
|
||||||
'image/jfif',
|
'image/jfif',
|
||||||
'image/pjp',
|
'image/pjp',
|
||||||
'image/pjpeg',
|
// 'image/pjpeg',
|
||||||
]);
|
]);
|
||||||
const deviceList = ref([
|
const deviceList = ref([
|
||||||
{
|
{
|
||||||
|
@ -188,7 +188,6 @@ const form = reactive({
|
||||||
*/
|
*/
|
||||||
const validateInput = async (_rule: Rule, value: string) => {
|
const validateInput = async (_rule: Rule, value: string) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
console.log(value.split('').length);
|
|
||||||
if (!isInput(value)) {
|
if (!isInput(value)) {
|
||||||
return Promise.reject('请输入英文或者数字或者-或者_');
|
return Promise.reject('请输入英文或者数字或者-或者_');
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -70,7 +70,11 @@
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<Ellipsis style="width: calc(100% - 100px); margin-bottom: 18px;"
|
<Ellipsis
|
||||||
|
style="
|
||||||
|
width: calc(100% - 100px);
|
||||||
|
margin-bottom: 18px;
|
||||||
|
"
|
||||||
><span
|
><span
|
||||||
style="font-weight: 600; font-size: 16px"
|
style="font-weight: 600; font-size: 16px"
|
||||||
>
|
>
|
||||||
|
@ -191,7 +195,7 @@ import {
|
||||||
updateDevice,
|
updateDevice,
|
||||||
} from '@/api/device/product';
|
} from '@/api/device/product';
|
||||||
import { isNoCommunity, downloadObject } from '@/utils/utils';
|
import { isNoCommunity, downloadObject } from '@/utils/utils';
|
||||||
import { omit , cloneDeep } from 'lodash-es';
|
import { omit, cloneDeep } from 'lodash-es';
|
||||||
import { typeOptions } from '@/components/Search/util';
|
import { typeOptions } from '@/components/Search/util';
|
||||||
import Save from './Save/index.vue';
|
import Save from './Save/index.vue';
|
||||||
import { useMenuStore } from 'store/menu';
|
import { useMenuStore } from 'store/menu';
|
||||||
|
@ -260,9 +264,7 @@ const columns = [
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const permission = usePermissionStore().hasPermission(
|
const permission = usePermissionStore().hasPermission(`device/Product:import`);
|
||||||
`device/Product:import`,
|
|
||||||
);
|
|
||||||
const _selectedRowKeys = ref<string[]>([]);
|
const _selectedRowKeys = ref<string[]>([]);
|
||||||
const currentForm = ref({});
|
const currentForm = ref({});
|
||||||
|
|
||||||
|
@ -318,7 +320,7 @@ const getActions = (
|
||||||
'accessProvider',
|
'accessProvider',
|
||||||
'messageProtocol',
|
'messageProtocol',
|
||||||
]);
|
]);
|
||||||
downloadObject(extra, data.name+'产品');
|
downloadObject(extra, data.name + '产品');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -397,31 +399,31 @@ const beforeUpload = (file: any) => {
|
||||||
onlyMessage('请上传json格式文件', 'error');
|
onlyMessage('请上传json格式文件', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(!text){
|
if (!text) {
|
||||||
onlyMessage('文件内容不能为空','error')
|
onlyMessage('文件内容不能为空', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const data = JSON.parse(text);
|
const data = JSON.parse(text);
|
||||||
// 设置导入的产品状态为未发布
|
// 设置导入的产品状态为未发布
|
||||||
data.state = 0;
|
data.state = 0;
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
onlyMessage('请上传正确格式文件', 'error');
|
onlyMessage('请上传正确格式文件', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
delete data.state;
|
delete data.state;
|
||||||
if(!data?.name){
|
if (!data?.name) {
|
||||||
data.name = "产品" + Date.now();
|
data.name = '产品' + Date.now();
|
||||||
}
|
}
|
||||||
if(!data?.deviceType || JSON.stringify(data?.deviceType) === '{}' ){
|
if (!data?.deviceType || JSON.stringify(data?.deviceType) === '{}') {
|
||||||
onlyMessage('缺少deviceType字段或对应的值','error')
|
onlyMessage('缺少deviceType字段或对应的值', 'error');
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
const res = await updateDevice(data);
|
const res = await updateDevice(data);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
onlyMessage('操作成功');
|
onlyMessage('操作成功');
|
||||||
tableRef.value?.reload();
|
tableRef.value?.reload();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
@ -471,7 +473,13 @@ const query = reactive({
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
getProviders().then((resp: any) => {
|
getProviders().then((resp: any) => {
|
||||||
const data = resp.result || [];
|
const data = resp.result || [];
|
||||||
resolve(accessConfigTypeFilter(data));
|
resolve(accessConfigTypeFilter(data).filter((i: any) => {
|
||||||
|
return (
|
||||||
|
i.id !== 'modbus-tcp' &&
|
||||||
|
i.id !== 'opc-ua'
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -548,7 +556,7 @@ const query = reactive({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '分类',
|
title: '产品分类',
|
||||||
key: 'classified',
|
key: 'classified',
|
||||||
dataIndex: 'classifiedId',
|
dataIndex: 'classifiedId',
|
||||||
search: {
|
search: {
|
||||||
|
@ -571,6 +579,7 @@ const query = reactive({
|
||||||
search: {
|
search: {
|
||||||
first: true,
|
first: true,
|
||||||
type: 'treeSelect',
|
type: 'treeSelect',
|
||||||
|
termOptions:['eq'],
|
||||||
options: async () => {
|
options: async () => {
|
||||||
return new Promise((res) => {
|
return new Promise((res) => {
|
||||||
queryOrgThree({ paging: false }).then((resp: any) => {
|
queryOrgThree({ paging: false }).then((resp: any) => {
|
||||||
|
@ -614,33 +623,40 @@ const query = reactive({
|
||||||
});
|
});
|
||||||
const saveRef = ref();
|
const saveRef = ref();
|
||||||
const handleSearch = (e: any) => {
|
const handleSearch = (e: any) => {
|
||||||
|
const newTerms = cloneDeep(e);
|
||||||
|
if (newTerms.terms?.length) {
|
||||||
|
newTerms.terms.forEach((a: any) => {
|
||||||
|
a.terms = a.terms.map((b: any) => {
|
||||||
|
if (b.column === 'id$dim-assets') {
|
||||||
|
const value = b.value;
|
||||||
|
b = {
|
||||||
|
column: 'id',
|
||||||
|
termType: 'dim-assets',
|
||||||
|
value: {
|
||||||
|
assetType: 'product',
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
type: 'org',
|
||||||
|
id: value,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if(b.column === 'accessProvider'){
|
||||||
|
if(b.value === 'collector-gateway'){
|
||||||
|
b.termType = b.termType === 'eq' ? 'in' : 'nin';
|
||||||
|
b.value = ['opc-ua','modbus-tcp','collector-gateway'];
|
||||||
|
}else if(Array.isArray(b.value) && b.value.includes('collector-gateway')){
|
||||||
|
b.value = ['opc-ua','modbus-tcp',...b.value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const newTerms = cloneDeep(e)
|
params.value = newTerms;
|
||||||
if (newTerms.terms?.length) {
|
|
||||||
newTerms.terms.forEach((a : any) => {
|
|
||||||
a.terms = a.terms.map((b: any) => {
|
|
||||||
if (b.column === 'id$dim-assets') {
|
|
||||||
const value = b.value
|
|
||||||
b = {
|
|
||||||
column: 'id',
|
|
||||||
termType: 'dim-assets',
|
|
||||||
value: {
|
|
||||||
assetType: 'product',
|
|
||||||
targets: [
|
|
||||||
{
|
|
||||||
type: 'org',
|
|
||||||
id: value,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
params.value = newTerms;
|
|
||||||
};
|
};
|
||||||
const routerParams = useRouterParams();
|
const routerParams = useRouterParams();
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
|
@ -63,8 +63,12 @@ export const validatorConfig = (value: any, _isObject: boolean = false) => {
|
||||||
export const handleTypeValue = (type:string, value: any = {}) => {
|
export const handleTypeValue = (type:string, value: any = {}) => {
|
||||||
let obj: any = {}
|
let obj: any = {}
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
//bug#22609
|
||||||
case 'array':
|
case 'array':
|
||||||
obj.elementType = value
|
obj.elementType = {
|
||||||
|
type: 'object',
|
||||||
|
properties: []
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'object':
|
case 'object':
|
||||||
obj.properties = (value || []).map((item: any) => {
|
obj.properties = (value || []).map((item: any) => {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -154,9 +154,9 @@
|
||||||
</template>
|
</template>
|
||||||
<template #registryTime="slotProps">
|
<template #registryTime="slotProps">
|
||||||
<span>{{
|
<span>{{
|
||||||
dayjs(slotProps.registryTime).format(
|
slotProps.registryTime ? dayjs(slotProps.registryTime).format(
|
||||||
'YYYY-MM-DD HH:mm:ss',
|
'YYYY-MM-DD HH:mm:ss',
|
||||||
)
|
) : ''
|
||||||
}}</span>
|
}}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
|
@ -269,6 +269,7 @@ const columns = [
|
||||||
type: 'string',
|
type: 'string',
|
||||||
defaultTermType: 'eq',
|
defaultTermType: 'eq',
|
||||||
},
|
},
|
||||||
|
ellipsis:true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '设备名称',
|
title: '设备名称',
|
||||||
|
@ -278,6 +279,7 @@ const columns = [
|
||||||
type: 'string',
|
type: 'string',
|
||||||
first: true,
|
first: true,
|
||||||
},
|
},
|
||||||
|
ellipsis:true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '产品名称',
|
title: '产品名称',
|
||||||
|
@ -375,6 +377,7 @@ const columns = [
|
||||||
title: '说明',
|
title: '说明',
|
||||||
dataIndex: 'describe',
|
dataIndex: 'describe',
|
||||||
key: 'describe',
|
key: 'describe',
|
||||||
|
ellipsis:true,
|
||||||
search: {
|
search: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
},
|
},
|
||||||
|
|
|
@ -45,7 +45,7 @@ const props = defineProps({
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - 50px);
|
height: calc(100% - 50px);
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
background-size: 95%;
|
background-size: 85%;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,7 @@ const goProviders = (param: any) => {
|
||||||
showType.value = param.type;
|
showType.value = param.type;
|
||||||
provider.value = param;
|
provider.value = param;
|
||||||
type.value = false;
|
type.value = false;
|
||||||
|
console.log(showType.value,provider.value)
|
||||||
};
|
};
|
||||||
|
|
||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
|
@ -105,6 +106,7 @@ const goBack = () => {
|
||||||
const TypeMap = new Map([
|
const TypeMap = new Map([
|
||||||
['fixed-media', 'media'],
|
['fixed-media', 'media'],
|
||||||
['gb28181-2016', 'media'],
|
['gb28181-2016', 'media'],
|
||||||
|
['onvif', 'media'],
|
||||||
['OneNet', 'cloud'],
|
['OneNet', 'cloud'],
|
||||||
['Ctwing', 'cloud'],
|
['Ctwing', 'cloud'],
|
||||||
['modbus-tcp', 'channel'],
|
['modbus-tcp', 'channel'],
|
||||||
|
@ -117,6 +119,7 @@ const TypeMap = new Map([
|
||||||
const DataMap = new Map();
|
const DataMap = new Map();
|
||||||
DataMap.set('fixed-media', { type: 'media', title: '视频类设备接入' });
|
DataMap.set('fixed-media', { type: 'media', title: '视频类设备接入' });
|
||||||
DataMap.set('gb28181-2016', { type: 'media', title: '视频类设备接入' });
|
DataMap.set('gb28181-2016', { type: 'media', title: '视频类设备接入' });
|
||||||
|
DataMap.set('onvif',{ type: 'media' , title:'视频类设备接入'});
|
||||||
DataMap.set('OneNet', { type: 'cloud', title: '云平台接入' });
|
DataMap.set('OneNet', { type: 'cloud', title: '云平台接入' });
|
||||||
DataMap.set('Ctwing', { type: 'cloud', title: '云平台接入' });
|
DataMap.set('Ctwing', { type: 'cloud', title: '云平台接入' });
|
||||||
DataMap.set('modbus-tcp', { type: 'channel', title: '通道类设备接入' });
|
DataMap.set('modbus-tcp', { type: 'channel', title: '通道类设备接入' });
|
||||||
|
@ -133,7 +136,7 @@ const getTypeList = (result: Record<string, any>) => {
|
||||||
const channel: any[] = [];
|
const channel: any[] = [];
|
||||||
const edge: any[] = [];
|
const edge: any[] = [];
|
||||||
result.map((item: any) => {
|
result.map((item: any) => {
|
||||||
if (item.id === 'fixed-media' || item.id === 'gb28181-2016') {
|
if (item.id === 'fixed-media' || item.id === 'gb28181-2016' || item.id ==='onvif') {
|
||||||
item.type = 'media';
|
item.type = 'media';
|
||||||
media.push(item);
|
media.push(item);
|
||||||
} else if (item.id === 'OneNet' || item.id === 'Ctwing') {
|
} else if (item.id === 'OneNet' || item.id === 'Ctwing') {
|
||||||
|
@ -190,6 +193,7 @@ const queryProviders = async () => {
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
const _data = resp.result || [];
|
const _data = resp.result || [];
|
||||||
dataSource.value = getTypeList(accessConfigTypeFilter(_data as any[]));
|
dataSource.value = getTypeList(accessConfigTypeFilter(_data as any[]));
|
||||||
|
console.log(dataSource.value)
|
||||||
// dataSource.value = getTypeList(resp.result)[0].list.filter(
|
// dataSource.value = getTypeList(resp.result)[0].list.filter(
|
||||||
// (item) => item.name !== '插件设备接入',
|
// (item) => item.name !== '插件设备接入',
|
||||||
// );
|
// );
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
<template>
|
||||||
|
<div class="card-last">
|
||||||
|
<j-row :gutter="[24, 24]">
|
||||||
|
<j-col :span="12">
|
||||||
|
<title-component data="基本信息" />
|
||||||
|
<div>
|
||||||
|
<j-form
|
||||||
|
:model="formState"
|
||||||
|
name="basic"
|
||||||
|
autocomplete="off"
|
||||||
|
layout="vertical"
|
||||||
|
@finish="onFinish"
|
||||||
|
>
|
||||||
|
<j-form-item
|
||||||
|
label="名称"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入名称',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
placeholder="请输入名称"
|
||||||
|
v-model:value="formState.name"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item label="说明" name="description">
|
||||||
|
<j-textarea
|
||||||
|
placeholder="请输入说明"
|
||||||
|
:rows="4"
|
||||||
|
v-model:value="formState.description"
|
||||||
|
show-count
|
||||||
|
:maxlength="200"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item>
|
||||||
|
<PermissionButton
|
||||||
|
v-if="view === 'false'"
|
||||||
|
type="primary"
|
||||||
|
html-type="submit"
|
||||||
|
:hasPermission="`link/AccessConfig:${
|
||||||
|
id === ':id' ? 'add' : 'update'
|
||||||
|
}`"
|
||||||
|
:loading="loading"
|
||||||
|
>
|
||||||
|
保存
|
||||||
|
</PermissionButton>
|
||||||
|
</j-form-item>
|
||||||
|
</j-form>
|
||||||
|
</div>
|
||||||
|
</j-col>
|
||||||
|
<j-col :span="12">
|
||||||
|
<div class="doc">
|
||||||
|
<h1>接入方式</h1>
|
||||||
|
<p>
|
||||||
|
{{ provider.name }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ provider.description }}
|
||||||
|
</p>
|
||||||
|
<h1>消息协议</h1>
|
||||||
|
<p>
|
||||||
|
{{ provider.id === 'fixed-media' ? 'URL' : 'SIP' }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</j-col>
|
||||||
|
</j-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
import { update, save } from '@/api/link/accessConfig';
|
||||||
|
interface FormState {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
const route = useRoute();
|
||||||
|
const view = route.query.view as string;
|
||||||
|
const id = route.params.id as string;
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
provider: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const loading = ref(false);
|
||||||
|
const channel = ref(props.provider.channel);
|
||||||
|
|
||||||
|
const formState = ref<FormState>({
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
});
|
||||||
|
const onFinish = async (values: any) => {
|
||||||
|
loading.value = true;
|
||||||
|
const params = {
|
||||||
|
...values,
|
||||||
|
provider: 'onvif',
|
||||||
|
transport: 'ONVIF',
|
||||||
|
channel: 'onvif',
|
||||||
|
};
|
||||||
|
const resp =
|
||||||
|
id === ':id' ? await save(params) : await update({ ...params, id });
|
||||||
|
if (resp.status === 200) {
|
||||||
|
onlyMessage('操作成功', 'success');
|
||||||
|
|
||||||
|
if (route.query.save) {
|
||||||
|
// @ts-ignore
|
||||||
|
if ((window as any).onTabSaveSuccess) {
|
||||||
|
(window as any).onTabSaveSuccess(resp.result);
|
||||||
|
setTimeout(() => window.close(), 300);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
history.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loading.value = false;
|
||||||
|
};
|
||||||
|
onMounted(() => {
|
||||||
|
if (id !== ':id') {
|
||||||
|
formState.value = {
|
||||||
|
name: props.data.name,
|
||||||
|
description: props.data?.description || '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.card-last {
|
||||||
|
padding-right: 5px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -78,12 +78,16 @@
|
||||||
<div v-else-if="channel === 'gb28181'">
|
<div v-else-if="channel === 'gb28181'">
|
||||||
<GB28181 :provider="props.provider" :data="props.data"></GB28181>
|
<GB28181 :provider="props.provider" :data="props.data"></GB28181>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="channel === 'onvif'">
|
||||||
|
<Onvif :provider="props.provider" :data="props.data"></Onvif>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup name="AccessMedia">
|
<script lang="ts" setup name="AccessMedia">
|
||||||
import { onlyMessage } from '@/utils/comm';
|
import { onlyMessage } from '@/utils/comm';
|
||||||
import GB28181 from './GB28181.vue';
|
import GB28181 from './GB28181.vue';
|
||||||
|
import Onvif from './Onvif.vue'
|
||||||
import { update, save } from '@/api/link/accessConfig';
|
import { update, save } from '@/api/link/accessConfig';
|
||||||
|
|
||||||
interface FormState {
|
interface FormState {
|
||||||
|
|
|
@ -198,6 +198,7 @@ import {
|
||||||
import { onlyMessage } from '@/utils/comm';
|
import { onlyMessage } from '@/utils/comm';
|
||||||
import { useMenuStore } from 'store/menu';
|
import { useMenuStore } from 'store/menu';
|
||||||
import { accessConfigTypeFilter } from '@/utils/setting';
|
import { accessConfigTypeFilter } from '@/utils/setting';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
const menuStory = useMenuStore();
|
const menuStory = useMenuStore();
|
||||||
const tableRef = ref<Record<string, any>>({});
|
const tableRef = ref<Record<string, any>>({});
|
||||||
|
@ -331,6 +332,9 @@ const getProvidersList = async () => {
|
||||||
const res: any = await getProviders();
|
const res: any = await getProviders();
|
||||||
providersList.value = res.result;
|
providersList.value = res.result;
|
||||||
providersOptions.value = accessConfigTypeFilter(res.result || []);
|
providersOptions.value = accessConfigTypeFilter(res.result || []);
|
||||||
|
providersOptions.value = providersOptions.value.filter((i:any)=>{
|
||||||
|
return i.id !== 'modbus-tcp' && i.id !== 'opc-ua'
|
||||||
|
})
|
||||||
};
|
};
|
||||||
getProvidersList();
|
getProvidersList();
|
||||||
|
|
||||||
|
@ -363,7 +367,23 @@ const getStatus = (slotProps: Record<string, any>) =>
|
||||||
* @param params
|
* @param params
|
||||||
*/
|
*/
|
||||||
const handleSearch = (e: any) => {
|
const handleSearch = (e: any) => {
|
||||||
params.value = e;
|
const newTerms = cloneDeep(e);
|
||||||
|
if (newTerms.terms?.length) {
|
||||||
|
newTerms.terms.forEach((a: any) => {
|
||||||
|
a.terms = a.terms.map((b: any) => {
|
||||||
|
if(b.column === 'provider'){
|
||||||
|
if(b.value === 'collector-gateway'){
|
||||||
|
b.termType = b.termType === 'eq' ? 'in' : 'nin';
|
||||||
|
b.value = ['opc-ua','modbus-tcp','collector-gateway'];
|
||||||
|
}else if(Array.isArray(b.value) && b.value.includes('collector-gateway')){
|
||||||
|
b.value = ['opc-ua','modbus-tcp',...b.value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
params.value = newTerms;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -99,7 +99,6 @@ const pickerTimeChange = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const echartsOptions = computed(() => {
|
const echartsOptions = computed(() => {
|
||||||
console.log(serverActive.value,'---')
|
|
||||||
const series = serverActive.value.length
|
const series = serverActive.value.length
|
||||||
? serverActive.value.map((key) => setOptions(serverData.data, key))
|
? serverActive.value.map((key) => setOptions(serverData.data, key))
|
||||||
: typeDataLine
|
: typeDataLine
|
||||||
|
|
|
@ -231,8 +231,12 @@ const handleClick = async (e: any) => {
|
||||||
e.protocol,
|
e.protocol,
|
||||||
e.transport,
|
e.transport,
|
||||||
);
|
);
|
||||||
|
if(e.protocol === 'onvif' && !result.scopes.find((i)=>{
|
||||||
extendFormItem.value = result.properties.map((item: any) => ({
|
return i === 'product'
|
||||||
|
})){
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
extendFormItem.value = result?.properties?.map((item: any) => ({
|
||||||
name: ['configuration', item.property],
|
name: ['configuration', item.property],
|
||||||
label: item.name,
|
label: item.name,
|
||||||
type: item.type?.type,
|
type: item.type?.type,
|
||||||
|
|
|
@ -135,8 +135,69 @@
|
||||||
placeholder="请输入接入密码"
|
placeholder="请输入接入密码"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
|
<template v-if="formData.channel === 'onvif'">
|
||||||
|
<j-form-item
|
||||||
|
label="接入地址"
|
||||||
|
:name="['others', 'onvifUrl']"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入接入密码',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
v-model:value="formData.others.onvifUrl"
|
||||||
|
></j-input>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item
|
||||||
|
label="接入账户"
|
||||||
|
:name="['others', 'onvifUsername']"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入接入密码',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
v-model:value="
|
||||||
|
formData.others.onvifUsername
|
||||||
|
"
|
||||||
|
></j-input>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item
|
||||||
|
label="接入密码"
|
||||||
|
:name="['others', 'onvifPassword']"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入接入密码',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input-password
|
||||||
|
v-model:value="
|
||||||
|
formData.others.onvifPassword
|
||||||
|
"
|
||||||
|
></j-input-password>
|
||||||
|
</j-form-item>
|
||||||
|
</template>
|
||||||
<template v-if="!!route.query.id">
|
<template v-if="!!route.query.id">
|
||||||
<j-form-item
|
<j-form-item
|
||||||
|
v-if="formData.channel === 'gb28181-2016'"
|
||||||
label="流传输模式"
|
label="流传输模式"
|
||||||
name="streamMode"
|
name="streamMode"
|
||||||
:rules="{
|
:rules="{
|
||||||
|
@ -156,26 +217,49 @@
|
||||||
</j-radio-button>
|
</j-radio-button>
|
||||||
</j-radio-group>
|
</j-radio-group>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item label="设备厂商"
|
<j-form-item
|
||||||
|
label="设备厂商"
|
||||||
name="manufacturer"
|
name="manufacturer"
|
||||||
:rules="[{ max: 64, message: '最多可输入64位字符', trigger: 'change' }]">
|
:rules="[
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64位字符',
|
||||||
|
trigger: 'change',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
<j-input
|
<j-input
|
||||||
v-model:value="formData.manufacturer"
|
v-model:value="formData.manufacturer"
|
||||||
placeholder="请输入设备厂商"
|
placeholder="请输入设备厂商"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item label="设备型号"
|
<j-form-item
|
||||||
|
label="设备型号"
|
||||||
name="model"
|
name="model"
|
||||||
:rules="[{ max: 64, message: '最多可输入64位字符', trigger: 'change' }]">
|
:rules="[
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64位字符',
|
||||||
|
trigger: 'change',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
<j-input
|
<j-input
|
||||||
v-model:value="formData.model"
|
v-model:value="formData.model"
|
||||||
placeholder="请输入设备型号"
|
placeholder="请输入设备型号"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item label="固件版本"
|
<j-form-item
|
||||||
|
label="固件版本"
|
||||||
name="firmware"
|
name="firmware"
|
||||||
:rules="[{ max: 64, message: '最多可输入64位字符', trigger: 'change' }]">
|
:rules="[
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64位字符',
|
||||||
|
trigger: 'change',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
<j-input
|
<j-input
|
||||||
v-model:value="formData.firmware"
|
v-model:value="formData.firmware"
|
||||||
placeholder="请输入固件版本"
|
placeholder="请输入固件版本"
|
||||||
|
@ -311,6 +395,7 @@ import { PROVIDER_OPTIONS } from '@/views/media/Device/const';
|
||||||
import type { ProductType } from '@/views/media/Device/typings';
|
import type { ProductType } from '@/views/media/Device/typings';
|
||||||
import SaveProduct from './SaveProduct.vue';
|
import SaveProduct from './SaveProduct.vue';
|
||||||
import { notification } from 'jetlinks-ui-components';
|
import { notification } from 'jetlinks-ui-components';
|
||||||
|
import { omit } from 'lodash-es';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -325,6 +410,9 @@ const formData = ref({
|
||||||
description: '',
|
description: '',
|
||||||
others: {
|
others: {
|
||||||
access_pwd: '',
|
access_pwd: '',
|
||||||
|
onvifUrl: '',
|
||||||
|
onvifPassword: '',
|
||||||
|
onvifUsername: '',
|
||||||
},
|
},
|
||||||
// 编辑字段
|
// 编辑字段
|
||||||
streamMode: 'UDP',
|
streamMode: 'UDP',
|
||||||
|
@ -375,6 +463,7 @@ const getDetail = async () => {
|
||||||
const res = await DeviceApi.detail(route.query.id as string);
|
const res = await DeviceApi.detail(route.query.id as string);
|
||||||
Object.assign(formData.value, res.result);
|
Object.assign(formData.value, res.result);
|
||||||
formData.value.channel = res.result.provider;
|
formData.value.channel = res.result.provider;
|
||||||
|
console.log(formData.value,'formData')
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
@ -387,7 +476,7 @@ onMounted(() => {
|
||||||
const btnLoading = ref<boolean>(false);
|
const btnLoading = ref<boolean>(false);
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
const {
|
let {
|
||||||
others,
|
others,
|
||||||
id,
|
id,
|
||||||
streamMode,
|
streamMode,
|
||||||
|
@ -402,48 +491,58 @@ const handleSubmit = () => {
|
||||||
params = !id
|
params = !id
|
||||||
? extraParams
|
? extraParams
|
||||||
: { id, streamMode, manufacturer, model, firmware, ...extraParams };
|
: { id, streamMode, manufacturer, model, firmware, ...extraParams };
|
||||||
} else {
|
} else if (formData.value.channel === 'gb28181-2016') {
|
||||||
// 国标
|
// 国标
|
||||||
const getParmas = () =>{
|
others = omit(others, [
|
||||||
if(others?.stream_mode){
|
'onvifUrl',
|
||||||
others.stream_mode = streamMode
|
'onvifPassword',
|
||||||
}
|
'onvifUsername',
|
||||||
return{
|
]);
|
||||||
others,
|
const getParmas = () => {
|
||||||
id,
|
if (others?.stream_mode) {
|
||||||
streamMode,
|
others.stream_mode = streamMode;
|
||||||
manufacturer,
|
|
||||||
model,
|
|
||||||
firmware,
|
|
||||||
...extraParams,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
|
others,
|
||||||
|
id,
|
||||||
|
streamMode,
|
||||||
|
manufacturer,
|
||||||
|
model,
|
||||||
|
firmware,
|
||||||
|
...extraParams,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
params = !id ? { others, id, ...extraParams } : getParmas();
|
||||||
|
} else {
|
||||||
|
others = omit(others, ['access_pwd']);
|
||||||
params = !id
|
params = !id
|
||||||
? { others, id, ...extraParams }
|
? {others,...extraParams}
|
||||||
: getParmas()
|
: { id, streamMode, manufacturer, model, firmware,others, ...extraParams };
|
||||||
}
|
}
|
||||||
|
|
||||||
formRef.value
|
formRef.value
|
||||||
?.validate()
|
?.validate()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
btnLoading.value = true;
|
btnLoading.value = true;
|
||||||
let res;
|
let res;
|
||||||
if(!route.query.id){
|
if (!route.query.id) {
|
||||||
const resp:any = await DeviceApi.validateId(id)
|
const resp: any = await DeviceApi.validateId(id);
|
||||||
if(resp.status === 200 && resp?.result?.passed){
|
if (resp.status === 200 && resp?.result?.passed) {
|
||||||
res = await DeviceApi.save(params)
|
res = await DeviceApi.save(params);
|
||||||
}else{
|
} else {
|
||||||
notification.error({ key: 'error', message: '设备ID已重复'})
|
notification.error({
|
||||||
}
|
key: 'error',
|
||||||
}else{
|
message: '设备ID已重复',
|
||||||
res = await DeviceApi.update(params);
|
});
|
||||||
}
|
|
||||||
if (res?.success) {
|
|
||||||
onlyMessage('保存成功');
|
|
||||||
history.back();
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
res = await DeviceApi.update(params);
|
||||||
}
|
}
|
||||||
)
|
if (res?.success) {
|
||||||
|
onlyMessage('保存成功');
|
||||||
|
history.back();
|
||||||
|
}
|
||||||
|
})
|
||||||
.catch((err: any) => {
|
.catch((err: any) => {
|
||||||
console.log('err: ', err);
|
console.log('err: ', err);
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export const PROVIDER_OPTIONS = [
|
export const PROVIDER_OPTIONS = [
|
||||||
{ label: 'GB/T28181', value: 'gb28181-2016' },
|
{ label: 'GB/T28181', value: 'gb28181-2016' },
|
||||||
{ label: '固定地址', value: 'fixed-media' },
|
{ label: '固定地址', value: 'fixed-media' },
|
||||||
|
{ label: 'Onvif', value: 'onvif'}
|
||||||
]
|
]
|
||||||
export const streamMode = [
|
export const streamMode = [
|
||||||
{ label: 'UDP', value: 'UDP' },
|
{ label: 'UDP', value: 'UDP' },
|
||||||
|
@ -10,4 +11,5 @@ export const streamMode = [
|
||||||
export const providerType = {
|
export const providerType = {
|
||||||
'gb28181-2016': 'GB/T28181',
|
'gb28181-2016': 'GB/T28181',
|
||||||
'fixed-media': '固定地址',
|
'fixed-media': '固定地址',
|
||||||
|
'onvif': 'Onvif'
|
||||||
};
|
};
|
|
@ -248,6 +248,7 @@ const columns = [
|
||||||
messageProtocol$in: [
|
messageProtocol$in: [
|
||||||
'gb28181-2016',
|
'gb28181-2016',
|
||||||
'fixed-media',
|
'fixed-media',
|
||||||
|
'onvif',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
started: 'processing',
|
started: 'processing',
|
||||||
disable: 'error',
|
disable: 'error',
|
||||||
}"
|
}"
|
||||||
|
@click="handleView(slotProps.id, slotProps.triggerType)"
|
||||||
>
|
>
|
||||||
<template #type>
|
<template #type>
|
||||||
<span
|
<span
|
||||||
|
@ -108,6 +109,8 @@ import { getImage, onlyMessage } from '@/utils/comm';
|
||||||
import Save from './Save/index.vue';
|
import Save from './Save/index.vue';
|
||||||
import { useAlarmConfigurationStore } from '@/store/alarm';
|
import { useAlarmConfigurationStore } from '@/store/alarm';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useMenuStore } from 'store/menu';
|
||||||
|
const menuStory = useMenuStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const id = route.query?.id;
|
const id = route.query?.id;
|
||||||
|
|
||||||
|
@ -191,6 +194,18 @@ const saveSuccess = () => {
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
actionRef.value.reload();
|
actionRef.value.reload();
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* 查看
|
||||||
|
* @param id
|
||||||
|
* @param triggerType 触发类型
|
||||||
|
*/
|
||||||
|
const handleView = (id: string, triggerType: string) => {
|
||||||
|
menuStory.jumpPage(
|
||||||
|
'rule-engine/Scene/Save',
|
||||||
|
{},
|
||||||
|
{ triggerType: triggerType, id, type: 'view' },
|
||||||
|
);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.subTitle {
|
.subTitle {
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
:params="params"
|
:params="params"
|
||||||
:gridColumns="[1, 1, 2]"
|
:gridColumns="[1, 1, 2]"
|
||||||
:gridColumn="2"
|
:gridColumn="2"
|
||||||
model="CARD"
|
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
>
|
>
|
||||||
<template #card="slotProps">
|
<template #card="slotProps">
|
||||||
|
@ -42,7 +41,7 @@
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<Ellipsis style="width: calc(100% - 100px);">
|
<Ellipsis style="width: calc(100% - 100px)">
|
||||||
<span style="font-weight: 500">
|
<span style="font-weight: 500">
|
||||||
{{ slotProps.alarmName }}
|
{{ slotProps.alarmName }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -116,6 +115,60 @@
|
||||||
</template>
|
</template>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
</template>
|
</template>
|
||||||
|
<template #targetType="slotProps">
|
||||||
|
{{ titleMap.get(slotProps.targetType) }}
|
||||||
|
</template>
|
||||||
|
<template #alarmTime="slotProps">
|
||||||
|
{{ dayjs(slotProps.alarmTime).format('YYYY-MM-DD HH:mm:ss')}}
|
||||||
|
</template>
|
||||||
|
<template #state="slotProps">
|
||||||
|
<BadgeStatus
|
||||||
|
:status="slotProps.state.value"
|
||||||
|
:statusName="{
|
||||||
|
warning: 'warning',
|
||||||
|
normal: 'default',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
</BadgeStatus
|
||||||
|
><span
|
||||||
|
:style="
|
||||||
|
slotProps.state.value === 'warning'
|
||||||
|
? 'color: #E50012'
|
||||||
|
: 'color:black'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ slotProps.state.text }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template #actions="slotProps">
|
||||||
|
<j-space>
|
||||||
|
<template
|
||||||
|
v-for="i in getActions(slotProps, 'table')"
|
||||||
|
:key="i.key"
|
||||||
|
>
|
||||||
|
<PermissionButton
|
||||||
|
type="link"
|
||||||
|
:disabled="
|
||||||
|
i.key === 'solve' &&
|
||||||
|
slotProps.state.value === 'normal'
|
||||||
|
"
|
||||||
|
:tooltip="{
|
||||||
|
...i.tooltip,
|
||||||
|
}"
|
||||||
|
@click="i.onClick"
|
||||||
|
:hasPermission="
|
||||||
|
i.key == 'solve'
|
||||||
|
? 'rule-engine/Alarm/Log:action'
|
||||||
|
: 'rule-engine/Alarm/Log:view'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<AIcon :type="i.icon" />
|
||||||
|
</template>
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
</j-space>
|
||||||
|
</template>
|
||||||
</JProTable>
|
</JProTable>
|
||||||
</FullPage>
|
</FullPage>
|
||||||
<SolveComponent
|
<SolveComponent
|
||||||
|
@ -128,21 +181,13 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
import {
|
import { getOrgList, query, getAlarmProduct } from '@/api/rule-engine/log';
|
||||||
getProductList,
|
|
||||||
getDeviceList,
|
|
||||||
getOrgList,
|
|
||||||
query,
|
|
||||||
getAlarmProduct
|
|
||||||
} from '@/api/rule-engine/log';
|
|
||||||
import { queryLevel } from '@/api/rule-engine/config';
|
import { queryLevel } from '@/api/rule-engine/config';
|
||||||
import Search from '@/components/Search';
|
|
||||||
import { useAlarmStore } from '@/store/alarm';
|
import { useAlarmStore } from '@/store/alarm';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import type { ActionsType } from '@/components/Table';
|
import type { ActionsType } from '@/components/Table';
|
||||||
import SolveComponent from '../SolveComponent/index.vue';
|
import SolveComponent from '../SolveComponent/index.vue';
|
||||||
import SolveLog from '../SolveLog/index.vue';
|
|
||||||
import { useMenuStore } from '@/store/menu';
|
import { useMenuStore } from '@/store/menu';
|
||||||
import { usePermissionStore } from '@/store/permission';
|
import { usePermissionStore } from '@/store/permission';
|
||||||
const menuStory = useMenuStore();
|
const menuStory = useMenuStore();
|
||||||
|
@ -150,14 +195,14 @@ const tableRef = ref();
|
||||||
const alarmStore = useAlarmStore();
|
const alarmStore = useAlarmStore();
|
||||||
const { data } = storeToRefs(alarmStore);
|
const { data } = storeToRefs(alarmStore);
|
||||||
|
|
||||||
const getDefaulitLevel = () => {
|
const getDefaultLevel = () => {
|
||||||
queryLevel().then((res) => {
|
queryLevel().then((res) => {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
data.value.defaultLevel = res.result?.levels || [];
|
data.value.defaultLevel = res.result?.levels || [];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
getDefaulitLevel();
|
getDefaultLevel();
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
type: string;
|
type: string;
|
||||||
id?: string;
|
id?: string;
|
||||||
|
@ -175,24 +220,34 @@ titleMap.set('device', '设备');
|
||||||
titleMap.set('other', '其他');
|
titleMap.set('other', '其他');
|
||||||
titleMap.set('org', '组织');
|
titleMap.set('org', '组织');
|
||||||
const columns = [
|
const columns = [
|
||||||
|
{
|
||||||
|
title:'配置名称',
|
||||||
|
dataIndex:'alarmName',
|
||||||
|
key:'alarmName',
|
||||||
|
},{
|
||||||
|
title:'类型',
|
||||||
|
dataIndex:'targetType',
|
||||||
|
key:'targetType',
|
||||||
|
scopedSlots:true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title:'关联场景联动',
|
||||||
|
dataIndex:'sourceName',
|
||||||
|
key:'sourceName',
|
||||||
|
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '告警级别',
|
title: '告警级别',
|
||||||
dataIndex: 'level',
|
dataIndex: 'level',
|
||||||
key: 'level',
|
key: 'level',
|
||||||
search: {
|
search: {
|
||||||
type: 'select',
|
type: 'select',
|
||||||
options: async () => {
|
options: data.value.defaultLevel.map((item: any) => {
|
||||||
const res = await queryLevel()
|
return {
|
||||||
if (res.success && res.result?.levels) {
|
|
||||||
return (res.result.levels as any[]).map((item: any) => {
|
|
||||||
return {
|
|
||||||
label: item.title,
|
label: item.title,
|
||||||
value: item.level
|
value: item.level,
|
||||||
}
|
};
|
||||||
})
|
}),
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -202,6 +257,7 @@ const columns = [
|
||||||
search: {
|
search: {
|
||||||
type: 'date',
|
type: 'date',
|
||||||
},
|
},
|
||||||
|
scopedSlots: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
|
@ -220,131 +276,133 @@ const columns = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dateIndex: 'actions',
|
||||||
|
key: 'actions',
|
||||||
|
scopedSlots: true,
|
||||||
|
width: 200,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const newColumns = computed(() => {
|
const newColumns = computed(() => {
|
||||||
|
const otherColumns = {
|
||||||
|
title: '产品名称',
|
||||||
|
dataIndex: 'targetId',
|
||||||
|
key: 'targetId',
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: async () => {
|
||||||
|
const termType = [
|
||||||
|
{
|
||||||
|
column: 'targetType',
|
||||||
|
termType: 'eq',
|
||||||
|
type: 'and',
|
||||||
|
value: props.type,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const otherColumns = {
|
if (props.id) {
|
||||||
title: '产品名称',
|
termType.push({
|
||||||
dataIndex: 'targetId',
|
termType: 'eq',
|
||||||
key: 'targetId',
|
column: 'alarmConfigId',
|
||||||
search: {
|
value: props.id,
|
||||||
type: 'select',
|
type: 'and',
|
||||||
options: async () => {
|
});
|
||||||
const termType = [
|
}
|
||||||
{
|
|
||||||
column: "targetType",
|
|
||||||
termType: "eq",
|
|
||||||
type: "and",
|
|
||||||
value: props.type,
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
if (props.id) {
|
const resp: any = await handleSearch({
|
||||||
termType.push({
|
sorts: [{ name: 'alarmTime', order: 'desc' }],
|
||||||
termType: 'eq',
|
terms: termType,
|
||||||
column: 'alarmConfigId',
|
});
|
||||||
value: props.id,
|
const listMap: Map<string, any> = new Map();
|
||||||
type: 'and',
|
|
||||||
},)
|
|
||||||
}
|
|
||||||
|
|
||||||
const resp: any = await handleSearch({
|
if (resp.status === 200) {
|
||||||
sorts: [{ name: 'alarmTime', order: 'desc' }],
|
resp.result.data.forEach((item) => {
|
||||||
terms: termType
|
if (item.targetId) {
|
||||||
});
|
listMap.set(item.targetId, {
|
||||||
const listMap: Map<string, any> = new Map()
|
label: item.targetName,
|
||||||
|
value: item.targetId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (resp.status === 200) {
|
return [...listMap.values()];
|
||||||
resp.result.data.forEach(item => {
|
}
|
||||||
if (item.targetId) {
|
return [];
|
||||||
listMap.set(item.targetId, {
|
},
|
||||||
label: item.targetName,
|
},
|
||||||
value: item.targetId,
|
};
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
switch (props.type) {
|
||||||
|
case 'device':
|
||||||
|
otherColumns.title = '设备名称';
|
||||||
|
break;
|
||||||
|
case 'org':
|
||||||
|
otherColumns.title = '组织名称';
|
||||||
|
break;
|
||||||
|
case 'other':
|
||||||
|
otherColumns.title = '场景名称';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (props.type === 'device') {
|
||||||
|
const productColumns = {
|
||||||
|
title: '产品名称',
|
||||||
|
dataIndex: 'product_id',
|
||||||
|
key: 'product_id',
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: async () => {
|
||||||
|
const termType = [
|
||||||
|
{
|
||||||
|
column: 'id$alarm-record',
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
column: 'targetType',
|
||||||
|
termType: 'eq',
|
||||||
|
value: 'device',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const resp: any = await getAlarmProduct({
|
||||||
|
sorts: [{ name: 'alarmTime', order: 'desc' }],
|
||||||
|
terms: termType,
|
||||||
|
});
|
||||||
|
const listMap: Map<string, any> = new Map();
|
||||||
|
|
||||||
return [...listMap.values()]
|
if (resp.status === 200) {
|
||||||
|
resp.result.data.forEach((item) => {
|
||||||
}
|
if (item.productId) {
|
||||||
return [];
|
listMap.set(item.productId, {
|
||||||
},
|
label: item.productName,
|
||||||
},
|
value: item.productId,
|
||||||
}
|
});
|
||||||
|
}
|
||||||
switch(props.type) {
|
});
|
||||||
case 'device':
|
return [...listMap.values()];
|
||||||
otherColumns.title = '设备名称'
|
}
|
||||||
break;
|
return [];
|
||||||
case 'org':
|
},
|
||||||
otherColumns.title = '组织名称'
|
},
|
||||||
break;
|
};
|
||||||
case 'other':
|
return [otherColumns, productColumns, ...columns];
|
||||||
otherColumns.title = '场景名称'
|
}
|
||||||
break;
|
return ['all', 'detail'].includes(props.type)
|
||||||
}
|
? columns
|
||||||
if(props.type === 'device'){
|
: [otherColumns, ...columns];
|
||||||
const productColumns = {
|
});
|
||||||
title: '产品名称',
|
|
||||||
dataIndex: 'product_id',
|
|
||||||
key: 'product_id',
|
|
||||||
search: {
|
|
||||||
type: 'select',
|
|
||||||
options: async () => {
|
|
||||||
const termType = [
|
|
||||||
{
|
|
||||||
column:"id$alarm-record",
|
|
||||||
value:[
|
|
||||||
{
|
|
||||||
column: "targetType",
|
|
||||||
termType: "eq",
|
|
||||||
value: "device",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
const resp: any = await getAlarmProduct({
|
|
||||||
sorts: [{ name: 'alarmTime', order: 'desc' }],
|
|
||||||
terms: termType
|
|
||||||
});
|
|
||||||
const listMap: Map<string, any> = new Map()
|
|
||||||
|
|
||||||
if (resp.status === 200) {
|
|
||||||
resp.result.data.forEach(item => {
|
|
||||||
if (item.productId) {
|
|
||||||
listMap.set(item.productId, {
|
|
||||||
label: item.productName,
|
|
||||||
value: item.productId,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
return [...listMap.values()]
|
|
||||||
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return [otherColumns,productColumns,...columns]
|
|
||||||
}
|
|
||||||
return ['all', 'detail'].includes(props.type) ? columns : [
|
|
||||||
otherColumns,
|
|
||||||
...columns,
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
let params: any = ref({
|
let params: any = ref({
|
||||||
sorts: [{ name: 'alarmTime', order: 'desc' }],
|
sorts: [{ name: 'alarmTime', order: 'desc' }],
|
||||||
terms: [],
|
terms: [],
|
||||||
});
|
});
|
||||||
const handleSearch = async (params: any) => {
|
const handleSearch = async (params: any) => {
|
||||||
const resp:any = await query(params);
|
const resp: any = await query(params);
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
const res:any = await getOrgList();
|
const res: any = await getOrgList();
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
resp.result.data.map((item: any) => {
|
resp.result.data.map((item: any) => {
|
||||||
if (item.targetType === 'org') {
|
if (item.targetType === 'org') {
|
||||||
|
@ -363,31 +421,6 @@ const handleSearch = async (params: any) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
onMounted(() => {
|
|
||||||
if (props.type !== 'all' && !props.id) {
|
|
||||||
params.value.terms = [
|
|
||||||
{
|
|
||||||
termType: 'eq',
|
|
||||||
column: 'targetType',
|
|
||||||
value: props.type,
|
|
||||||
type: 'and',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
if (props.id) {
|
|
||||||
params.value.terms = [
|
|
||||||
{
|
|
||||||
termType: 'eq',
|
|
||||||
column: 'alarmConfigId',
|
|
||||||
value: props.id,
|
|
||||||
type: 'and',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
if (props.type === 'all') {
|
|
||||||
params.value.terms = [];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const search = (data: any) => {
|
const search = (data: any) => {
|
||||||
params.value.terms = [...data?.terms];
|
params.value.terms = [...data?.terms];
|
||||||
|
@ -399,13 +432,16 @@ const search = (data: any) => {
|
||||||
type: 'and',
|
type: 'and',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if(props.type === 'device' && data?.terms[0]?.terms[0]?.column === 'product_id'){
|
if (
|
||||||
params.value.terms = [{
|
props.type === 'device' &&
|
||||||
column:"targetId$dev-instance",
|
data?.terms[0]?.terms[0]?.column === 'product_id'
|
||||||
value:[
|
) {
|
||||||
data?.terms[0]?.terms[0]
|
params.value.terms = [
|
||||||
]
|
{
|
||||||
}]
|
column: 'targetId$dev-instance',
|
||||||
|
value: [data?.terms[0]?.terms[0]],
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
if (props.id) {
|
if (props.id) {
|
||||||
params.value.terms.push({
|
params.value.terms.push({
|
||||||
|
@ -419,7 +455,7 @@ const search = (data: any) => {
|
||||||
|
|
||||||
const getActions = (
|
const getActions = (
|
||||||
currentData: Partial<Record<string, any>>,
|
currentData: Partial<Record<string, any>>,
|
||||||
type: 'card',
|
type: 'card' | 'table',
|
||||||
): ActionsType[] => {
|
): ActionsType[] => {
|
||||||
if (!currentData) return [];
|
if (!currentData) return [];
|
||||||
const actions = [
|
const actions = [
|
||||||
|
@ -485,12 +521,31 @@ const closeSolve = () => {
|
||||||
data.value.solveVisible = false;
|
data.value.solveVisible = false;
|
||||||
tableRef.value.reload(params.value);
|
tableRef.value.reload(params.value);
|
||||||
};
|
};
|
||||||
/**
|
onMounted(() => {
|
||||||
* 关闭处理记录
|
if (props.type !== 'all' && !props.id) {
|
||||||
*/
|
params.value.terms = [
|
||||||
const closeLog = () => {
|
{
|
||||||
data.value.logVisible = false;
|
termType: 'eq',
|
||||||
};
|
column: 'targetType',
|
||||||
|
value: props.type,
|
||||||
|
type: 'and',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (props.id) {
|
||||||
|
params.value.terms = [
|
||||||
|
{
|
||||||
|
termType: 'eq',
|
||||||
|
column: 'alarmConfigId',
|
||||||
|
value: props.id,
|
||||||
|
type: 'and',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (props.type === 'all') {
|
||||||
|
params.value.terms = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.content-left {
|
.content-left {
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
import { isNoCommunity } from '@/utils/utils';
|
import { isNoCommunity } from '@/utils/utils';
|
||||||
import { useAlarmStore } from '@/store/alarm';
|
import { useAlarmStore } from '@/store/alarm';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { queryLevel } from '@/api/rule-engine/config';
|
|
||||||
import TableComponents from './TabComponent/index.vue';
|
import TableComponents from './TabComponent/index.vue';
|
||||||
const list = [
|
const list = [
|
||||||
{
|
{
|
||||||
|
@ -52,14 +51,6 @@ const noList = [
|
||||||
];
|
];
|
||||||
const alarmStore = useAlarmStore();
|
const alarmStore = useAlarmStore();
|
||||||
const { data } = storeToRefs(alarmStore);
|
const { data } = storeToRefs(alarmStore);
|
||||||
const getDefaulitLevel = () => {
|
|
||||||
queryLevel().then((res)=>{
|
|
||||||
if(res.status === 200 ){
|
|
||||||
data.value.defaultLevel = res.result?.levels || [];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
getDefaulitLevel();
|
|
||||||
const onTabChange = (key:string) =>{
|
const onTabChange = (key:string) =>{
|
||||||
data.value.tab = key;
|
data.value.tab = key;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
box-shadow: 0 3px 6px -4px #0000001f, 0 6px 16px #00000014, 0 9px 28px 8px #0000000d;
|
box-shadow: 0 3px 6px -4px #0000001f, 0 6px 16px #00000014, 0 9px 28px 8px #0000000d;
|
||||||
}
|
}
|
||||||
|
.scene-select-item{
|
||||||
|
border-bottom: 1px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.column {
|
.column {
|
||||||
color: #00a4fe;
|
color: #00a4fe;
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
<template>
|
||||||
|
<j-dropdown
|
||||||
|
class="scene-select-value"
|
||||||
|
trigger="click"
|
||||||
|
v-model:visible="visible"
|
||||||
|
:overlayStyle="{
|
||||||
|
maxWidth: '300px',
|
||||||
|
}"
|
||||||
|
@visibleChange="visibleChange"
|
||||||
|
>
|
||||||
|
<div @click.prevent="visible = true">
|
||||||
|
<slot :label="label">
|
||||||
|
<div class="dropdown-button value">
|
||||||
|
<AIcon v-if="!!icon" :type="icon" />
|
||||||
|
<Ellipsis style="max-width: 220px">
|
||||||
|
{{ label }}
|
||||||
|
</Ellipsis>
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
<template #overlay>
|
||||||
|
<div class="scene-select-content">
|
||||||
|
<j-tabs @change="tabsChange" v-model:activeKey="mySource">
|
||||||
|
<j-tab-pane
|
||||||
|
v-for="item in tabsOptions"
|
||||||
|
:tab="item.label"
|
||||||
|
:key="item.key"
|
||||||
|
>
|
||||||
|
<div v-for="(i, index) in myValue" class="scene-select-item">
|
||||||
|
<ArrayItem
|
||||||
|
v-model:value="myValue[index]"
|
||||||
|
:valueName="valueName"
|
||||||
|
:labelName="labelName"
|
||||||
|
:options="options"
|
||||||
|
:component="item.component"
|
||||||
|
@select="(v, l) => onSelect(v, l, index)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<j-button @click="addItem">+</j-button>
|
||||||
|
<j-button @click="deleteItem">-</j-button>
|
||||||
|
</j-tab-pane>
|
||||||
|
</j-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</j-dropdown>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup name="ArrayParamsDropdown">
|
||||||
|
import type { ValueType } from './typings';
|
||||||
|
import { defaultSetting } from './typings';
|
||||||
|
import ArrayItem from './ArrayItem.vue';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
import { getOption } from '../DropdownButton/util';
|
||||||
|
type Emit = {
|
||||||
|
(e: 'update:value', data: ValueType): void;
|
||||||
|
(e: 'update:source', data: string): void;
|
||||||
|
(
|
||||||
|
e: 'select',
|
||||||
|
data: any,
|
||||||
|
label?: string,
|
||||||
|
labelObj?: Record<number, any>,
|
||||||
|
option?: any,
|
||||||
|
): void;
|
||||||
|
(e: 'tabChange', data: any): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
...defaultSetting,
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<Emit>();
|
||||||
|
const myValue = ref<ValueType>(cloneDeep(props.value) || [undefined, undefined] as any);
|
||||||
|
const mySource = ref<string>(props.source);
|
||||||
|
const label = ref<any>(props.placeholder);
|
||||||
|
const visible = ref(false);
|
||||||
|
const tabsChange = (e: string) => {
|
||||||
|
mySource.value = e;
|
||||||
|
emit('update:source', mySource.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelect = (v: any, _label: string, index: number) => {
|
||||||
|
emit('update:value', myValue.value);
|
||||||
|
label.value[index] = _label;
|
||||||
|
emit('select', myValue.value, _label, label.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const visibleChange = (v: boolean) => {
|
||||||
|
visible.value = v;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addItem = () => {
|
||||||
|
myValue.value?.push(null);
|
||||||
|
emit('update:value', myValue.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteItem = () => {
|
||||||
|
myValue.value?.pop();
|
||||||
|
emit('update:value', myValue.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
const _options = props.options;
|
||||||
|
const _value = props.value;
|
||||||
|
const _valueName = props.valueName;
|
||||||
|
if (Array.isArray(_value) && _value.length) {
|
||||||
|
label.value = []
|
||||||
|
_value?.forEach((i: any, index: number) => {
|
||||||
|
const option = getOption(_options, i as string, _valueName);
|
||||||
|
if (option) {
|
||||||
|
label.value.push(option[props.labelName] || option.name);
|
||||||
|
} else {
|
||||||
|
label.value.push(i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(()=>props.value,()=>{
|
||||||
|
myValue.value = cloneDeep(props.value);
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
@import '../DropdownButton/index.less';
|
||||||
|
.manual-time-picker {
|
||||||
|
position: absolute;
|
||||||
|
top: -2px;
|
||||||
|
left: 0;
|
||||||
|
border: none;
|
||||||
|
visibility: hidden;
|
||||||
|
:deep(.ant-picker-input) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,82 @@
|
||||||
|
<template>
|
||||||
|
<div class="select-box-content">
|
||||||
|
<DropdownTimePicker
|
||||||
|
v-if="['time', 'date'].includes(props.component)"
|
||||||
|
type="time"
|
||||||
|
v-model:value="myValue"
|
||||||
|
@change="timeChange"
|
||||||
|
/>
|
||||||
|
<template
|
||||||
|
v-else-if="['select', 'enum', 'boolean'].includes(props.component)"
|
||||||
|
>
|
||||||
|
<DropdownMenus
|
||||||
|
v-if="options.length"
|
||||||
|
:options="options"
|
||||||
|
:value="myValue"
|
||||||
|
:valueName="valueName"
|
||||||
|
@click="onSelect"
|
||||||
|
/>
|
||||||
|
<div class="scene-select-empty" v-else>
|
||||||
|
<j-empty />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<ValueItem
|
||||||
|
v-else
|
||||||
|
v-model:modelValue="myValue"
|
||||||
|
:itemType="props.component"
|
||||||
|
:options="options"
|
||||||
|
@change="valueItemChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import ValueItem from '@/components/ValueItem/index.vue';
|
||||||
|
import type { ValueType } from './typings';
|
||||||
|
import { DropdownMenus, DropdownTimePicker } from '../DropdownButton';
|
||||||
|
import { getOption } from '../DropdownButton/util';
|
||||||
|
import { defaultSetting } from './typings';
|
||||||
|
const props = defineProps({
|
||||||
|
...defaultSetting,
|
||||||
|
component: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
type Emit = {
|
||||||
|
(e: 'update:value', data: ValueType): void;
|
||||||
|
(e: 'update:source', data: string): void;
|
||||||
|
(
|
||||||
|
e: 'select',
|
||||||
|
data: any,
|
||||||
|
label?: string,
|
||||||
|
labelObj?: Record<number, any>,
|
||||||
|
option?: any,
|
||||||
|
): void;
|
||||||
|
(e: 'tabChange', data: any): void;
|
||||||
|
};
|
||||||
|
const emit = defineEmits<Emit>();
|
||||||
|
const label = ref();
|
||||||
|
const myValue = ref<ValueType>(props.value);
|
||||||
|
const valueItemChange = (e: string) => {
|
||||||
|
label.value = e;
|
||||||
|
emit('update:value', e);
|
||||||
|
emit('select', e, label.value, { 0: label.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelect = (e: string, option: any, index: number) => {
|
||||||
|
label.value = option[props.labelName];
|
||||||
|
emit('update:value', e);
|
||||||
|
emit('select', e, label.value, { 0: label.value }, option);
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeChange = (e: any) => {
|
||||||
|
label.value = e;
|
||||||
|
emit('update:value', e);
|
||||||
|
emit('select', e, label.value, { 0: label.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import '../DropdownButton/index.less';
|
||||||
|
</style>
|
|
@ -1,17 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<ParamsDropdown
|
<ParamsDropdown
|
||||||
v-for="(i,index) in myValue"
|
v-for="(i,index) in myValue"
|
||||||
v-model:value='myValue[index]'
|
v-model:value='myValue[index]'
|
||||||
v-model:source='mySource'
|
v-model:source='mySource'
|
||||||
:valueName='valueName'
|
:valueName='valueName'
|
||||||
:labelName='labelName'
|
:labelName='labelName'
|
||||||
:options='options'
|
:options='options'
|
||||||
:icon='icon'
|
:icon='icon'
|
||||||
:placeholder='placeholder'
|
:placeholder='placeholder'
|
||||||
:tabs-options='tabsOptions'
|
:tabs-options='tabsOptions'
|
||||||
:metricOptions='metricOptions'
|
:metricOptions='metricOptions'
|
||||||
@select='(v, l) => onSelect(v, l, index)'
|
@select='(v, l) => onSelect(v, l, index)'
|
||||||
@tabChange='tabChange'
|
@tabChange='tabChange'
|
||||||
/>
|
/>
|
||||||
<!-- <ParamsDropdown
|
<!-- <ParamsDropdown
|
||||||
v-model:value='myValue[1]'
|
v-model:value='myValue[1]'
|
||||||
|
@ -26,13 +26,13 @@
|
||||||
@select='(v, l) => onSelect(v, l,1)'
|
@select='(v, l) => onSelect(v, l,1)'
|
||||||
@tabChange='tabChange'
|
@tabChange='tabChange'
|
||||||
/> -->
|
/> -->
|
||||||
<j-button @click="addDropdown" v-if="['contains_all', 'contains_any', 'not_contains'].includes(props.termType)" class="operation">+</j-button>
|
|
||||||
<j-button @click="deleteDropdown" v-if="['contains_all', 'contains_any', 'not_contains'].includes(props.termType) && myValue?.length > 2" class="operation">-</j-button>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang='ts' setup name='DoubleParamsDropdown'>
|
<script lang='ts' setup name='DoubleParamsDropdown'>
|
||||||
import ParamsDropdown from './index.vue'
|
import ParamsDropdown from './index.vue'
|
||||||
import { defaultSetting, ValueType } from './typings'
|
import { defaultSetting, ValueType } from './typings'
|
||||||
|
import Array from './Array.vue'
|
||||||
|
|
||||||
type Emit = {
|
type Emit = {
|
||||||
(e: 'update:value', data: ValueType): void
|
(e: 'update:value', data: ValueType): void
|
||||||
|
@ -63,13 +63,6 @@ const onSelect = (v: any, _label: string, index: number) => {
|
||||||
emit('select', myValue.value, _label, label)
|
emit('select', myValue.value, _label, label)
|
||||||
}
|
}
|
||||||
|
|
||||||
const addDropdown = () =>{
|
|
||||||
myValue.value.push(undefined)
|
|
||||||
}
|
|
||||||
const deleteDropdown = () =>{
|
|
||||||
myValue.value.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
const tabChange = (e: string) => {
|
const tabChange = (e: string) => {
|
||||||
emit('update:source', e)
|
emit('update:source', e)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import ParamsDropdown from './index.vue'
|
import ParamsDropdown from './index.vue'
|
||||||
import DoubleParamsDropdown from './Double.vue'
|
import DoubleParamsDropdown from './Double.vue'
|
||||||
|
import ArrayParamsDropdown from './Array.vue'
|
||||||
|
|
||||||
export default ParamsDropdown
|
export default ParamsDropdown
|
||||||
|
|
||||||
export {
|
export {
|
||||||
DoubleParamsDropdown
|
DoubleParamsDropdown,
|
||||||
|
ArrayParamsDropdown
|
||||||
}
|
}
|
|
@ -167,7 +167,6 @@ watchEffect(() => {
|
||||||
const option = getOption(_options, _value as string, _valueName) // 回显label值
|
const option = getOption(_options, _value as string, _valueName) // 回显label值
|
||||||
myValue.value = isMetric ? props.metric : props.value
|
myValue.value = isMetric ? props.metric : props.value
|
||||||
mySource.value = props.source
|
mySource.value = props.source
|
||||||
console.log(option)
|
|
||||||
if (option) {
|
if (option) {
|
||||||
label.value = option[props.labelName] || option.name
|
label.value = option[props.labelName] || option.name
|
||||||
treeOpenKeys.value = openKeysByTree(_options, props.value, props.valueName)
|
treeOpenKeys.value = openKeysByTree(_options, props.value, props.valueName)
|
||||||
|
|
|
@ -1,76 +1,95 @@
|
||||||
<template>
|
<template>
|
||||||
<div class='terms-params-item'>
|
<div class="terms-params-item">
|
||||||
<div v-if='!isFirst' class='term-type-warp'>
|
<div v-if="!isFirst" class="term-type-warp">
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
:options='[
|
:options="[
|
||||||
{ label: "并且", value: "and" },
|
{ label: '并且', value: 'and' },
|
||||||
{ label: "或者", value: "or" },
|
{ label: '或者', value: 'or' },
|
||||||
]'
|
]"
|
||||||
type='type'
|
type="type"
|
||||||
v-model:value='paramsValue.type'
|
v-model:value="paramsValue.type"
|
||||||
@select='typeSelect'
|
@select="typeSelect"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="params-item_button"
|
||||||
|
@mouseover="mouseover"
|
||||||
|
@mouseout="mouseout"
|
||||||
|
>
|
||||||
|
<DropdownButton
|
||||||
|
:options="columnOptions"
|
||||||
|
icon="icon-zhihangdongzuoxie-1"
|
||||||
|
type="column"
|
||||||
|
value-name="column"
|
||||||
|
label-name="fullName"
|
||||||
|
placeholder="请选择参数"
|
||||||
|
v-model:value="paramsValue.column"
|
||||||
|
component="treeSelect"
|
||||||
|
@select="columnSelect"
|
||||||
|
/>
|
||||||
|
<DropdownButton
|
||||||
|
:options="termTypeOptions"
|
||||||
|
type="termType"
|
||||||
|
value-name="id"
|
||||||
|
label-name="name"
|
||||||
|
placeholder="操作符"
|
||||||
|
v-model:value="paramsValue.termType"
|
||||||
|
@select="termsTypeSelect"
|
||||||
|
/>
|
||||||
|
<div v-if="!['notnull', 'isnull'].includes(paramsValue.termType)">
|
||||||
|
<DoubleParamsDropdown
|
||||||
|
v-if="showDouble"
|
||||||
|
icon="icon-canshu"
|
||||||
|
placeholder="参数值"
|
||||||
|
:options="valueOptions"
|
||||||
|
:metricOptions="metricOption"
|
||||||
|
:tabsOptions="tabsOptions"
|
||||||
|
v-model:value="paramsValue.value.value"
|
||||||
|
v-model:source="paramsValue.value.source"
|
||||||
|
@select="valueSelect"
|
||||||
|
/>
|
||||||
|
<ArrayParamsDropdown
|
||||||
|
v-else-if="
|
||||||
|
showArray
|
||||||
|
"
|
||||||
|
icon="icon-canshu"
|
||||||
|
placeholder="参数值"
|
||||||
|
:options="valueOptions"
|
||||||
|
:metricOptions="metricOption"
|
||||||
|
:tabsOptions="tabsOptions"
|
||||||
|
v-model:value="paramsValue.value.value"
|
||||||
|
v-model:source="paramsValue.value.source"
|
||||||
|
@select="valueSelect"
|
||||||
|
/>
|
||||||
|
<ParamsDropdown
|
||||||
|
v-else
|
||||||
|
icon="icon-canshu"
|
||||||
|
placeholder="参数值"
|
||||||
|
:options="valueOptions"
|
||||||
|
:metricOptions="metricOption"
|
||||||
|
:tabsOptions="tabsOptions"
|
||||||
|
:metric="paramsValue.value?.metric"
|
||||||
|
v-model:value="paramsValue.value.value"
|
||||||
|
v-model:source="paramsValue.value.source"
|
||||||
|
@select="valueSelect"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<j-popconfirm
|
||||||
|
title="确认删除?"
|
||||||
|
@confirm="onDelete"
|
||||||
|
:overlayStyle="{ minWidth: '180px' }"
|
||||||
|
>
|
||||||
|
<div v-show="showDelete" class="button-delete">
|
||||||
|
<AIcon type="CloseOutlined" />
|
||||||
|
</div>
|
||||||
|
</j-popconfirm>
|
||||||
|
</div>
|
||||||
|
<div class="term-add" @click.stop="termAdd" v-if="isLast">
|
||||||
|
<div class="terms-content">
|
||||||
|
<AIcon type="PlusOutlined" style="font-size: 12px" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class='params-item_button'
|
|
||||||
@mouseover='mouseover'
|
|
||||||
@mouseout='mouseout'
|
|
||||||
>
|
|
||||||
<DropdownButton
|
|
||||||
:options='columnOptions'
|
|
||||||
icon='icon-zhihangdongzuoxie-1'
|
|
||||||
type='column'
|
|
||||||
value-name='column'
|
|
||||||
label-name='fullName'
|
|
||||||
placeholder='请选择参数'
|
|
||||||
v-model:value='paramsValue.column'
|
|
||||||
component='treeSelect'
|
|
||||||
@select='columnSelect'
|
|
||||||
/>
|
|
||||||
<DropdownButton
|
|
||||||
:options='termTypeOptions'
|
|
||||||
type="termType"
|
|
||||||
value-name='id'
|
|
||||||
label-name='name'
|
|
||||||
placeholder="操作符"
|
|
||||||
v-model:value='paramsValue.termType'
|
|
||||||
@select='termsTypeSelect'
|
|
||||||
/>
|
|
||||||
<div v-if="!['notnull','isnull'].includes(paramsValue.termType)">
|
|
||||||
<DoubleParamsDropdown
|
|
||||||
v-if='showDouble'
|
|
||||||
icon='icon-canshu'
|
|
||||||
placeholder='参数值'
|
|
||||||
:options='valueOptions'
|
|
||||||
:metricOptions='metricOption'
|
|
||||||
:tabsOptions='tabsOptions'
|
|
||||||
v-model:value='paramsValue.value.value'
|
|
||||||
v-model:source='paramsValue.value.source'
|
|
||||||
@select='valueSelect'
|
|
||||||
/>
|
|
||||||
<ParamsDropdown
|
|
||||||
v-else
|
|
||||||
icon='icon-canshu'
|
|
||||||
placeholder='参数值'
|
|
||||||
:options='valueOptions'
|
|
||||||
:metricOptions='metricOption'
|
|
||||||
:tabsOptions='tabsOptions'
|
|
||||||
:metric='paramsValue.value?.metric'
|
|
||||||
v-model:value='paramsValue.value.value'
|
|
||||||
v-model:source='paramsValue.value.source'
|
|
||||||
@select='valueSelect'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<j-popconfirm title='确认删除?' @confirm='onDelete' :overlayStyle='{minWidth: "180px"}'>
|
|
||||||
<div v-show='showDelete' class='button-delete'> <AIcon type='CloseOutlined' /></div>
|
|
||||||
</j-popconfirm>
|
|
||||||
</div>
|
|
||||||
<div class='term-add' @click.stop='termAdd' v-if='isLast'>
|
|
||||||
<div class='terms-content'>
|
|
||||||
<AIcon type='PlusOutlined' style='font-size: 12px' />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ParamsItem">
|
<script setup lang="ts" name="ParamsItem">
|
||||||
|
@ -78,9 +97,17 @@ import type { PropType } from 'vue';
|
||||||
import type { TermsType } from '@/views/rule-engine/Scene/typings';
|
import type { TermsType } from '@/views/rule-engine/Scene/typings';
|
||||||
import DropdownButton from '../DropdownButton';
|
import DropdownButton from '../DropdownButton';
|
||||||
import { getOption } from '../DropdownButton/util';
|
import { getOption } from '../DropdownButton/util';
|
||||||
import ParamsDropdown, { DoubleParamsDropdown } from '../ParamsDropdown';
|
import ParamsDropdown, {
|
||||||
|
DoubleParamsDropdown,
|
||||||
|
ArrayParamsDropdown,
|
||||||
|
} from '../ParamsDropdown';
|
||||||
import { inject, watch } from 'vue';
|
import { inject, watch } from 'vue';
|
||||||
import { ContextKey, arrayParamsKey, timeTypeKeys } from './util';
|
import {
|
||||||
|
ContextKey,
|
||||||
|
arrayParamsKey,
|
||||||
|
timeTypeKeys,
|
||||||
|
doubleParamsKey,
|
||||||
|
} from './util';
|
||||||
import { useSceneStore } from 'store/scene';
|
import { useSceneStore } from 'store/scene';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { Form } from 'jetlinks-ui-components';
|
import { Form } from 'jetlinks-ui-components';
|
||||||
|
@ -108,51 +135,47 @@ type TabsOption = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
isFirst: {
|
isFirst: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true,
|
||||||
},
|
},
|
||||||
isLast: {
|
isLast: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true,
|
||||||
},
|
},
|
||||||
showDeleteBtn: {
|
showDeleteBtn: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true,
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0,
|
||||||
},
|
},
|
||||||
termsName: {
|
termsName: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0,
|
||||||
},
|
},
|
||||||
branchName: {
|
branchName: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0,
|
||||||
},
|
},
|
||||||
branches_Index: {
|
whenName: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0,
|
||||||
},
|
},
|
||||||
whenName: {
|
value: {
|
||||||
type: Number,
|
type: Object as PropType<TermsType>,
|
||||||
default: 0
|
default: () => ({
|
||||||
},
|
column: '',
|
||||||
value: {
|
type: '',
|
||||||
type: Object as PropType<TermsType>,
|
termType: 'eq',
|
||||||
default: () => ({
|
value: {
|
||||||
column: '',
|
source: 'manual',
|
||||||
type: '',
|
value: undefined,
|
||||||
termType: 'eq',
|
},
|
||||||
value: {
|
}),
|
||||||
source: 'manual',
|
},
|
||||||
value: undefined
|
});
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits<Emit>();
|
const emit = defineEmits<Emit>();
|
||||||
|
|
||||||
|
@ -280,7 +303,7 @@ watch(
|
||||||
|
|
||||||
const showDouble = computed(() => {
|
const showDouble = computed(() => {
|
||||||
const isRange = paramsValue.termType
|
const isRange = paramsValue.termType
|
||||||
? arrayParamsKey.includes(paramsValue.termType)
|
? doubleParamsKey.includes(paramsValue.termType)
|
||||||
: false;
|
: false;
|
||||||
const isSourceMetric = paramsValue.value?.source === 'metric';
|
const isSourceMetric = paramsValue.value?.source === 'metric';
|
||||||
if (metricsCacheOption.value.length) {
|
if (metricsCacheOption.value.length) {
|
||||||
|
@ -300,6 +323,32 @@ const showDouble = computed(() => {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const showArray = computed(()=>{
|
||||||
|
const isRange = paramsValue.termType ? [
|
||||||
|
'in',
|
||||||
|
'nin',
|
||||||
|
'contains_all',
|
||||||
|
'contains_any',
|
||||||
|
'not_contains',
|
||||||
|
].includes(paramsValue.termType) : false;
|
||||||
|
const isSourceMetric = paramsValue.value?.source === 'metric';
|
||||||
|
if (metricsCacheOption.value.length) {
|
||||||
|
metricOption.value = metricsCacheOption.value.filter((item) =>
|
||||||
|
isRange ? item.range : !item.range,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
metricOption.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRange) {
|
||||||
|
if (isMetric.value) {
|
||||||
|
return !isSourceMetric;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
|
||||||
const mouseover = () => {
|
const mouseover = () => {
|
||||||
if (props.showDeleteBtn) {
|
if (props.showDeleteBtn) {
|
||||||
showDelete.value = true;
|
showDelete.value = true;
|
||||||
|
@ -319,20 +368,32 @@ const columnSelect = (option: any) => {
|
||||||
// 如果参数类型未发生变化,则不修改操作符以及值
|
// 如果参数类型未发生变化,则不修改操作符以及值
|
||||||
const termTypes = option.termTypes;
|
const termTypes = option.termTypes;
|
||||||
|
|
||||||
if (!termTypes.some((item: {id: string}) => paramsValue.termType === item.id)) { // 修改操作符
|
if (
|
||||||
termTypeChange = true
|
!termTypes.some(
|
||||||
paramsValue.termType = termTypes?.length ? termTypes[0].id : 'eq'
|
(item: { id: string }) => paramsValue.termType === item.id,
|
||||||
}
|
)
|
||||||
|
) {
|
||||||
if (hasTypeChange || !tabsOptions.value.every(a => a.key === paramsValue.value.source )) { // 类型发生变化
|
// 修改操作符
|
||||||
paramsValue.termType = termTypes?.length ? termTypes[0].id : 'eq'
|
termTypeChange = true;
|
||||||
paramsValue.value = {
|
paramsValue.termType = termTypes?.length ? termTypes[0].id : 'eq';
|
||||||
source: tabsOptions.value[0].key,
|
|
||||||
value: undefined
|
|
||||||
}
|
}
|
||||||
} else if (termTypeChange) {
|
if (
|
||||||
const oldValue = isArray(paramsValue.value!.value) ? paramsValue.value!.value[0] : paramsValue.value!.value
|
hasTypeChange ||
|
||||||
const value = arrayParamsKey.includes(paramsValue.termType as string) ? [ oldValue, undefined ] : oldValue
|
!tabsOptions.value.every((a) => a.key === paramsValue.value.source)
|
||||||
|
) {
|
||||||
|
// 类型发生变化
|
||||||
|
paramsValue.termType = termTypes?.length ? termTypes[0].id : 'eq';
|
||||||
|
paramsValue.value = {
|
||||||
|
source: tabsOptions.value[0].key,
|
||||||
|
value: undefined,
|
||||||
|
};
|
||||||
|
} else if (termTypeChange) {
|
||||||
|
const oldValue = isArray(paramsValue.value!.value)
|
||||||
|
? paramsValue.value!.value[0]
|
||||||
|
: paramsValue.value!.value;
|
||||||
|
const value = arrayParamsKey.includes(paramsValue.termType as string)
|
||||||
|
? [oldValue, undefined]
|
||||||
|
: oldValue;
|
||||||
|
|
||||||
const _source = paramsValue.value?.source || tabsOptions.value[0].key;
|
const _source = paramsValue.value?.source || tabsOptions.value[0].key;
|
||||||
const newValue: any = {
|
const newValue: any = {
|
||||||
|
@ -344,22 +405,21 @@ const columnSelect = (option: any) => {
|
||||||
newValue.metric = paramsValue.value?.metric;
|
newValue.metric = paramsValue.value?.metric;
|
||||||
}
|
}
|
||||||
|
|
||||||
paramsValue.value = newValue
|
paramsValue.value = newValue;
|
||||||
}
|
}
|
||||||
|
console.log(paramsValue, hasTypeChange);
|
||||||
handOptionByColumn(option)
|
handOptionByColumn(option);
|
||||||
emit('update:value', { ...paramsValue })
|
emit('update:value', { ...paramsValue });
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
formItemContext.onFieldChange()
|
formItemContext.onFieldChange();
|
||||||
})
|
});
|
||||||
|
formModel.value.options!.when[props.branchName].terms[props.whenName].terms[
|
||||||
if (!formModel.value.options!.when[props.branches_Index]) {
|
props.termsName
|
||||||
formModel.value.options!.when[props.branches_Index] = {terms:[{terms: [['', '', '', '并且']]}]}
|
][0] = option.name;
|
||||||
}
|
formModel.value.options!.when[props.branchName].terms[props.whenName].terms[
|
||||||
console.log([props.branchName, props.whenName, props.termsName])
|
props.termsName
|
||||||
formModel.value.options!.when[props.branches_Index].terms[props.whenName].terms[props.termsName][0] = option.name
|
][1] = paramsValue.termType;
|
||||||
formModel.value.options!.when[props.branches_Index].terms[props.whenName].terms[props.termsName][1] = paramsValue.termType
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const termsTypeSelect = (e: { key: string; name: string }) => {
|
const termsTypeSelect = (e: { key: string; name: string }) => {
|
||||||
const oldValue = isArray(paramsValue.value!.value)
|
const oldValue = isArray(paramsValue.value!.value)
|
||||||
|
@ -390,26 +450,27 @@ const termsTypeSelect = (e: { key: string; name: string }) => {
|
||||||
value: value,
|
value: value,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_source === 'metric') {
|
if (_source === 'metric') {
|
||||||
newValue.metric = paramsValue.value?.metric
|
newValue.metric = paramsValue.value?.metric;
|
||||||
const isArray = isString(paramsValue.value!.value) ? paramsValue.value!.value?.includes?.(',') : false
|
const isArray = isString(paramsValue.value!.value)
|
||||||
if (arrayParamsKey.includes(e.key) !== isArray) { // 有变化
|
? paramsValue.value!.value?.includes?.(',')
|
||||||
newValue.value = undefined
|
: false;
|
||||||
|
if (arrayParamsKey.includes(e.key) !== isArray) {
|
||||||
|
// 有变化
|
||||||
|
newValue.value = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (['isnull', 'notnull'].includes(e.key)) {
|
||||||
|
newValue.value = 1;
|
||||||
if(
|
newValue.source = tabsOptions.value[0].key;
|
||||||
['isnull','notnull'].includes(e.key)
|
}
|
||||||
){
|
paramsValue.value = newValue;
|
||||||
newValue.value = 1
|
emit('update:value', { ...paramsValue });
|
||||||
}
|
formItemContext.onFieldChange();
|
||||||
paramsValue.value = newValue
|
formModel.value.options!.when[props.branchName].terms[props.whenName].terms[
|
||||||
|
props.termsName
|
||||||
emit('update:value', { ...paramsValue })
|
][1] = e.name;
|
||||||
formItemContext.onFieldChange()
|
};
|
||||||
formModel.value.options!.when[props.branches_Index].terms[props.whenName].terms[props.termsName][1] = e.key
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const valueSelect = (
|
const valueSelect = (
|
||||||
v: any,
|
v: any,
|
||||||
|
@ -420,48 +481,51 @@ const valueSelect = (
|
||||||
if (paramsValue.value?.source === 'metric') {
|
if (paramsValue.value?.source === 'metric') {
|
||||||
paramsValue.value.metric = option?.id;
|
paramsValue.value.metric = option?.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newValues = { ...paramsValue };
|
const newValues = { ...paramsValue };
|
||||||
|
if (paramsValue.value?.source !== 'metric') {
|
||||||
if (paramsValue.value?.source !== 'metric') {
|
delete newValues.value.metric;
|
||||||
delete newValues.value.metric
|
}
|
||||||
}
|
emit('update:value', { ...newValues });
|
||||||
emit('update:value', { ...newValues })
|
formItemContext.onFieldChange();
|
||||||
formItemContext.onFieldChange()
|
formModel.value.options!.when[props.branchName].terms[props.whenName].terms[
|
||||||
|
props.termsName
|
||||||
if (!formModel.value.options!.when[props.branches_Index]) {
|
][2] = labelObj;
|
||||||
formModel.value.options!.when[props.branches_Index] = {terms:[{terms: [['', '', '', '并且']]}]}
|
};
|
||||||
}
|
|
||||||
formModel.value.options!.when[props.branches_Index].terms[props.whenName].terms[props.termsName][2] = labelObj
|
|
||||||
}
|
|
||||||
|
|
||||||
const typeSelect = (e: any) => {
|
const typeSelect = (e: any) => {
|
||||||
emit('update:value', { ...paramsValue })
|
emit('update:value', { ...paramsValue });
|
||||||
formModel.value.options!.when[props.branches_Index].terms[props.whenName].terms[props.termsName][3] = e.label
|
formModel.value.options!.when[props.branchName].terms[props.whenName].terms[
|
||||||
}
|
props.termsName
|
||||||
|
][3] = e.label;
|
||||||
|
};
|
||||||
|
|
||||||
const termAdd = () => {
|
const termAdd = () => {
|
||||||
const terms = {
|
const terms = {
|
||||||
column: undefined,
|
column: undefined,
|
||||||
value: {
|
value: {
|
||||||
source: 'manual',
|
source: 'manual',
|
||||||
value: undefined
|
value: undefined,
|
||||||
},
|
},
|
||||||
termType: undefined,
|
termType: undefined,
|
||||||
type: 'and',
|
type: 'and',
|
||||||
key: `params_${new Date().getTime()}`
|
key: `params_${new Date().getTime()}`,
|
||||||
}
|
};
|
||||||
if (!formModel.value.options!.when[props.branches_Index]) {
|
formModel.value.branches?.[props.branchName]?.when?.[
|
||||||
formModel.value.options!.when[props.branches_Index] = {terms:[{terms: [['', '', '', '并且']]}]}
|
props.whenName
|
||||||
}
|
]?.terms?.push(terms);
|
||||||
formModel.value.branches?.[props.branches_Index]?.when?.[props.whenName]?.terms?.push(terms)
|
formModel.value.options!.when[props.branchName].terms[props.whenName].terms[
|
||||||
formModel.value.options!.when[props.branches_Index].terms[props.whenName].terms.push(['', '', '', '并且'])
|
props.termsName
|
||||||
}
|
].push(['', '', '', '并且']);
|
||||||
|
};
|
||||||
|
|
||||||
const onDelete = () => {
|
const onDelete = () => {
|
||||||
formModel.value.branches?.[props.branches_Index]?.when?.[props.whenName]?.terms?.splice(props.termsName, 1)
|
formModel.value.branches?.[props.branchName]?.when?.[
|
||||||
formModel.value.options!.when[props.branches_Index].terms[props.whenName].terms.splice(props.termsName, 1)
|
props.whenName
|
||||||
}
|
]?.terms?.splice(props.termsName, 1);
|
||||||
|
formModel.value.options!.when[props.branchName].terms[
|
||||||
|
props.whenName
|
||||||
|
].terms.splice(props.termsName, 1);
|
||||||
|
};
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
Object.assign(
|
Object.assign(
|
||||||
|
@ -477,9 +541,7 @@ nextTick(() => {
|
||||||
'key',
|
'key',
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { BranchesThen } from '@/views/rule-engine/Scene/typings'
|
||||||
|
|
||||||
export const ContextKey = 'columnOptions'
|
export const ContextKey = 'columnOptions'
|
||||||
export const arrayParamsKey = ['nbtw', 'btw', 'in', 'nin', 'contains_all', 'contains_any', 'not_contains']
|
export const arrayParamsKey = ['nbtw', 'btw', 'in', 'nin', 'contains_all', 'contains_any', 'not_contains']
|
||||||
|
export const doubleParamsKey= ['nbtw','btw']
|
||||||
export const timeTypeKeys = ['time_gt_now', 'time_lt_now']
|
export const timeTypeKeys = ['time_gt_now', 'time_lt_now']
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1274,6 +1274,10 @@
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入用户名前缀',
|
message: '请输入用户名前缀',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
validator: checkCh,
|
||||||
|
trigger: 'change'
|
||||||
|
}
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<j-input
|
<j-input
|
||||||
|
@ -1548,7 +1552,14 @@ const form = reactive({
|
||||||
uploadLoading: false,
|
uploadLoading: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 请求头和参数必填验证
|
|
||||||
|
const checkCh = (_rule:Rule,value:string): Promise<any> =>
|
||||||
|
new Promise((resolve,reject) => {
|
||||||
|
if (/[\u4e00-\u9fa5]/.test(value)) return reject('用户名不能包含中文');
|
||||||
|
else return resolve('')
|
||||||
|
})
|
||||||
|
|
||||||
|
// 请求头和参数必填验
|
||||||
const headerValid = ref(true);
|
const headerValid = ref(true);
|
||||||
const paramsValid = ref(true);
|
const paramsValid = ref(true);
|
||||||
const headerValidator = () => {
|
const headerValidator = () => {
|
||||||
|
|
|
@ -271,15 +271,15 @@
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="upload-image"
|
class="upload-image"
|
||||||
v-if="formValue.backgroud"
|
v-if="formValue.background"
|
||||||
:style="
|
:style="
|
||||||
formValue.backgroud
|
formValue.background
|
||||||
? `background-image: url(${formValue.backgroud});`
|
? `background-image: url(${formValue.background});`
|
||||||
: ''
|
: ''
|
||||||
"
|
"
|
||||||
></div>
|
></div>
|
||||||
<div
|
<div
|
||||||
v-if="formValue.backgroud"
|
v-if="formValue.background"
|
||||||
class="upload-image-mask"
|
class="upload-image-mask"
|
||||||
>
|
>
|
||||||
点击修改
|
点击修改
|
||||||
|
@ -345,7 +345,7 @@ const form = reactive<formType>({
|
||||||
'base-path': `${window.location.origin}/api`,
|
'base-path': `${window.location.origin}/api`,
|
||||||
logo: '',
|
logo: '',
|
||||||
ico: '',
|
ico: '',
|
||||||
backgroud: '',
|
background: '',
|
||||||
},
|
},
|
||||||
rulesFrom: {
|
rulesFrom: {
|
||||||
title: [
|
title: [
|
||||||
|
@ -387,8 +387,8 @@ const form = reactive<formType>({
|
||||||
headerTheme: configInfo.front?.headerTheme,
|
headerTheme: configInfo.front?.headerTheme,
|
||||||
logo: configInfo.front?.logo || '/logo.png',
|
logo: configInfo.front?.logo || '/logo.png',
|
||||||
ico: configInfo.front?.ico || '/favicon.ico',
|
ico: configInfo.front?.ico || '/favicon.ico',
|
||||||
backgroud:
|
background:
|
||||||
configInfo.front?.backgroud || '/images/login.png',
|
configInfo.front?.background || '/images/login.png',
|
||||||
apiKey: configInfo.amap?.apiKey,
|
apiKey: configInfo.amap?.apiKey,
|
||||||
'base-path': configInfo.paths?.['base-path'],
|
'base-path': configInfo.paths?.['base-path'],
|
||||||
};
|
};
|
||||||
|
@ -497,7 +497,7 @@ const uploader: uploaderType = {
|
||||||
} else if (info.file.status === 'done') {
|
} else if (info.file.status === 'done') {
|
||||||
info.file.url = info.file.response?.result;
|
info.file.url = info.file.response?.result;
|
||||||
form.backLoading = false;
|
form.backLoading = false;
|
||||||
form.formValue.backgroud = info.file.response?.result;
|
form.formValue.background = info.file.response?.result;
|
||||||
} else if (info.file.status === 'error') {
|
} else if (info.file.status === 'error') {
|
||||||
form.logoLoading = false;
|
form.logoLoading = false;
|
||||||
onlyMessage('背景图上传失败,请稍后再试', 'error');
|
onlyMessage('背景图上传失败,请稍后再试', 'error');
|
||||||
|
|
|
@ -4,40 +4,65 @@
|
||||||
<h3>基本信息</h3>
|
<h3>基本信息</h3>
|
||||||
<j-form ref="basicFormRef" :model="form.data" class="basic-form">
|
<j-form ref="basicFormRef" :model="form.data" class="basic-form">
|
||||||
<div class="row" style="display: flex">
|
<div class="row" style="display: flex">
|
||||||
<j-form-item ref="uploadIcon" label="菜单图标" name="icon" :rules="[
|
<j-form-item
|
||||||
{
|
ref="uploadIcon"
|
||||||
required: true,
|
label="菜单图标"
|
||||||
message: '请上传图标',
|
name="icon"
|
||||||
trigger: 'change',
|
:rules="[
|
||||||
},
|
{
|
||||||
]" style="flex: 0 0 186px">
|
required: true,
|
||||||
|
message: '请上传图标',
|
||||||
|
trigger: 'change',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
style="flex: 0 0 186px"
|
||||||
|
>
|
||||||
<div class="icon-upload has-icon" v-if="form.data.icon">
|
<div class="icon-upload has-icon" v-if="form.data.icon">
|
||||||
<AIcon :type="form.data.icon" style="font-size: 90px" />
|
<AIcon
|
||||||
<span class="mark" @click="dialogVisible = true">点击修改</span>
|
:type="form.data.icon"
|
||||||
|
style="font-size: 90px"
|
||||||
|
/>
|
||||||
|
<span class="mark" @click="dialogVisible = true"
|
||||||
|
>点击修改</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else @click="dialogVisible = true" class="icon-upload no-icon">
|
<div
|
||||||
|
v-else
|
||||||
|
@click="dialogVisible = true"
|
||||||
|
class="icon-upload no-icon"
|
||||||
|
>
|
||||||
<span>
|
<span>
|
||||||
<AIcon type="PlusOutlined" style="font-size: 30px" />
|
<AIcon
|
||||||
|
type="PlusOutlined"
|
||||||
|
style="font-size: 30px"
|
||||||
|
/>
|
||||||
<p>点击选择图标</p>
|
<p>点击选择图标</p>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-row :gutter="24" style="flex: 1 1 auto">
|
<j-row :gutter="24" style="flex: 1 1 auto">
|
||||||
<j-col :span="12">
|
<j-col :span="12">
|
||||||
<j-form-item label="名称" name="name" :rules="[
|
<j-form-item
|
||||||
{
|
label="名称"
|
||||||
required: true,
|
name="name"
|
||||||
message: '请输入名称',
|
:rules="[
|
||||||
trigger: 'change',
|
{
|
||||||
},
|
required: true,
|
||||||
{
|
message: '请输入名称',
|
||||||
max: 64,
|
trigger: 'change',
|
||||||
message: '最多可输入64个字符',
|
},
|
||||||
trigger: 'change',
|
{
|
||||||
},
|
max: 64,
|
||||||
]">
|
message: '最多可输入64个字符',
|
||||||
<j-input v-model:value="form.data.name" placeholder="请输入名称" />
|
trigger: 'change',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
v-model:value="form.data.name"
|
||||||
|
placeholder="请输入名称"
|
||||||
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-col>
|
</j-col>
|
||||||
<j-col :span="12">
|
<j-col :span="12">
|
||||||
|
@ -46,81 +71,140 @@
|
||||||
name="code"
|
name="code"
|
||||||
:validateFirst="true"
|
:validateFirst="true"
|
||||||
:rules="[
|
:rules="[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: '请输入编码',
|
message: '请输入编码',
|
||||||
trigger: 'change',
|
trigger: 'change',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
max: 64,
|
max: 64,
|
||||||
message: '最多可输入64个字符',
|
message: '最多可输入64个字符',
|
||||||
trigger: 'change',
|
trigger: 'change',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
validator: form.checkCode,
|
validator: checkCh,
|
||||||
trigger: 'blur',
|
trigger: ['change', 'blur'],
|
||||||
},
|
},
|
||||||
]">
|
{
|
||||||
<j-input v-model:value="form.data.code" placeholder="请输入编码" />
|
validator: form.checkCode,
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
v-model:value="form.data.code"
|
||||||
|
placeholder="请输入编码"
|
||||||
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-col>
|
</j-col>
|
||||||
<j-col :span="12">
|
<j-col :span="12">
|
||||||
<j-form-item :rules="[
|
<j-form-item
|
||||||
{
|
:rules="[
|
||||||
required: true,
|
{
|
||||||
message: '请输入页面地址',
|
required: true,
|
||||||
},
|
message: '请输入页面地址',
|
||||||
{ max: 128, message: '最多可输入128个字符' },
|
},
|
||||||
{ pattern: /^\//, message: '请正确填写地址,以/开头' },
|
{
|
||||||
]" :validateFirst="true" label="页面地址" name="url">
|
max: 128,
|
||||||
<j-input v-model:value="form.data.url" placeholder="请输入页面地址" />
|
message: '最多可输入128个字符',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /^\//,
|
||||||
|
message: '请正确填写地址,以/开头',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:validateFirst="true"
|
||||||
|
label="页面地址"
|
||||||
|
name="url"
|
||||||
|
>
|
||||||
|
<j-input
|
||||||
|
v-model:value="form.data.url"
|
||||||
|
placeholder="请输入页面地址"
|
||||||
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-col>
|
</j-col>
|
||||||
<j-col :span="12">
|
<j-col :span="12">
|
||||||
<j-form-item label="排序" name="sortIndex" :rules="[
|
<j-form-item
|
||||||
{
|
label="排序"
|
||||||
pattern: /^[0-9]*[1-9][0-9]*$/,
|
name="sortIndex"
|
||||||
message: '请输入大于0的整数',
|
:rules="[
|
||||||
},
|
{
|
||||||
]">
|
pattern: /^[0-9]*[1-9][0-9]*$/,
|
||||||
<j-input-number v-model:value="form.data.sortIndex" placeholder="请输入排序"
|
message: '请输入大于0的整数',
|
||||||
style="width: 100%" />
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<j-input-number
|
||||||
|
v-model:value="form.data.sortIndex"
|
||||||
|
placeholder="请输入排序"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-col>
|
</j-col>
|
||||||
</j-row>
|
</j-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<j-form-item label="说明" name="describe">
|
<j-form-item label="说明" name="describe">
|
||||||
<j-textarea v-model:value="form.data.describe" :rows="4" show-count :maxlength="200"
|
<j-textarea
|
||||||
placeholder="请输入说明" />
|
v-model:value="form.data.describe"
|
||||||
|
:rows="4"
|
||||||
|
show-count
|
||||||
|
:maxlength="200"
|
||||||
|
placeholder="请输入说明"
|
||||||
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-form>
|
</j-form>
|
||||||
</div>
|
</div>
|
||||||
<div class="card" v-if="!form.data.appId">
|
<div class="card" v-if="!form.data.appId">
|
||||||
<h3>权限配置</h3>
|
<h3>权限配置</h3>
|
||||||
<j-form ref="permissFormRef" :model="form.data" class="basic-form permiss-form">
|
<j-form
|
||||||
|
ref="permissFormRef"
|
||||||
|
:model="form.data"
|
||||||
|
class="basic-form permiss-form"
|
||||||
|
>
|
||||||
<j-form-item name="accessSupport" required v-if="isNoCommunity">
|
<j-form-item name="accessSupport" required v-if="isNoCommunity">
|
||||||
<template #label>
|
<template #label>
|
||||||
<span style="margin-right: 3px">数据权限控制</span>
|
<span style="margin-right: 3px">数据权限控制</span>
|
||||||
<j-tooltip title="此菜单页面数据所对应的资产类型">
|
<j-tooltip title="此菜单页面数据所对应的资产类型">
|
||||||
<AIcon type="QuestionCircleOutlined" class="img-style" style="color: #a6a6a6" />
|
<AIcon
|
||||||
|
type="QuestionCircleOutlined"
|
||||||
|
class="img-style"
|
||||||
|
style="color: #a6a6a6"
|
||||||
|
/>
|
||||||
</j-tooltip>
|
</j-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<j-radio-group v-model:value="form.data.accessSupport" name="radioGroup">
|
<j-radio-group
|
||||||
|
v-model:value="form.data.accessSupport"
|
||||||
|
name="radioGroup"
|
||||||
|
>
|
||||||
<j-radio value="unsupported">不支持</j-radio>
|
<j-radio value="unsupported">不支持</j-radio>
|
||||||
<j-radio value="support">支持</j-radio>
|
<j-radio value="support">支持</j-radio>
|
||||||
<j-radio value="indirect">
|
<j-radio value="indirect">
|
||||||
<span style="margin-right: 3px">间接控制</span>
|
<span style="margin-right: 3px">间接控制</span>
|
||||||
<j-tooltip title="此菜单内的数据基于其他菜单的数据权限控制">
|
<j-tooltip
|
||||||
<AIcon type="QuestionCircleFilled" class="img-style" />
|
title="此菜单内的数据基于其他菜单的数据权限控制"
|
||||||
|
>
|
||||||
|
<AIcon
|
||||||
|
type="QuestionCircleFilled"
|
||||||
|
class="img-style"
|
||||||
|
/>
|
||||||
</j-tooltip>
|
</j-tooltip>
|
||||||
</j-radio>
|
</j-radio>
|
||||||
</j-radio-group>
|
</j-radio-group>
|
||||||
|
|
||||||
<j-form-item name="assetType" v-if="form.data.accessSupport === 'support'"
|
<j-form-item
|
||||||
:rules="[{ required: true, message: '请选择资产类型' }]" style="margin-top: 24px; margin-bottom: 0">
|
name="assetType"
|
||||||
<j-select v-model:value="form.data.assetType" style="width: 500px" placeholder="请选择资产类型" show-search
|
v-if="form.data.accessSupport === 'support'"
|
||||||
:options="form.assetsType">
|
:rules="[{ required: true, message: '请选择资产类型' }]"
|
||||||
|
style="margin-top: 24px; margin-bottom: 0"
|
||||||
|
>
|
||||||
|
<j-select
|
||||||
|
v-model:value="form.data.assetType"
|
||||||
|
style="width: 500px"
|
||||||
|
placeholder="请选择资产类型"
|
||||||
|
show-search
|
||||||
|
:options="form.assetsType"
|
||||||
|
>
|
||||||
<!-- <j-select-option
|
<!-- <j-select-option
|
||||||
v-for="item in form.assetsType"
|
v-for="item in form.assetsType"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
|
@ -129,32 +213,60 @@
|
||||||
</j-select>
|
</j-select>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
|
|
||||||
<j-form-item name="indirectMenus" v-if="form.data.accessSupport === 'indirect'"
|
<j-form-item
|
||||||
:rules="[{ required: true, message: '请选择关联菜单' }]" style="margin-top: 24px; margin-bottom: 0">
|
name="indirectMenus"
|
||||||
<j-tree-select v-model:value="form.data.indirectMenus" style="width: 400px" :dropdown-style="{
|
v-if="form.data.accessSupport === 'indirect'"
|
||||||
maxHeight: '400px',
|
:rules="[{ required: true, message: '请选择关联菜单' }]"
|
||||||
overflow: 'auto',
|
style="margin-top: 24px; margin-bottom: 0"
|
||||||
}" placeholder="请选择关联菜单" multiple show-search :tree-data="form.treeData" :field-names="{
|
>
|
||||||
children: 'children',
|
<j-tree-select
|
||||||
label: 'name',
|
v-model:value="form.data.indirectMenus"
|
||||||
value: 'id',
|
style="width: 400px"
|
||||||
}">
|
:dropdown-style="{
|
||||||
|
maxHeight: '400px',
|
||||||
|
overflow: 'auto',
|
||||||
|
}"
|
||||||
|
placeholder="请选择关联菜单"
|
||||||
|
multiple
|
||||||
|
show-search
|
||||||
|
:tree-data="form.treeData"
|
||||||
|
:field-names="{
|
||||||
|
children: 'children',
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
}"
|
||||||
|
>
|
||||||
</j-tree-select>
|
</j-tree-select>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item label="权限">
|
<j-form-item label="权限">
|
||||||
<PermissChoose :first-width="3" max-height="350px" v-model:value="form.data.permissions"
|
<PermissChoose
|
||||||
:key="form.data.id || ''" />
|
:first-width="3"
|
||||||
|
max-height="350px"
|
||||||
|
v-model:value="form.data.permissions"
|
||||||
|
:key="form.data.id || ''"
|
||||||
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-form>
|
</j-form>
|
||||||
</div>
|
</div>
|
||||||
<PermissionButton type="primary" :hasPermission="`${permission}:${route.params.id === ':id' ? 'add' : 'update'
|
<PermissionButton
|
||||||
}`" @click="form.clickSave" :loading='form.saveLoading' class="saveBtn">
|
type="primary"
|
||||||
保存
|
:hasPermission="`${permission}:${
|
||||||
|
route.params.id === ':id' ? 'add' : 'update'
|
||||||
|
}`"
|
||||||
|
@click="form.clickSave"
|
||||||
|
:loading="form.saveLoading"
|
||||||
|
class="saveBtn"
|
||||||
|
>
|
||||||
|
保存
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
<!-- 弹窗 -->
|
<!-- 弹窗 -->
|
||||||
<ChooseIconDialog v-if="dialogVisible" v-model:visible="dialogVisible" :icon="form.data.icon"
|
<ChooseIconDialog
|
||||||
@confirm="(typeStr: string) => choseIcon(typeStr)" />
|
v-if="dialogVisible"
|
||||||
|
v-model:visible="dialogVisible"
|
||||||
|
:icon="form.data.icon"
|
||||||
|
@confirm="(typeStr: string) => choseIcon(typeStr)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -175,7 +287,6 @@ import { Rule } from 'ant-design-vue/lib/form';
|
||||||
import { isNoCommunity } from '@/utils/utils';
|
import { isNoCommunity } from '@/utils/utils';
|
||||||
import { onlyMessage } from '@/utils/comm';
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
|
||||||
|
|
||||||
const permission = 'system/Menu';
|
const permission = 'system/Menu';
|
||||||
// 路由
|
// 路由
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -191,7 +302,7 @@ const basicFormRef = ref<FormInstance>();
|
||||||
const permissFormRef = ref<FormInstance>();
|
const permissFormRef = ref<FormInstance>();
|
||||||
const uploadIcon = ref<FormInstance>();
|
const uploadIcon = ref<FormInstance>();
|
||||||
//菜单应用选项
|
//菜单应用选项
|
||||||
const appOptions = ref<any>([])
|
const appOptions = ref<any>([]);
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
data: {
|
data: {
|
||||||
name: '',
|
name: '',
|
||||||
|
@ -216,7 +327,9 @@ const form = reactive({
|
||||||
getMenuInfo_api(routeParams.id).then((resp: any) => {
|
getMenuInfo_api(routeParams.id).then((resp: any) => {
|
||||||
form.data = {
|
form.data = {
|
||||||
...(resp.result as formType),
|
...(resp.result as formType),
|
||||||
permissions: resp.result?.permissions ? resp.result.permissions : [],
|
permissions: resp.result?.permissions
|
||||||
|
? resp.result.permissions
|
||||||
|
: [],
|
||||||
accessSupport:
|
accessSupport:
|
||||||
resp.result?.accessSupport?.value || 'unsupported',
|
resp.result?.accessSupport?.value || 'unsupported',
|
||||||
};
|
};
|
||||||
|
@ -224,25 +337,34 @@ const form = reactive({
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isNoCommunity) {
|
if (isNoCommunity) {
|
||||||
// 获取关联菜单
|
// 获取关联菜单
|
||||||
getMenuTree_api({ paging: false,terms:[{terms:[{
|
getMenuTree_api({
|
||||||
terms:[
|
paging: false,
|
||||||
{
|
terms: [
|
||||||
value:"%show\":true%",
|
{
|
||||||
termType:"like",
|
terms: [
|
||||||
column:"options"
|
{
|
||||||
}
|
terms: [
|
||||||
]
|
{
|
||||||
}]}]}).then((resp: any) => {
|
value: '%show":true%',
|
||||||
form.treeData = resp.result;
|
termType: 'like',
|
||||||
});
|
column: 'options',
|
||||||
// 获取资产类型
|
},
|
||||||
getAssetsType_api().then((resp: any) => {
|
],
|
||||||
form.assetsType = resp.result.map((item: any) => ({
|
},
|
||||||
label: item.name,
|
],
|
||||||
value: item.id,
|
},
|
||||||
}));
|
],
|
||||||
});
|
}).then((resp: any) => {
|
||||||
|
form.treeData = resp.result;
|
||||||
|
});
|
||||||
|
// 获取资产类型
|
||||||
|
getAssetsType_api().then((resp: any) => {
|
||||||
|
form.assetsType = resp.result.map((item: any) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
}));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
checkCode: async (_rule: Rule, value: string): Promise<any> => {
|
checkCode: async (_rule: Rule, value: string): Promise<any> => {
|
||||||
|
@ -270,7 +392,7 @@ const form = reactive({
|
||||||
const api = routeParams.id ? saveMenuInfo_api : addMenuInfo_api;
|
const api = routeParams.id ? saveMenuInfo_api : addMenuInfo_api;
|
||||||
form.saveLoading = true;
|
form.saveLoading = true;
|
||||||
const accessSupportValue = form.data.accessSupport;
|
const accessSupportValue = form.data.accessSupport;
|
||||||
const params:any = {
|
const params: any = {
|
||||||
...form.data,
|
...form.data,
|
||||||
owner: 'iot',
|
owner: 'iot',
|
||||||
options: { show: true },
|
options: { show: true },
|
||||||
|
@ -280,8 +402,8 @@ const form = reactive({
|
||||||
accessSupportValue === 'unsupported'
|
accessSupportValue === 'unsupported'
|
||||||
? '不支持'
|
? '不支持'
|
||||||
: accessSupportValue === 'support'
|
: accessSupportValue === 'support'
|
||||||
? '支持'
|
? '支持'
|
||||||
: '间接控制',
|
: '间接控制',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
api(params)
|
api(params)
|
||||||
|
@ -302,15 +424,20 @@ const form = reactive({
|
||||||
})
|
})
|
||||||
.finally(() => (form.saveLoading = false));
|
.finally(() => (form.saveLoading = false));
|
||||||
})
|
})
|
||||||
.catch((err) => { });
|
.catch((err) => {});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
form.init();
|
form.init();
|
||||||
|
|
||||||
|
const checkCh = async (_rule: Rule, value: string) => {
|
||||||
|
if (/[\u4e00-\u9fa5]/.test(value))
|
||||||
|
return Promise.reject('编码不能包含中文');
|
||||||
|
else return Promise.resolve('');
|
||||||
|
};
|
||||||
const choseIcon = (typeStr: string) => {
|
const choseIcon = (typeStr: string) => {
|
||||||
form.data.icon = typeStr;
|
form.data.icon = typeStr;
|
||||||
uploadIcon.value?.clearValidate();
|
uploadIcon.value?.clearValidate();
|
||||||
}
|
};
|
||||||
// 弹窗
|
// 弹窗
|
||||||
const dialogVisible = ref(false);
|
const dialogVisible = ref(false);
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
:menu-info="menuInfo"
|
:menu-info="menuInfo"
|
||||||
:mode="dialogTitle"
|
:mode="dialogTitle"
|
||||||
:data="selectItem"
|
:data="selectItem"
|
||||||
|
:menuData="table.tableData"
|
||||||
@confirm="table.getList"
|
@confirm="table.getList"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -84,8 +85,6 @@ const openDialog = (mode: '查看' | '新增' | '编辑', row: object) => {
|
||||||
if (!routeParams.id && !paramsId.value) {
|
if (!routeParams.id && !paramsId.value) {
|
||||||
return onlyMessage('请先新增菜单基本信息', 'warning');
|
return onlyMessage('请先新增菜单基本信息', 'warning');
|
||||||
}
|
}
|
||||||
console.log(3);
|
|
||||||
|
|
||||||
selectItem.value = { ...row };
|
selectItem.value = { ...row };
|
||||||
dialogTitle.value = mode;
|
dialogTitle.value = mode;
|
||||||
dialogVisible.value = true;
|
dialogVisible.value = true;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
:rules="[
|
:rules="[
|
||||||
{ required: true, message: '请输入编码' },
|
{ required: true, message: '请输入编码' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
{ validator: validateIdRepeat, trigger: 'blur' },
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<j-auto-complete
|
<j-auto-complete
|
||||||
|
@ -59,12 +60,12 @@
|
||||||
max-height="350px"
|
max-height="350px"
|
||||||
v-model:value="form.data.permissions"
|
v-model:value="form.data.permissions"
|
||||||
:disabled="props.mode === '查看'"
|
:disabled="props.mode === '查看'"
|
||||||
:btnId="form.data.id "
|
:btnId="form.data.id"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item label="说明" name="describe">
|
<j-form-item label="说明" name="description">
|
||||||
<j-textarea
|
<j-textarea
|
||||||
v-model:value="form.data.describe"
|
v-model:value="form.data.description"
|
||||||
:rows="4"
|
:rows="4"
|
||||||
placeholder="请输入说明"
|
placeholder="请输入说明"
|
||||||
:disabled="props.mode === '查看'"
|
:disabled="props.mode === '查看'"
|
||||||
|
@ -90,6 +91,10 @@ const props = defineProps<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
mode: '查看' | '新增' | '编辑';
|
mode: '查看' | '新增' | '编辑';
|
||||||
data: formType;
|
data: formType;
|
||||||
|
menuData: {
|
||||||
|
type: Array<any>;
|
||||||
|
default: [];
|
||||||
|
};
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
@ -126,7 +131,7 @@ const initForm = {
|
||||||
name: '',
|
name: '',
|
||||||
id: '',
|
id: '',
|
||||||
permissions: [],
|
permissions: [],
|
||||||
describe: '',
|
description: '',
|
||||||
} as formType;
|
} as formType;
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
|
@ -141,12 +146,24 @@ const codeOptions = [
|
||||||
{ label: 'delete', value: 'delete', message: '删除' },
|
{ label: 'delete', value: 'delete', message: '删除' },
|
||||||
{ label: 'update', value: 'update', message: '更新' },
|
{ label: 'update', value: 'update', message: '更新' },
|
||||||
];
|
];
|
||||||
|
const validateIdRepeat = (rule: any, val: any) => {
|
||||||
|
if (props.mode === '编辑') {
|
||||||
|
return Promise.resolve('');
|
||||||
|
}
|
||||||
|
const isRepeat = props.menuData.find((i: any) => {
|
||||||
|
return i.id === val;
|
||||||
|
});
|
||||||
|
if (isRepeat) {
|
||||||
|
return Promise.reject('编码重复');
|
||||||
|
} else {
|
||||||
|
return Promise.resolve('');
|
||||||
|
}
|
||||||
|
};
|
||||||
type formType = {
|
type formType = {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
permissions: any[];
|
permissions: any[];
|
||||||
describe: string;
|
description: string;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,9 @@
|
||||||
v-model:checked="rowItem.checkAll"
|
v-model:checked="rowItem.checkAll"
|
||||||
:indeterminate="rowItem.indeterminate"
|
:indeterminate="rowItem.indeterminate"
|
||||||
@change="() => permission.selectAllOpions(rowItem)"
|
@change="() => permission.selectAllOpions(rowItem)"
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled || rowItem.disabled"
|
||||||
>
|
>
|
||||||
{{ rowItem.name }}
|
{{ rowItem.name }}
|
||||||
</j-checkbox>
|
</j-checkbox>
|
||||||
</j-col>
|
</j-col>
|
||||||
<j-col :span="24 - props.firstWidth">
|
<j-col :span="24 - props.firstWidth">
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
v-model:value="rowItem.checkedList"
|
v-model:value="rowItem.checkedList"
|
||||||
:options="rowItem.options"
|
:options="rowItem.options"
|
||||||
@change="((checkValue:string[])=>permission.selectOption(rowItem, checkValue))"
|
@change="((checkValue:string[])=>permission.selectOption(rowItem, checkValue))"
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled || rowItem.disabled"
|
||||||
/>
|
/>
|
||||||
</j-col>
|
</j-col>
|
||||||
</j-row>
|
</j-row>
|
||||||
|
@ -177,6 +177,7 @@ const permission = reactive({
|
||||||
checked.actions.length < item.actions.length) ||
|
checked.actions.length < item.actions.length) ||
|
||||||
false,
|
false,
|
||||||
options,
|
options,
|
||||||
|
disabled: item.status === 0,
|
||||||
};
|
};
|
||||||
}) as permissionType[];
|
}) as permissionType[];
|
||||||
|
|
||||||
|
@ -192,6 +193,7 @@ type permissionType = {
|
||||||
checkAll: boolean;
|
checkAll: boolean;
|
||||||
indeterminate: boolean;
|
indeterminate: boolean;
|
||||||
options: any[];
|
options: any[];
|
||||||
|
disabled: boolean;
|
||||||
};
|
};
|
||||||
type paramsType = {
|
type paramsType = {
|
||||||
paging: boolean;
|
paging: boolean;
|
||||||
|
|
|
@ -108,7 +108,7 @@
|
||||||
</j-button>
|
</j-button>
|
||||||
</div>
|
</div>
|
||||||
<j-monaco-editor
|
<j-monaco-editor
|
||||||
v-if="refStr"
|
v-if=" method !=='get' && method !=='patch'"
|
||||||
ref="editorRef"
|
ref="editorRef"
|
||||||
language="json"
|
language="json"
|
||||||
style="height: 100% ; min-height: 200px;"
|
style="height: 100% ; min-height: 200px;"
|
||||||
|
@ -143,6 +143,7 @@ const props = defineProps<{
|
||||||
const responsesContent = ref({});
|
const responsesContent = ref({});
|
||||||
const editorRef = ref();
|
const editorRef = ref();
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
|
const method = ref()
|
||||||
const requestBody = reactive({
|
const requestBody = reactive({
|
||||||
tableColumns: [
|
tableColumns: [
|
||||||
{
|
{
|
||||||
|
@ -199,9 +200,10 @@ let schema: any = {};
|
||||||
const refStr = ref('');
|
const refStr = ref('');
|
||||||
|
|
||||||
const init = () => {
|
const init = () => {
|
||||||
if (!props.selectApi.requestBody) return;
|
method.value = props.selectApi.method
|
||||||
schema = props.selectApi.requestBody.content['application/json'].schema;
|
// if (!props.selectApi.requestBody) return;
|
||||||
refStr.value = schema.$ref || schema?.items?.$ref;
|
// schema = props.selectApi.requestBody.content['application/json'].schema;
|
||||||
|
// refStr.value = schema.$ref || schema?.items?.$ref;
|
||||||
};
|
};
|
||||||
init();
|
init();
|
||||||
|
|
||||||
|
|
|
@ -142,14 +142,16 @@
|
||||||
<j-form-item
|
<j-form-item
|
||||||
name="username"
|
name="username"
|
||||||
label="用户名"
|
label="用户名"
|
||||||
|
:validateFirst="true"
|
||||||
:rules="[
|
:rules="[
|
||||||
{ required: true, message: '' },
|
{ required: true, message: '请输入用户名' },
|
||||||
|
{
|
||||||
|
validator: checkCh,
|
||||||
|
trigger: ['change', 'blur'],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
validator: form.rules.checkUserName,
|
validator: form.rules.checkUserName,
|
||||||
trigger: 'blur',
|
trigger: 'blur',
|
||||||
},{
|
|
||||||
validator: form.rules.checkCh,
|
|
||||||
trigger: 'change'
|
|
||||||
}
|
}
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
|
@ -267,15 +269,9 @@ const form = reactive({
|
||||||
data: {} as formType,
|
data: {} as formType,
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
checkCh: (_rule:Rule,value:string): Promise<any> =>
|
|
||||||
new Promise((resolve,reject) => {
|
|
||||||
if (/[\u4e00-\u9fa5]/.test(value)) return reject('用户名不能包含中文');
|
|
||||||
else return resolve('')
|
|
||||||
}),
|
|
||||||
checkUserName: (_rule: Rule, value: string): Promise<any> =>
|
checkUserName: (_rule: Rule, value: string): Promise<any> =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
if (props.type === 'edit') return resolve('');
|
if (props.type === 'edit') return resolve('');
|
||||||
if (!value) return reject('请输入用户名');
|
|
||||||
else if (value.length > 64) return reject('最多可输入64个字符');
|
else if (value.length > 64) return reject('最多可输入64个字符');
|
||||||
validateField_api('username', value).then((resp: any): any => {
|
validateField_api('username', value).then((resp: any): any => {
|
||||||
resp.result.passed
|
resp.result.passed
|
||||||
|
@ -397,7 +393,10 @@ const form = reactive({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const checkCh = async(_rule:Rule,value:string) => {
|
||||||
|
if (/[\u4e00-\u9fa5]/.test(value)) return Promise.reject('用户名不能包含中文');
|
||||||
|
else return Promise.resolve('')
|
||||||
|
}
|
||||||
const dealRoleList = (data:any) =>{
|
const dealRoleList = (data:any) =>{
|
||||||
return data.map((item:any)=>{
|
return data.map((item:any)=>{
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -118,7 +118,7 @@
|
||||||
<div class="other-button">
|
<div class="other-button">
|
||||||
<div
|
<div
|
||||||
class='other-button-item'
|
class='other-button-item'
|
||||||
v-for="(item, index) in bindings"
|
v-for="(item, index) in bindings.slice(0,4)"
|
||||||
:key="index"
|
:key="index"
|
||||||
@click="handleClickOther(item)"
|
@click="handleClickOther(item)"
|
||||||
>
|
>
|
||||||
|
@ -133,6 +133,9 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="more" v-if="bindings.length > 4" @click="moreVisible = true">
|
||||||
|
查看更多
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -158,6 +161,31 @@
|
||||||
</div>
|
</div>
|
||||||
</j-spin>
|
</j-spin>
|
||||||
</div>
|
</div>
|
||||||
|
<j-modal
|
||||||
|
title="更多登录"
|
||||||
|
:visible="moreVisible"
|
||||||
|
@cancel="() => (moreVisible = false)"
|
||||||
|
:footer="null"
|
||||||
|
:width="800"
|
||||||
|
>
|
||||||
|
<div class="more-button">
|
||||||
|
<div
|
||||||
|
class="more-button-item"
|
||||||
|
v-for="(item, index) in bindings"
|
||||||
|
:key="index"
|
||||||
|
@click="handleClickOther(item)"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
style="width: 100px; height: 100px"
|
||||||
|
:alt="item.name"
|
||||||
|
:src="item.logoUrl"
|
||||||
|
/>
|
||||||
|
<Ellipsis style="margin-top: 5px; width:calc(100% - 40px); margin: 0 auto">
|
||||||
|
{{ item.name }}
|
||||||
|
</Ellipsis>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -189,7 +217,7 @@ const viewLogo = getImage('/view-logo.png');
|
||||||
const LoginWarpStyle = reactive({
|
const LoginWarpStyle = reactive({
|
||||||
backgroundImage: `url(${bgImage})`,
|
backgroundImage: `url(${bgImage})`,
|
||||||
});
|
});
|
||||||
|
const moreVisible = ref(false)
|
||||||
const screenWidth = ref(document.body.clientWidth);
|
const screenWidth = ref(document.body.clientWidth);
|
||||||
const screenHeight = ref(document.body.clientHeight);
|
const screenHeight = ref(document.body.clientHeight);
|
||||||
|
|
||||||
|
@ -515,6 +543,10 @@ onMounted(()=>{
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
.more{
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.prefixIcon {
|
.prefixIcon {
|
||||||
|
@ -601,4 +633,15 @@ onMounted(()=>{
|
||||||
margin-top: 12% !important;
|
margin-top: 12% !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.more-button {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
cursor: pointer;
|
||||||
|
.more-button-item {
|
||||||
|
width: 18%;
|
||||||
|
margin-left: 2%;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue