fix: 修改查询组件引入方式;修改opc_ua写入失败

* fix: 14203、14491、9658、13663、14207、14226、14493

* fix: 修改bug(14733、14793、13672、11767)

* fix: 修改物模型映射样式

* fix: 修改opc_ua写入

* fix: 修改场景联动关系用户名称回显的问题

* fix: 修改查询组件引入方式

* fix: 修改opc_ua写入失败
This commit is contained in:
XieYongHong 2023-06-01 11:58:02 +08:00 committed by GitHub
parent c669985f54
commit fbc902aa14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 449 additions and 133 deletions

View File

@ -25,7 +25,7 @@
"event-source-polyfill": "^1.0.31", "event-source-polyfill": "^1.0.31",
"global": "^4.4.0", "global": "^4.4.0",
"jetlinks-store": "^0.0.3", "jetlinks-store": "^0.0.3",
"jetlinks-ui-components": "^1.0.18", "jetlinks-ui-components": "^1.0.21",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"less": "^4.1.3", "less": "^4.1.3",
"less-loader": "^11.1.0", "less-loader": "^11.1.0",

View File

@ -55,3 +55,6 @@ export const scanOpcUAList = (data: any) =>
data?.nodeId || '' data?.nodeId || ''
}`, }`,
); );
export const queryTypeList = () => server.get(`/data-collect/opc/data-types`);

View File

@ -1,7 +1,9 @@
<template> <template>
<div class='full-page-warp' ref='fullPage' :style='{ minHeight: `calc(100vh - ${y + 24}px)`}'> <div class='full-page-warp' ref='fullPage' :style='{ minHeight: `calc(100vh - ${y + 24}px)`}'>
<div class="full-page-warp-content">
<slot></slot> <slot></slot>
</div> </div>
</div>
</template> </template>
<script setup lang='ts' name='FullPage'> <script setup lang='ts' name='FullPage'>
@ -12,8 +14,13 @@ const { y } = useElementBounding(fullPage)
</script> </script>
<style scoped> <style scoped lang="less">
.full-page-warp { .full-page-warp {
background: #fff; background: #fff;
display: flex;
.full-page-warp-content {
width: 100%;
} }
}
</style> </style>

View File

@ -114,11 +114,11 @@ const handleAdd = () => {
const validateIndicator = (value: any) => { const validateIndicator = (value: any) => {
if (value?.range) { if (value?.range) {
if (!value?.value || !value?.value[0] || !value?.value[1]) { if (!value?.value || (!value?.value[0] && value?.value[0] !== 0) || (!value?.value[1] && value?.value[1] !== 0)) {
return Promise.reject(new Error('请输入指标值')); return Promise.reject(new Error('请输入指标值'));
} }
} else { } else {
if (!value?.value) { if (!value?.value && value?.value !== 0) {
return Promise.reject(new Error('请输入指标值')); return Promise.reject(new Error('请输入指标值'));
} }
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<AdvancedSearch <j-advanced-search
:target='target' :target='target'
:type='type' :type='type'
:request='saveSearchHistory' :request='saveSearchHistory'
@ -16,7 +16,6 @@
import { PropType } from 'vue' import { PropType } from 'vue'
import { JColumnsProps } from 'components/Table/types' import { JColumnsProps } from 'components/Table/types'
import { saveSearchHistory, getSearchHistory, deleteSearchHistory } from '@/api/comm' import { saveSearchHistory, getSearchHistory, deleteSearchHistory } from '@/api/comm'
import { AdvancedSearch } from 'jetlinks-ui-components'
interface Emit { interface Emit {
(e: 'search', data: any): void (e: 'search', data: any): void

View File

@ -111,12 +111,12 @@
<j-form-item <j-form-item
v-if="isSecurityMode" v-if="isSecurityMode"
label="证书" label="证书"
:name="['configuration', 'certificate']" :name="['configuration', 'certId']"
:rules="FormValidate.certificate" :rules="FormValidate.certId"
> >
<j-select <j-select
style="width: 100%" style="width: 100%"
v-model:value="formData.configuration.certificate" v-model:value="formData.configuration.certId"
:options="certificateList" :options="certificateList"
placeholder="请选择证书" placeholder="请选择证书"
allowClear allowClear

View File

@ -11,7 +11,7 @@ export const FormState: FormDataType = {
endpoint: '', endpoint: '',
securityPolicy: 'None', securityPolicy: 'None',
securityMode: undefined, securityMode: undefined,
certificate: undefined, certId: undefined,
authType: 'anonymous', authType: 'anonymous',
username: '', username: '',
password: '', password: '',
@ -87,7 +87,7 @@ export const FormValidate = {
}, },
{ {
validator: checkEndpoint, validator: checkEndpoint,
// trigger: 'blur', trigger: 'blur',
}, },
], ],
@ -103,7 +103,7 @@ export const FormValidate = {
message: '请选择安全模式', message: '请选择安全模式',
}, },
], ],
certificate: [ certId: [
{ {
required: true, required: true,
message: '请选择证书', message: '请选择证书',

View File

@ -6,7 +6,7 @@ export interface ConfigurationType {
endpoint: string, endpoint: string,
securityPolicy: string | undefined, securityPolicy: string | undefined,
securityMode: string | undefined, securityMode: string | undefined,
certificate: string | undefined, certId: string | undefined,
authType: string | undefined, authType: string | undefined,
} }

View File

@ -19,13 +19,15 @@
<j-select <j-select
style="width: 100%" style="width: 100%"
v-model:value="formData.configuration.type" v-model:value="formData.configuration.type"
:options="[ :options="options
{ value: 'Number', label: '数值类型' }, // [
{ value: 'DateTime', label: '时间类型' }, // { value: 'Number', label: '' },
{ value: 'Array', label: '数组类型' }, // { value: 'DateTime', label: '' },
{ value: 'String', label: '文本类型' }, // { value: 'Array', label: '' },
{ value: 'Boolean', label: '布尔' }, // { value: 'String', label: '' },
]" // { value: 'Boolean', label: '' },
// ]
"
placeholder="请选择数据类型" placeholder="请选择数据类型"
allowClear allowClear
show-search show-search
@ -98,6 +100,7 @@ import {
savePoint, savePoint,
updatePoint, updatePoint,
_validateField, _validateField,
queryTypeList
} from '@/api/data-collect/collector'; } from '@/api/data-collect/collector';
import { OPCUARules } from '../../data.ts'; import { OPCUARules } from '../../data.ts';
import type { FormInstance } from 'ant-design-vue'; import type { FormInstance } from 'ant-design-vue';
@ -118,6 +121,7 @@ const formRef = ref<FormInstance>();
const id = props.data.id; const id = props.data.id;
const collectorId = props.data.collectorId; const collectorId = props.data.collectorId;
const provider = props.data.provider; const provider = props.data.provider;
const options = ref([]);
const formData = ref({ const formData = ref({
name: '', name: '',
@ -157,6 +161,19 @@ const filterOption = (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0; return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
}; };
onMounted(() => {
queryTypeList().then((resp: any) => {
if(resp.status === 200){
options.value = (resp?.result || []).map((item: any) => {
return {
label: item,
value: item
}
})
}
})
})
watch( watch(
() => props.data, () => props.data,
(value) => { (value) => {

View File

@ -130,6 +130,7 @@ const onCheck = (checkedKeys: any, info: any) => {
value: last ? last?.accessModes?.value : one?.accessModes || [], value: last ? last?.accessModes?.value : one?.accessModes || [],
check: true, check: true,
}, },
type: one?.type,
configuration: { configuration: {
...one?.configuration, ...one?.configuration,
interval: { interval: {

View File

@ -65,6 +65,7 @@ const handleOk = async () => {
pointKey: item.id, pointKey: item.id,
configuration: { configuration: {
interval: item.configuration?.interval?.value, interval: item.configuration?.interval?.value,
type: item.type,
}, },
features: !item.features?.value ? [] : ['changedOnly'], features: !item.features?.value ? [] : ['changedOnly'],
accessModes: item.accessModes?.value || [], accessModes: item.accessModes?.value || [],

View File

@ -30,6 +30,57 @@
showCount showCount
/> />
</j-form-item> </j-form-item>
<j-form-item
:label="data.name"
name="value"
:rules="[
{
required: true,
message: `请输入${data.name}`,
},
]"
v-else-if="data.provider === 'OPC_UA'"
>
<j-input-number
v-if="['double', 'float', 'llong', 'long', 'integer', 'short'].includes(valueType)"
style="width: 100%"
placeholder="请输入"
v-model:value="formData.value"
/>
<j-select
v-else-if="['boolean'].includes(valueType)"
style="width: 100%"
v-model:value="formData.value"
:options="[
{
label: '是',
value: true,
},
{
label: '否',
value: false,
},
]"
placeholder="请选择"
allowClear
show-search
:filter-option="filterOption"
/>
<j-date-picker
v-else-if="['datetime'].includes(valueType)"
style="width: 100%"
format="YYYY-MM-DD HH:mm:ss"
show-time
placeholder="请选择"
@change="onChange"
/>
<j-input
v-else
placeholder="请输入"
v-model:value="formData.value"
/>
</j-form-item>
<j-form-item <j-form-item
:label="data.name" :label="data.name"
name="value" name="value"
@ -111,7 +162,7 @@ const props = defineProps({
const valueType: string = ( const valueType: string = (
props.data?.provider === 'OPC_UA' props.data?.provider === 'OPC_UA'
? props?.data?.configuration?.type || 'Number' ? props?.data?.configuration?.type || 'String'
: props.data?.configuration?.codec?.provider || 'int8' : props.data?.configuration?.codec?.provider || 'int8'
).toLocaleLowerCase(); ).toLocaleLowerCase();

View File

@ -218,7 +218,18 @@ const handleSearch = async (value: any) => {
} else if (!!searchValue.value) { } else if (!!searchValue.value) {
clickSearch = true; clickSearch = true;
params.value = { ..._.cloneDeep(defualtParams) }; params.value = { ..._.cloneDeep(defualtParams) };
params.value.terms[1] = { // params.value.terms[1] = {
// terms: [
// {
// column: 'name',
// value: `%${searchValue.value}%`,
// termType: 'like',
// },
// ],
// };
params.value.terms = [
...(params.value?.terms || []),
{
terms: [ terms: [
{ {
column: 'name', column: 'name',
@ -226,7 +237,8 @@ const handleSearch = async (value: any) => {
termType: 'like', termType: 'like',
}, },
], ],
}; }
];
} else { } else {
!!value && (params.value = value); !!value && (params.value = value);
} }

View File

@ -46,7 +46,7 @@
:rules="[{ required: true, message: '请选择告警规则' }]" :rules="[{ required: true, message: '请选择告警规则' }]"
> >
<j-select <j-select
:value="form.topicConfig.alarmConfigId?.split(',')" :value="form.topicConfig?.alarmConfigId?.split(',')"
:options="alarmList" :options="alarmList"
placeholder="请选择告警规则" placeholder="请选择告警规则"
mode="multiple" mode="multiple"
@ -146,8 +146,8 @@ function init() {
function onSelect(keys: string[], items: optionsType) { function onSelect(keys: string[], items: optionsType) {
form.value.topicConfig = { form.value.topicConfig = {
alarmConfigId: keys.join(','), alarmConfigId: keys.length ? keys.join(',') : undefined,
alarmConfigName: items.map((item) => item.label).join(','), alarmConfigName: items.length ? items.map((item) => item.label).join(',') : undefined,
}; };
} }
</script> </script>

View File

@ -53,6 +53,6 @@ const findName = (item: any) => {
const _element = item.dataType.elements?.find((a: any) => a.value === item.value) const _element = item.dataType.elements?.find((a: any) => a.value === item.value)
name = _element?.text name = _element?.text
} }
return name return name || item.value
} }
</script> </script>

View File

@ -198,20 +198,24 @@ getDefaultMetadata()
<style scoped lang='less'> <style scoped lang='less'>
.metadata-map { .metadata-map {
position: relative; // position: relative;
min-height: 100%; min-height: 100%;
display: flex;
gap: 24px;
.left { .left {
margin-right: 424px; // margin-right: 44px;
flex: 1;
} }
.right { .right {
position: absolute; // position: absolute;
border: 1px solid rgba(0, 0, 0, 0.08); border: 1px solid rgba(0, 0, 0, 0.08);
height: 100%; min-height: 100%;
width: 400px; min-width: 410px;
top: 0; width: 33.33333%;
right: 0; // top: 0;
// right: 0;
padding: 16px; padding: 16px;
.title { .title {

View File

@ -91,7 +91,7 @@
> >
</template> </template>
<FullPage> <FullPage>
<j-card :bordered="false"> <div style="height: 100%; padding: 24px;">
<component <component
:is="tabs[productStore.tabActiveKey]" :is="tabs[productStore.tabActiveKey]"
:class=" :class="
@ -101,7 +101,7 @@
" "
v-bind="{ type: 'product' }" v-bind="{ type: 'product' }"
/> />
</j-card> </div>
</FullPage> </FullPage>
</page-container> </page-container>
</template> </template>

View File

@ -47,10 +47,10 @@
</template> </template>
<template v-if="modelType === 'tags'"> <template v-if="modelType === 'tags'">
<value-type-form :name="['valueType']" v-model:value="value.valueType" key="property" title="数据类型"></value-type-form> <value-type-form :name="['valueType']" v-model:value="value.valueType" key="property" title="数据类型"></value-type-form>
<j-form-item label="标签类型" :name="['expands', 'type']" :rules="[ <j-form-item label="读写类型" :name="['expands', 'type']" :rules="[
{ required: true, message: '请选择标签类型' }, { required: true, message: '请选择读写类型' },
]"> ]">
<j-select v-model:value="value.expands.type" :options="ExpandsTypeList" mode="multiple" size="small" placeholder="请选择标签类型"></j-select> <j-select v-model:value="value.expands.type" :options="ExpandsTypeList" mode="multiple" size="small" placeholder="请选择读写类型"></j-select>
</j-form-item> </j-form-item>
</template> </template>
<j-form-item label="说明" name="description" :rules="[ <j-form-item label="说明" name="description" :rules="[

View File

@ -0,0 +1,73 @@
<template>
<j-select
:value="host"
:options="options"
placeholder="请选择本地地址"
allowClear
show-search
:disabled="shareCluster"
@change="changeHost"
>
</j-select>
</template>
<script lang="ts" setup>
import { Store } from "jetlinks-store"
import { resourceClustersById } from "@/api/link/type"
const props = defineProps({
value: {
type: String,
default: undefined
},
shareCluster: {
type: Boolean,
default: true
},
serverId: {
type: String,
default: undefined
}
})
const emit = defineEmits(['update:value', 'change', 'valueChange'])
const host = ref<string>()
const options = ref<any[]>([])
const getResourcesClustersById = async (id: string) => {
const _value = Store.get('resourcesClusters')?.[id]
if(!_value){
const resp = await resourceClustersById(id)
if (resp.status === 200) {
const checked = resp.result?.[0]
const checkedHost = [{ value: checked?.host, label: checked?.host }];
options.value = checked ? checkedHost : []
const resourcesClusters = Store.get('resourcesClusters') || {}
resourcesClusters[id] = resp.result
Store.set('resourcesClusters', resourcesClusters)
emit('valueChange', props.value)
} else {
options.value = []
}
} else {
const checked = Store.get('resourcesClusters')?.[id]?.[0]
const checkedHost = [{ value: checked?.host, label: checked?.host }];
options.value = checked ? checkedHost : []
}
}
watchEffect(() => {
host.value = props.value
if(!props.shareCluster && props.serverId){
getResourcesClustersById(props.serverId)
}
emit('valueChange', props.value)
})
const changeHost = (_value: string) => {
emit('update:value', _value)
emit('change', props.value)
}
</script>

View File

@ -209,7 +209,7 @@
/> />
</j-tooltip> </j-tooltip>
</template> </template>
<j-select <!-- <j-select
v-model:value=" v-model:value="
cluster cluster
.configuration .configuration
@ -237,7 +237,14 @@
) )
" "
> >
</j-select> </j-select> -->
<LocalAddressSelect
v-model:value="cluster.configuration.host"
:serverId="cluster.serverId"
:shareCluster="shareCluster"
@change="(value) => changeHost(cluster.serverId, value, index)"
@valueChange="(value) => changeHost(cluster.serverId, value, index, true)"
/>
</j-form-item> </j-form-item>
</j-col> </j-col>
<j-col <j-col
@ -1112,6 +1119,8 @@ import {
supports, supports,
certificates, certificates,
start, start,
resourceClusters,
resourceClustersById,
} from '@/api/link/type'; } from '@/api/link/type';
import { import {
ParserConfiguration, ParserConfiguration,
@ -1129,6 +1138,7 @@ import {
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import type { FormData2Type, FormDataType } from '../type'; import type { FormData2Type, FormDataType } from '../type';
import { Store } from 'jetlinks-store'; import { Store } from 'jetlinks-store';
import LocalAddressSelect from './LocalAddressSelect.vue';
const route = useRoute(); const route = useRoute();
const NetworkType = route.query.type as string; const NetworkType = route.query.type as string;
@ -1147,8 +1157,9 @@ const hostOptionsIndex: any = ref([]);
const clustersListIndex: any = ref([]); const clustersListIndex: any = ref([]);
const typeOptions = ref([]); const typeOptions = ref([]);
const portOptionsIndex: any = ref([]); const portOptionsIndex: any = ref([]);
let portOptionsConst: any = []; // let portOptionsConst: any = [];
const certIdOptions = ref([]); const certIdOptions = ref([]);
const configClustersList = ref<any[]>([]);
const dynamicValidateForm = reactive<{ cluster: FormData2Type[] }>({ const dynamicValidateForm = reactive<{ cluster: FormData2Type[] }>({
cluster: [{ ...cloneDeep(FormStates2), id: '1' }], cluster: [{ ...cloneDeep(FormStates2), id: '1' }],
@ -1178,7 +1189,7 @@ const filterPortOption = (input: string, option: any) => {
return JSON.stringify(option.label).indexOf(input) >= 0; return JSON.stringify(option.label).indexOf(input) >= 0;
}; };
const filterConfigByType = (data: any, type: string) => { const filterConfigByType = (data: any[], type: string) => {
let _temp = type; let _temp = type;
if (TCPList.includes(type)) { if (TCPList.includes(type)) {
_temp = 'TCP'; _temp = 'TCP';
@ -1197,7 +1208,7 @@ const filterConfigByType = (data: any, type: string) => {
}); });
}; };
const getPortOptions = (portOptions: object, index = 0) => { const getPortOptions = (portOptions: any, index = 0) => {
if (!portOptions) return; if (!portOptions) return;
const type = formData.value.type; const type = formData.value.type;
const host = dynamicValidateForm.cluster[index].configuration.host; const host = dynamicValidateForm.cluster[index].configuration.host;
@ -1227,40 +1238,57 @@ const changeType = (value: string) => {
const updateClustersListIndex = () => { const updateClustersListIndex = () => {
const { cluster } = dynamicValidateForm; const { cluster } = dynamicValidateForm;
const filters = cluster?.map((item) => item.serverId); const filters = cluster?.map((item) => item.serverId);
const newConfigRef = Store.get('configRef')?.filter( const newConfigRef = shareCluster.value ? Store.get('configRef')?.filter(
(item: any) => !filters.includes(item.clusterNodeId), (item: any) => !filters.includes(item.clusterNodeId),
); ) : configClustersList.value?.filter(
(item: any) => !filters.includes(item.id),
)
cluster.forEach((item, index) => { cluster.forEach((item, index) => {
!item.serverId && clustersListIndex.value[index] = newConfigRef?.map((i: any) => {
(clustersListIndex.value[index] = newConfigRef?.map((i: any) => ({ if(shareCluster.value){
return {
value: i.clusterNodeId, value: i.clusterNodeId,
lable: i.clusterNodeId, label: i.clusterNodeId,
}))); }
} else {
return {
value: i.id,
label: i.name,
}
}
})
if(item.serverId) {
clustersListIndex.value[index].push({
value: item.serverId,
label: item.serverId
})
}
}); });
}; };
const changeServerId = (value: string | undefined, index: number) => { const changeServerId = async (value: string | undefined, index: number) => {
const { configuration } = dynamicValidateForm.cluster[index]; const { configuration } = dynamicValidateForm.cluster[index];
configuration.host = undefined; configuration.host = undefined;
configuration.port = undefined; configuration.port = undefined;
const checked = cloneDeep(portOptionsConst).find( hostOptionsIndex.value[index] = [];
(i: any) => i.clusterNodeId === value, if(value){
);
const checkedHost = [{ value: checked?.host, lable: checked?.host }];
hostOptionsIndex.value[index] = checked ? checkedHost : [];
updateClustersListIndex(); updateClustersListIndex();
}
}; };
const changeHost = ( const changeHost = (
serverId: string | undefined, serverId: string | undefined,
value: string | undefined, value: string | undefined,
index: number, index: number,
flag?: boolean
) => { ) => {
const { configuration } = dynamicValidateForm.cluster[index]; const { configuration } = dynamicValidateForm.cluster[index];
if(!flag){
configuration.port = undefined; configuration.port = undefined;
const checked = cloneDeep(portOptionsConst).find( }
(i: any) => i.clusterNodeId === serverId && i.host === value, const checked = Store.get('resourcesClusters')?.[serverId || '']
); if(checked){
checked && getPortOptions([checked], index); getPortOptions(checked, index)
}
}; };
const changeParserType = (value: string | undefined, index: number) => { const changeParserType = (value: string | undefined, index: number) => {
@ -1346,9 +1374,20 @@ const getCertificates = async () => {
const getResourcesCurrent = () => { const getResourcesCurrent = () => {
resourcesCurrent().then((resp) => { resourcesCurrent().then((resp) => {
if (resp.status === 200) { if (resp.status === 200) {
portOptionsConst = resp.result; const clusterNodeId = resp.result?.[0]?.clusterNodeId
const resourcesClusters = Store.get('resourcesClusters') || {}
resourcesClusters[clusterNodeId] = resp.result
Store.set('resourcesClusters', resourcesClusters)
Store.set('configRef', resp.result); Store.set('configRef', resp.result);
getPortOptions(portOptionsConst); getPortOptions(Store.get('resourcesClusters')?.[clusterNodeId]);
}
});
};
const getResourcesClusters = () => {
resourceClusters().then((resp) => {
if (resp.status === 200) {
configClustersList.value = resp.result as any[]
} }
}); });
}; };
@ -1369,22 +1408,24 @@ const getDetail = () => {
...configuration, ...configuration,
}; };
const configRef = Store.get('configRef').filter( // const configRef = Store.get('configRef').filter(
(item: any) => item.host === '0.0.0.0', // (item: any) => item.host === '0.0.0.0',
); // );
getPortOptions(configRef); // // getPortOptions(configRef); //
} else { } else {
dynamicValidateForm.cluster = cluster; dynamicValidateForm.cluster = cluster;
// const arr = cluster.map((item: any) => item.configuration.serverId)
// //
setTimeout(() => { // setTimeout(() => {
cluster.forEach((item: any, index: number) => { // cluster.forEach((item: any, index: number) => {
const { host } = item.configuration; // const { serverId } = item.configuration
let configRef = Store.get('configRef').filter( // if(!resourcesClustersMap.get(serverId)){
(item: any) => item.host === host, // // await getResourcesClustersById(serverId)
); // }
getPortOptions(configRef, index); // const checked = resourcesClustersMap.get(serverId)
}); // getPortOptions(checked, index);
}, 0); // });
// }, 0);
} }
if (dynamicValidateForm.cluster.length === 1) { if (dynamicValidateForm.cluster.length === 1) {
@ -1400,6 +1441,7 @@ onMounted(() => {
getSupports(); getSupports();
getCertificates(); getCertificates();
getResourcesCurrent(); getResourcesCurrent();
getResourcesClusters();
getDetail(); getDetail();
}); });

View File

@ -250,7 +250,7 @@ const getActions = (data: any) => {
message.success('操作成功!'); message.success('操作成功!');
instanceRef.value?.reload(); instanceRef.value?.reload();
} else { } else {
message.error('操作失败!'); message.error(resp?.message || '操作失败!');
} }
}, },
}, },

View File

@ -247,6 +247,8 @@ watch(
const validateChannelId = async (_rule: Rule, value: string) => { const validateChannelId = async (_rule: Rule, value: string) => {
// ID, ID, ID // ID, ID, ID
if (!value) return; if (!value) return;
//
if(!!formData.value?.id) return;
const { result } = await ChannelApi.validateField({ const { result } = await ChannelApi.validateField({
deviceId: route.query.id, deviceId: route.query.id,
channelId: value, channelId: value,

View File

@ -944,6 +944,7 @@ const formRules = {
} }
], ],
calledShowNumbers: [ calledShowNumbers: [
{ required: true, message: '请输入被叫显号' },
{ max: 64, message: '最多可输入64个字符' }, { max: 64, message: '最多可输入64个字符' },
{ {
validator(_rule: Rule, value: string) { validator(_rule: Rule, value: string) {

View File

@ -98,7 +98,9 @@ watchEffect(() => {
}); });
watchEffect(() => { watchEffect(() => {
if(props?.template?.template?.sendTo) {
emit('change', { sendTo: props?.template?.template?.sendTo?.join(' ') }); emit('change', { sendTo: props?.template?.template?.sendTo?.join(' ') });
}
}); });
const getType = (item: any) => { const getType = (item: any) => {

View File

@ -38,6 +38,7 @@ const props = defineProps<{
parentId: string; parentId: string;
allPermission: dictType; allPermission: dictType;
assetType: 'product' | 'device'; assetType: 'product' | 'device';
defaultPermission: string[];
}>(); }>();
// //
const loading = ref(false); const loading = ref(false);
@ -51,10 +52,12 @@ const confirm = () => {
}) })
.finally(() => (loading.value = false)); .finally(() => (loading.value = false));
}; };
const form = reactive({ const form = reactive({
assetIdList: [...props.ids], assetIdList: [...props.ids],
permission: [...props.permissionList], permission: Array.isArray(props.defaultPermission) && props.defaultPermission?.length ? props.defaultPermission : ['read'],
}); });
const options = computed(() => { const options = computed(() => {
const result: optionsType = []; const result: optionsType = [];
props.allPermission.forEach((item) => { props.allPermission.forEach((item) => {

View File

@ -13,8 +13,12 @@
:gridColumn="2" :gridColumn="2"
:params="queryParams" :params="queryParams"
:rowSelection="{ :rowSelection="{
// selectedRowKeys: table._selectedRowKeys.value,
// onChange:(keys:string[])=>table._selectedRowKeys.value = [...keys],
// onSelectNone: table.cancelSelect
selectedRowKeys: table._selectedRowKeys.value, selectedRowKeys: table._selectedRowKeys.value,
onChange:(keys:string[])=>table._selectedRowKeys.value = [...keys], onSelect: table.onSelect,
onSelectAll: table.onSelectAll,
onSelectNone: table.cancelSelect onSelectNone: table.cancelSelect
}" }"
:columns="columns" :columns="columns"
@ -204,6 +208,7 @@
:parent-id="props.parentId" :parent-id="props.parentId"
:all-permission="table.permissionList.value" :all-permission="table.permissionList.value"
asset-type="device" asset-type="device"
:defaultPermission="table.defaultPermission"
@confirm="table.refresh" @confirm="table.refresh"
/> />
</div> </div>
@ -222,6 +227,7 @@ import {
getPermissionDict_api, getPermissionDict_api,
unBindDeviceOrProduct_api, unBindDeviceOrProduct_api,
getDeviceProduct_api, getDeviceProduct_api,
getBindingsPermission,
} from '@/api/system/department'; } from '@/api/system/department';
import { intersection } from 'lodash-es'; import { intersection } from 'lodash-es';
@ -269,7 +275,11 @@ const columns = [
rename: 'productId$product-info', rename: 'productId$product-info',
type: 'select', type: 'select',
handleValue(value: string) { handleValue(value: string) {
return `id in ${value.toString()}`; return value && value.length ? [{
column: 'id',
termType: 'in',
value: `${value.toString()}`
}] : undefined;
}, },
options: () => options: () =>
new Promise((resolve) => { new Promise((resolve) => {
@ -338,6 +348,7 @@ const table = {
_selectedRowKeys: ref<string[]>([]), _selectedRowKeys: ref<string[]>([]),
selectedRows: [] as any[], selectedRows: [] as any[],
permissionList: ref<dictType>([]), permissionList: ref<dictType>([]),
defaultPermission: [] as string[],
init: () => { init: () => {
table.getPermissionDict(); table.getPermissionDict();
@ -408,6 +419,43 @@ const table = {
table._selectedRowKeys.value = []; table._selectedRowKeys.value = [];
table.selectedRows = []; table.selectedRows = [];
}, },
onSelect: (record: any, selected: boolean) => {
const arr = [...table._selectedRowKeys.value]
const _index = arr.findIndex(item => item === record?.id)
if (selected) {
if (!(_index > -1)) {
table._selectedRowKeys.value.push(record.id)
table.selectedRows.push(record)
}
} else {
if (_index > -1) { //
table._selectedRowKeys.value.splice(_index, 1)
table.selectedRows.splice(_index, 1)
}
}
},
onSelectAll: (selected: boolean, _: any[], changeRows: any) => {
if (selected) {
changeRows.map((i: any) => {
if (!table._selectedRowKeys.value.includes(i.id)) {
table._selectedRowKeys.value.push(i.id)
table.selectedRows.push(i)
}
})
} else {
const arr = changeRows.map((item: any) => item.id)
const _arr: string[] = [];
const _ids: string[] = [];
[...table.selectedRows].map((i: any) => {
if (!arr.includes(i?.id)) {
_arr.push(i)
_ids.push(i.id)
}
})
table._selectedRowKeys.value = _ids
table.selectedRows = _arr
}
},
// //
getData: (params: object, parentId: string) => getData: (params: object, parentId: string) =>
new Promise((resolve) => { new Promise((resolve) => {
@ -494,23 +542,27 @@ const table = {
departmentStore.setType(type); departmentStore.setType(type);
dialogs.addShow = true; dialogs.addShow = true;
}, },
clickEdit: (row?: any) => { queryPermissionList: async (ids: string[]) => {
const resp: any = await getBindingsPermission('device', ids)
if(resp.status === 200){
const arr = resp.result.map((item: any) => {
return item?.permissionInfoList?.map((i: any) => i?.id)
})
return intersection(...arr)
}
return []
},
clickEdit: async (row?: any) => {
const ids = row ? [row.id] : [...table._selectedRowKeys.value]; const ids = row ? [row.id] : [...table._selectedRowKeys.value];
if (row || table.selectedRows.length === 1) { if (ids.length < 1) return message.warning('请勾选需要编辑的数据');
const permissionList =
row?.permission || table.selectedRows[0].permission;
dialogs.selectIds = ids;
dialogs.permissList = permissionList;
dialogs.editShow = true;
return;
} else if (table.selectedRows.length === 0) return;
const permissionList = table.selectedRows.map(
(item) => item.permission,
);
const mixPermissionList = intersection(...permissionList) as string[];
table.defaultPermission = row ? row?.permission : intersection(...table.selectedRows.map(
(item) => item.permission,
)) as string[]
const _result = await table.queryPermissionList(ids)
dialogs.selectIds = ids; dialogs.selectIds = ids;
dialogs.permissList = mixPermissionList; dialogs.permissList = _result as string[];
dialogs.editShow = true; dialogs.editShow = true;
}, },
clickUnBind: (row?: any) => { clickUnBind: (row?: any) => {

View File

@ -14,7 +14,8 @@
:params="queryParams" :params="queryParams"
:rowSelection="{ :rowSelection="{
selectedRowKeys: tableData._selectedRowKeys, selectedRowKeys: tableData._selectedRowKeys,
onChange:(keys:string[])=>tableData._selectedRowKeys = [...keys], onSelect: table.onSelect,
onSelectAll: table.onSelectAll,
onSelectNone: table.cancelSelect onSelectNone: table.cancelSelect
}" }"
:columns="columns" :columns="columns"
@ -221,6 +222,7 @@
:parent-id="parentId" :parent-id="parentId"
:all-permission="tableData.permissionList" :all-permission="tableData.permissionList"
asset-type="product" asset-type="product"
:defaultPermission="tableData.defaultPermission"
@confirm="table.refresh" @confirm="table.refresh"
/> />
<NextDialog <NextDialog
@ -244,6 +246,7 @@ import {
getPermission_api, getPermission_api,
getPermissionDict_api, getPermissionDict_api,
unBindDeviceOrProduct_api, unBindDeviceOrProduct_api,
getBindingsPermission,
} from '@/api/system/department'; } from '@/api/system/department';
import { intersection } from 'lodash-es'; import { intersection } from 'lodash-es';
@ -328,6 +331,7 @@ const tableData = reactive({
_selectedRowKeys: [] as string[], _selectedRowKeys: [] as string[],
selectedRows: [] as any[], selectedRows: [] as any[],
permissionList: [] as any[], permissionList: [] as any[],
defaultPermission: [] as string[]
}); });
const table = { const table = {
init: () => { init: () => {
@ -384,23 +388,59 @@ const table = {
}, },
// //
onSelectChange: (row: any) => { onSelectChange: (row: any) => {
const selectedRowKeys = tableData._selectedRowKeys; const index = tableData._selectedRowKeys.indexOf(row.id);
const index = selectedRowKeys.indexOf(row.id);
if (index === -1) { if (index === -1) {
selectedRowKeys.push(row.id); tableData._selectedRowKeys.push(row.id);
tableData.selectedRows.push(row); tableData.selectedRows.push(row);
} else { } else {
selectedRowKeys.splice(index, 1); tableData._selectedRowKeys.splice(index, 1);
tableData.selectedRows.splice(index, 1); tableData.selectedRows.splice(index, 1);
} }
}, },
// //
cancelSelect: () => { cancelSelect: () => {
console.log(1111); // console.log(1111);
tableData._selectedRowKeys = []; tableData._selectedRowKeys = [];
tableData.selectedRows = []; tableData.selectedRows = [];
}, },
onSelect: (record: any, selected: boolean) => {
const arr = [...tableData._selectedRowKeys]
const _index = arr.findIndex(item => item === record?.id)
if (selected) {
if (!(_index > -1)) {
tableData._selectedRowKeys.push(record.id)
tableData.selectedRows.push(record)
}
} else {
if (_index > -1) { //
tableData._selectedRowKeys.splice(_index, 1)
tableData.selectedRows.splice(_index, 1)
}
}
},
onSelectAll: (selected: boolean, _: any[], changeRows: any) => {
if (selected) {
changeRows.map((i: any) => {
if (!tableData._selectedRowKeys.includes(i.id)) {
tableData._selectedRowKeys.push(i.id)
tableData.selectedRows.push(i)
}
})
} else {
const arr = changeRows.map((item: any) => item.id)
const _arr: string[] = [];
const _ids: string[] = [];
[...tableData.selectedRows].map((i: any) => {
if (!arr.includes(i?.id)) {
_arr.push(i)
_ids.push(i.id)
}
})
tableData._selectedRowKeys = _ids
tableData.selectedRows = _arr
}
},
// //
getData: (params: object, parentId: string) => getData: (params: object, parentId: string) =>
new Promise((resolve) => { new Promise((resolve) => {
@ -495,25 +535,25 @@ const table = {
}; };
} }
}, },
clickEdit: (row?: any) => { queryPermissionList: async (ids: string[]) => {
const resp: any = await getBindingsPermission('product', ids)
if(resp.status === 200){
const arr = resp.result.map((item: any) => {
return item?.permissionInfoList?.map((i: any) => i?.id)
})
return intersection(...arr)
}
return []
},
clickEdit: async (row?: any) => {
const ids = row ? [row.id] : [...tableData._selectedRowKeys]; const ids = row ? [row.id] : [...tableData._selectedRowKeys];
if (ids.length < 1) return message.warning('请勾选需要编辑的数据'); if (ids.length < 1) return message.warning('请勾选需要编辑的数据');
tableData.defaultPermission = row ? row?.permission : intersection(...tableData.selectedRows.map(
if (row || tableData.selectedRows.length === 1) {
const permissionList =
row?.permission || tableData.selectedRows[0].permission;
dialogs.selectIds = ids;
dialogs.permissList = permissionList;
dialogs.editShow = true;
return;
} else if (tableData.selectedRows.length === 0) return;
const permissionList = tableData.selectedRows.map(
(item) => item.permission, (item) => item.permission,
); )) as string[]
const mixPermissionList = intersection(...permissionList) as string[]; const _result = await table.queryPermissionList(ids)
dialogs.selectIds = ids; dialogs.selectIds = ids;
dialogs.permissList = mixPermissionList; dialogs.permissList = _result as string[];
dialogs.editShow = true; dialogs.editShow = true;
}, },
clickUnBind: (row?: any) => { clickUnBind: (row?: any) => {

View File

@ -213,7 +213,8 @@ onMounted(() => {
cloneDeep(systemMenu.value), cloneDeep(systemMenu.value),
); );
treeData.value = AllMenu; //
treeData.value = handleSorts(AllMenu);
} }
}); });
}); });

View File

@ -1,4 +1,4 @@
import { cloneDeep } from 'lodash-es'; import { cloneDeep, omit } from 'lodash-es';
import type { import type {
AntTreeNodeDropEvent, AntTreeNodeDropEvent,
TreeProps, TreeProps,
@ -39,6 +39,10 @@ export const mergeArr = (oldData: Array<any>, newData: Array<any>) => {
return newItem; return newItem;
} }
if(oldItem && newItem){
oldItem = { ...oldData,...omit(newItem, ['children'])}
}
if (!oldItem.children && newItem.children) { if (!oldItem.children && newItem.children) {
oldItem.children = newItem.children; oldItem.children = newItem.children;
return oldItem; return oldItem;
@ -312,7 +316,7 @@ export const handleSorts = (node: any[]) => {
if (item.index !== index) { if (item.index !== index) {
item.sortIndex = index item.sortIndex = index
if (item.children) { if (item.children) {
item.children = handleSorts(item.children) item.children = handleSorts(item.children).sort((a, b) => a.sortIndex - b.sortIndex)
} }
} }
return item return item

View File

@ -150,6 +150,7 @@ const indeterminate = ref<boolean>(false);
const selectAllChange = () => { const selectAllChange = () => {
flatTableData.forEach((item) => { flatTableData.forEach((item) => {
item.granted = selectedAll.value; item.granted = selectedAll.value;
item.indeterminate = false;
item.buttons?.forEach((button) => { item.buttons?.forEach((button) => {
button.granted = selectedAll.value; button.granted = selectedAll.value;
}); });

View File

@ -3823,10 +3823,10 @@ jetlinks-store@^0.0.3:
resolved "https://registry.npmjs.org/jetlinks-store/-/jetlinks-store-0.0.3.tgz" resolved "https://registry.npmjs.org/jetlinks-store/-/jetlinks-store-0.0.3.tgz"
integrity sha512-AZf/soh1hmmwjBZ00fr1emuMEydeReaI6IBTGByQYhTmK1Zd5pQAxC7WLek2snRAn/HHDgJfVz2hjditKThl6Q== integrity sha512-AZf/soh1hmmwjBZ00fr1emuMEydeReaI6IBTGByQYhTmK1Zd5pQAxC7WLek2snRAn/HHDgJfVz2hjditKThl6Q==
jetlinks-ui-components@^1.0.18: jetlinks-ui-components@^1.0.21:
version "1.0.20" version "1.0.21"
resolved "https://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.20.tgz#ca8df39e35e99cf0e124029609a8fc25f7f97b24" resolved "https://registry.jetlinks.cn/jetlinks-ui-components/-/jetlinks-ui-components-1.0.21.tgz#e2dc28682db91b4b05332fb8ec76faa2a2f4dcd2"
integrity sha512-McGHwvkwEKrb1Bp9EZzpQN3YQe790fkO0Z03pGsil+bZrZ7xqiEywESZtW3MIIRWo/6u+zwNQr4L4/ohvXRCpA== integrity sha512-nlVGo2ze6e4YFPOyqS1GVCpaH9P4VGPMGYJIucTEjjMb/QqPmkwS7HcwyDX3kvb6Acb+dG5oQZlscMHIGqv0YQ==
dependencies: dependencies:
"@vueuse/core" "^9.12.0" "@vueuse/core" "^9.12.0"
"@vueuse/router" "^9.13.0" "@vueuse/router" "^9.13.0"