parent
33d1aff0be
commit
951215955a
4
build.sh
4
build.sh
|
@ -1,3 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
docker build -t registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.3.0-SNAPSHOT .
|
||||
docker push registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.3.0-SNAPSHOT
|
||||
docker build -t registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.2.0-TEST .
|
||||
docker push registry.cn-shenzhen.aliyuncs.com/jetlinks/jetlinks-ui-vue:2.2.0-TEST
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
Binary file not shown.
After Width: | Height: | Size: 70 KiB |
File diff suppressed because one or more lines are too long
|
@ -22,7 +22,7 @@ import '@vuemap/vue-amap/dist/style.css';
|
|||
import { getAMapUiPromise } from './utils';
|
||||
import { useSystem } from '@/store/system';
|
||||
|
||||
const emit = defineEmits(['init'])
|
||||
const emit = defineEmits(['init']);
|
||||
|
||||
const system = useSystem();
|
||||
interface AMapProps {
|
||||
|
@ -31,11 +31,12 @@ interface AMapProps {
|
|||
AMapUI?: string | boolean;
|
||||
}
|
||||
const amapKey = system.$state.configInfo.amap?.apiKey;
|
||||
const secretKey = system.$state.configInfo.amap?.secretKey;
|
||||
|
||||
initAMapApiLoader({
|
||||
key: amapKey || 'c86c1b22c6b223e3ed08815532676445',
|
||||
securityJsCode: 'b0efcf1ce14cbf2d56d3cde630cd19cf',
|
||||
plugins: ['AMap.DistrictSearch'],
|
||||
key: amapKey,
|
||||
securityJsCode: secretKey,
|
||||
plugins: ['AMap.DistrictSearch', 'AMap.GeoJSON'],
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
|
@ -49,7 +50,7 @@ const mapRef = ref();
|
|||
|
||||
const uiLoading = ref<boolean>(false);
|
||||
|
||||
const map = ref<any>(null);
|
||||
let mapInstance:any = null;
|
||||
|
||||
const isOpenUi = computed(() => {
|
||||
return 'AMapUI' in props || props.AMapUI;
|
||||
|
@ -65,13 +66,27 @@ const getAMapUI = () => {
|
|||
const marker = ref<any[]>([]);
|
||||
|
||||
const initMap = (e: any) => {
|
||||
map.value = e;
|
||||
mapInstance = e;
|
||||
if (isOpenUi.value) {
|
||||
getAMapUI();
|
||||
}
|
||||
emit('init', e)
|
||||
emit('init', e);
|
||||
};
|
||||
|
||||
const setBounds = (bounds: any) => {
|
||||
console.log(bounds)
|
||||
if (mapInstance) {
|
||||
mapInstance.setBounds(bounds)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
console.log(secretKey,'secretKey')
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
setBounds
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
</style>
|
||||
<style lang="less" scoped></style>
|
||||
|
|
|
@ -2,7 +2,7 @@ import { defineStore } from 'pinia'
|
|||
import type { FormModelType } from '@/views/rule-engine/Scene/typings'
|
||||
import { detail } from '@/api/rule-engine/scene'
|
||||
import {cloneDeep, isArray, isObject} from 'lodash-es'
|
||||
import { randomString } from '@/utils/utils'
|
||||
import {randomNumber, randomString} from '@/utils/utils'
|
||||
|
||||
type DataType = {
|
||||
data: FormModelType | any
|
||||
|
@ -12,7 +12,6 @@ type DataType = {
|
|||
const assignmentKey = (data: any[]): any[] => {
|
||||
const onlyKey = ['when', 'then', 'terms', 'actions'];
|
||||
if (!data) return [];
|
||||
|
||||
return data.map((item: any) => {
|
||||
if (item) {
|
||||
item.key = randomString();
|
||||
|
@ -25,7 +24,7 @@ const assignmentKey = (data: any[]): any[] => {
|
|||
return item;
|
||||
});
|
||||
};
|
||||
|
||||
const defaultBranchId = randomNumber()
|
||||
export const defaultBranches = [
|
||||
{
|
||||
when: [
|
||||
|
@ -55,8 +54,8 @@ export const defaultBranches = [
|
|||
},
|
||||
then: [],
|
||||
executeAnyway: true,
|
||||
branchId: Math.floor(Math.random() * 100000000),
|
||||
branchName:''
|
||||
branchId: defaultBranchId,
|
||||
branchName:'条件'
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -69,6 +68,10 @@ const defaultOptions = {
|
|||
terms: [['','eq','','and']],
|
||||
},
|
||||
],
|
||||
branchName:'条件',
|
||||
key: 'branches_1',
|
||||
executeAnyway: true,
|
||||
groupIndex: 1
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -156,7 +159,22 @@ export const useSceneStore = defineStore('scene', () => {
|
|||
// branches.push(null);
|
||||
// }
|
||||
}
|
||||
console.log('result.options',branches)
|
||||
|
||||
branches = branches.map(item => {
|
||||
if (item?.then) {
|
||||
item?.then.forEach(thenItem => {
|
||||
if (thenItem.actions) {
|
||||
thenItem.actions.forEach(actionItem => {
|
||||
if (!actionItem.actionId) {
|
||||
actionItem.actionId = randomNumber()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
return item
|
||||
})
|
||||
|
||||
data.value = {
|
||||
...result,
|
||||
trigger: result.trigger || {},
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6,188 +6,203 @@
|
|||
:visible="true"
|
||||
width="700px"
|
||||
:confirm-loading="loading"
|
||||
:maskClosable="false"
|
||||
@cancel="handleCancel"
|
||||
@ok="handleOk"
|
||||
>
|
||||
<a-spin :spinning="_loading">
|
||||
<j-form
|
||||
class="form"
|
||||
layout="vertical"
|
||||
:model="formData"
|
||||
name="basic"
|
||||
autocomplete="off"
|
||||
>
|
||||
<j-row :gutter="[24, 0]">
|
||||
<j-col :span="24">
|
||||
<j-form-item label="名称" v-bind="validateInfos.name">
|
||||
<j-input
|
||||
placeholder="请输入名称"
|
||||
v-model:value="formData.name"
|
||||
/></j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24"
|
||||
><j-form-item
|
||||
label="所属产品"
|
||||
v-bind="validateInfos.productId"
|
||||
>
|
||||
<j-select
|
||||
v-model:value="formData.productId"
|
||||
:options="productOptions"
|
||||
placeholder="请选择所属产品"
|
||||
allowClear
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
/> </j-form-item
|
||||
></j-col>
|
||||
<j-col :span="12"
|
||||
><j-form-item label="版本号" v-bind="validateInfos.version">
|
||||
<j-input
|
||||
placeholder="请输入版本号"
|
||||
v-model:value="formData.version" /></j-form-item
|
||||
></j-col>
|
||||
<j-col :span="12"
|
||||
><j-form-item
|
||||
label="版本序号"
|
||||
v-bind="validateInfos.versionOrder"
|
||||
>
|
||||
<j-input-number
|
||||
placeholder="请输入版本序号"
|
||||
style="width: 100%"
|
||||
:min="1"
|
||||
:max="99999"
|
||||
v-model:value="
|
||||
formData.versionOrder
|
||||
" /></j-form-item
|
||||
></j-col>
|
||||
<j-col :span="12"
|
||||
><j-form-item
|
||||
label="签名方式"
|
||||
v-bind="validateInfos.signMethod"
|
||||
>
|
||||
<j-select
|
||||
v-model:value="formData.signMethod"
|
||||
:options="[
|
||||
{ label: 'MD5', value: 'md5' },
|
||||
{ label: 'SHA256', value: 'sha256' },
|
||||
]"
|
||||
placeholder="请选择签名方式"
|
||||
allowClear
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
@change="changeSignMethod"
|
||||
/>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="12"
|
||||
><j-form-item v-bind="validateInfos.sign">
|
||||
<template #label>
|
||||
签名
|
||||
<j-tooltip title="请输入本地文件进行签名加密后的值">
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
<j-input
|
||||
placeholder="请输入签名"
|
||||
v-model:value="formData.sign" /></j-form-item
|
||||
></j-col>
|
||||
<j-col :span="24">
|
||||
<j-form-item label="固件上传" v-bind="validateInfos.url">
|
||||
<FileUpload
|
||||
v-model:modelValue="formData.url"
|
||||
v-model:extraValue="extraValue"
|
||||
/> </j-form-item
|
||||
></j-col>
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
label="其他配置"
|
||||
v-bind="validateInfos.properties"
|
||||
>
|
||||
<j-form
|
||||
:class="
|
||||
dynamicValidateForm.properties.length !== 0 &&
|
||||
'formRef'
|
||||
"
|
||||
ref="formRef"
|
||||
name="dynamic_form_nest_item"
|
||||
:model="dynamicValidateForm"
|
||||
>
|
||||
<div
|
||||
class="formRef-content"
|
||||
v-for="(
|
||||
propertie, index
|
||||
) in dynamicValidateForm.properties"
|
||||
:key="propertie.keyid"
|
||||
>
|
||||
<j-form-item
|
||||
:label="index === 0 && 'Key'"
|
||||
class="formRef-form-item"
|
||||
:name="['properties', index, 'id']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请输入KEY',
|
||||
}"
|
||||
>
|
||||
<j-input
|
||||
v-model:value="propertie.id"
|
||||
placeholder="请输入KEY"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
:label="index === 0 && 'Value'"
|
||||
class="formRef-form-item"
|
||||
:name="['properties', index, 'value']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请输入VALUE',
|
||||
}"
|
||||
>
|
||||
<j-input
|
||||
v-model:value="propertie.value"
|
||||
placeholder="请输入VALUE"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
:label="index === 0 && '操作'"
|
||||
class="formRef-form-item"
|
||||
style="width: 10%"
|
||||
>
|
||||
<j-popconfirm
|
||||
title="确认删除吗?"
|
||||
ok-text="确认"
|
||||
cancel-text="取消"
|
||||
@confirm="removeList(propertie)"
|
||||
<j-form
|
||||
class="form"
|
||||
layout="vertical"
|
||||
:model="formData"
|
||||
name="basic"
|
||||
autocomplete="off"
|
||||
>
|
||||
<j-row :gutter="[24, 0]">
|
||||
<j-col :span="24">
|
||||
<j-form-item label="名称" v-bind="validateInfos.name">
|
||||
<j-input
|
||||
placeholder="请输入名称"
|
||||
v-model:value="formData.name"
|
||||
/></j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="24"
|
||||
><j-form-item
|
||||
label="所属产品"
|
||||
v-bind="validateInfos.productId"
|
||||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</j-popconfirm>
|
||||
</j-form-item>
|
||||
</div>
|
||||
<j-form-item class="formRef-form-item-add">
|
||||
<j-button type="dashed" block @click="addList">
|
||||
<AIcon type="PlusOutlined" />
|
||||
添加
|
||||
</j-button>
|
||||
</j-form-item>
|
||||
</j-form>
|
||||
</j-form-item></j-col
|
||||
>
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
label="说明"
|
||||
v-bind="validateInfos.description"
|
||||
>
|
||||
<j-textarea
|
||||
placeholder="请输入说明"
|
||||
v-model:value="formData.description"
|
||||
:maxlength="200"
|
||||
:rows="3"
|
||||
showCount
|
||||
/> </j-form-item
|
||||
></j-col>
|
||||
</j-row>
|
||||
</j-form>
|
||||
<j-select
|
||||
v-model:value="formData.productId"
|
||||
:options="productOptions"
|
||||
placeholder="请选择所属产品"
|
||||
allowClear
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
/> </j-form-item
|
||||
></j-col>
|
||||
<j-col :span="12"
|
||||
><j-form-item
|
||||
label="版本号"
|
||||
v-bind="validateInfos.version"
|
||||
>
|
||||
<j-input
|
||||
placeholder="请输入版本号"
|
||||
v-model:value="formData.version" /></j-form-item
|
||||
></j-col>
|
||||
<j-col :span="12"
|
||||
><j-form-item
|
||||
label="版本序号"
|
||||
v-bind="validateInfos.versionOrder"
|
||||
>
|
||||
<j-input-number
|
||||
placeholder="请输入版本序号"
|
||||
style="width: 100%"
|
||||
:min="1"
|
||||
:max="99999"
|
||||
v-model:value="
|
||||
formData.versionOrder
|
||||
" /></j-form-item
|
||||
></j-col>
|
||||
<j-col :span="12"
|
||||
><j-form-item
|
||||
label="签名方式"
|
||||
v-bind="validateInfos.signMethod"
|
||||
>
|
||||
<j-select
|
||||
v-model:value="formData.signMethod"
|
||||
:options="[
|
||||
{ label: 'MD5', value: 'md5' },
|
||||
{ label: 'SHA256', value: 'sha256' },
|
||||
]"
|
||||
placeholder="请选择签名方式"
|
||||
allowClear
|
||||
show-search
|
||||
:filter-option="filterOption"
|
||||
@change="changeSignMethod"
|
||||
/>
|
||||
</j-form-item>
|
||||
</j-col>
|
||||
<j-col :span="12"
|
||||
><j-form-item v-bind="validateInfos.sign">
|
||||
<template #label>
|
||||
签名
|
||||
<j-tooltip
|
||||
title="请输入本地文件进行签名加密后的值"
|
||||
>
|
||||
<AIcon
|
||||
type="QuestionCircleOutlined"
|
||||
style="margin-left: 2px"
|
||||
/>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
<j-input
|
||||
placeholder="请输入签名"
|
||||
v-model:value="formData.sign" /></j-form-item
|
||||
></j-col>
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
label="固件上传"
|
||||
v-bind="validateInfos.url"
|
||||
>
|
||||
<FileUpload
|
||||
v-model:modelValue="formData.url"
|
||||
v-model:extraValue="extraValue"
|
||||
/> </j-form-item
|
||||
></j-col>
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
label="其他配置"
|
||||
v-bind="validateInfos.properties"
|
||||
>
|
||||
<j-form
|
||||
:class="
|
||||
dynamicValidateForm.properties.length !==
|
||||
0 && 'formRef'
|
||||
"
|
||||
ref="formRef"
|
||||
name="dynamic_form_nest_item"
|
||||
:model="dynamicValidateForm"
|
||||
>
|
||||
<div
|
||||
class="formRef-content"
|
||||
v-for="(
|
||||
propertie, index
|
||||
) in dynamicValidateForm.properties"
|
||||
:key="propertie.keyid"
|
||||
>
|
||||
<j-form-item
|
||||
:label="index === 0 && 'Key'"
|
||||
class="formRef-form-item"
|
||||
:name="['properties', index, 'id']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请输入KEY',
|
||||
}"
|
||||
>
|
||||
<j-input
|
||||
v-model:value="propertie.id"
|
||||
placeholder="请输入KEY"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
:label="index === 0 && 'Value'"
|
||||
class="formRef-form-item"
|
||||
:name="['properties', index, 'value']"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请输入VALUE',
|
||||
}"
|
||||
>
|
||||
<j-input
|
||||
v-model:value="propertie.value"
|
||||
placeholder="请输入VALUE"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
:label="index === 0 && '操作'"
|
||||
class="formRef-form-item"
|
||||
style="width: 10%"
|
||||
>
|
||||
<PermissionButton
|
||||
type="text"
|
||||
:popConfirm="{
|
||||
title: '确认删除吗?',
|
||||
onConfirm: () =>
|
||||
removeList(propertie),
|
||||
}"
|
||||
>
|
||||
<AIcon type="DeleteOutlined"
|
||||
/></PermissionButton>
|
||||
</j-form-item>
|
||||
</div>
|
||||
<j-form-item class="formRef-form-item-add">
|
||||
<j-button
|
||||
type="dashed"
|
||||
block
|
||||
@click="addList"
|
||||
>
|
||||
<AIcon type="PlusOutlined" />
|
||||
添加
|
||||
</j-button>
|
||||
</j-form-item>
|
||||
</j-form>
|
||||
</j-form-item></j-col
|
||||
>
|
||||
<j-col :span="24">
|
||||
<j-form-item
|
||||
label="说明"
|
||||
v-bind="validateInfos.description"
|
||||
>
|
||||
<j-textarea
|
||||
placeholder="请输入说明"
|
||||
v-model:value="formData.description"
|
||||
:maxlength="200"
|
||||
:rows="3"
|
||||
showCount
|
||||
/> </j-form-item
|
||||
></j-col>
|
||||
</j-row>
|
||||
</j-form>
|
||||
</a-spin>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
@ -226,13 +241,16 @@ const addList = () => {
|
|||
const loading = ref(false);
|
||||
const _loading = ref(false);
|
||||
const useForm = Form.useForm;
|
||||
const productOptions = ref([]);
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
productOptions: {
|
||||
type: Array,
|
||||
default: [],
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['change']);
|
||||
|
@ -285,30 +303,28 @@ const validatorVersionOrder = async (_: Record<string, any>, value: string) => {
|
|||
};
|
||||
|
||||
const validatorProductExist = async (_: Record<string, any>, value: string) => {
|
||||
if (!value) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
const dt = productOptions.value.find((i: any) => i.value === value)
|
||||
if(dt){
|
||||
return Promise.resolve();
|
||||
if (!value) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return Promise.reject('当前产品不存在,请选择产品')
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const validatorVersionValue = async (_rule:any,value:any) => {
|
||||
return new Promise (async(resolve,reject)=>{
|
||||
const posReg = /^[1-9]\d*$/;
|
||||
if(posReg.test(value.toString())){
|
||||
return resolve('');
|
||||
}else {
|
||||
return reject('请输入1~99999之间的正整数');
|
||||
const dt = props.productOptions.find((i: any) => i.value === value);
|
||||
if (dt) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return Promise.reject('当前产品不存在,请选择产品');
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const validatorVersionValue = async (_rule: any, value: any) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const posReg = /^[1-9]\d*$/;
|
||||
if (posReg.test(value.toString())) {
|
||||
return resolve('');
|
||||
} else {
|
||||
return reject('请输入1~99999之间的正整数');
|
||||
}
|
||||
});
|
||||
};
|
||||
const { resetFields, validate, validateInfos } = useForm(
|
||||
formData,
|
||||
reactive({
|
||||
|
@ -318,7 +334,7 @@ const { resetFields, validate, validateInfos } = useForm(
|
|||
],
|
||||
productId: [
|
||||
{ required: true, message: '请选择所属产品' },
|
||||
{ validator: validatorProductExist, trigger: 'blur' }
|
||||
{ validator: validatorProductExist, trigger: 'blur' },
|
||||
],
|
||||
version: [
|
||||
{ required: true, message: '请输入版本号' },
|
||||
|
@ -327,7 +343,7 @@ const { resetFields, validate, validateInfos } = useForm(
|
|||
versionOrder: [
|
||||
{ required: true, message: '请输入版本序号' },
|
||||
{ validator: validatorVersionOrder, trigger: 'blur' },
|
||||
{ validator: validatorVersionValue, trigger: 'change'}
|
||||
{ validator: validatorVersionValue, trigger: 'change' },
|
||||
],
|
||||
signMethod: [{ required: true, message: '请选择签名方式' }],
|
||||
sign: [
|
||||
|
@ -348,7 +364,7 @@ const handleOk = async () => {
|
|||
|
||||
validate()
|
||||
.then(async (res: any) => {
|
||||
const product: any = productOptions.value.find(
|
||||
const product: any = props.productOptions.find(
|
||||
(item: any) => item?.value === res.productId,
|
||||
);
|
||||
const productName = product?.label || props.data?.url;
|
||||
|
@ -385,20 +401,6 @@ const changeSignMethod = () => {
|
|||
formData.value.url = '';
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
_loading.value = true
|
||||
queryProduct({
|
||||
paging: false,
|
||||
terms: [{ column: 'state', value: 1 }],
|
||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||
}).then((resp: any) => {
|
||||
_loading.value = false
|
||||
productOptions.value = resp.result.map((item: any) => ({
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
}));
|
||||
});
|
||||
});
|
||||
watch(
|
||||
() => props.data,
|
||||
(value) => {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,14 +1,13 @@
|
|||
import { ColumnProps } from "ant-design-vue/es/table";
|
||||
import { DataType, Source, InputParams, OtherSetting, OutputParams, ConfigParams, TagsType } from './components'
|
||||
import SelectColumn from './components/Events/SelectColumn.vue';
|
||||
import AsyncSelect from './components/Function/AsyncSelect.vue';
|
||||
import { EventLevel } from "@/views/device/data";
|
||||
import {MetadataType} from "@/views/device/Product/typings";
|
||||
import { DataType } from './components'
|
||||
import {MetadataItem, MetadataType} from "@/views/device/Product/typings";
|
||||
import { getUnit } from '@/api/device/instance';
|
||||
import {Ref} from "vue";
|
||||
import {omit, pick, isObject, cloneDeep} from "lodash-es";
|
||||
import { message } from 'jetlinks-ui-components'
|
||||
import {omit, isObject, groupBy} from "lodash-es";
|
||||
import { onlyMessage } from "@/utils/comm";
|
||||
import {getSourceMap} from "@/views/device/components/Metadata/Base/utils";
|
||||
import {getTypeMap} from "components/Metadata/Table/components/Type/data";
|
||||
import {getEventLevelMap} from "@/views/device/data";
|
||||
interface DataTableColumnProps extends ColumnProps {
|
||||
type?: string,
|
||||
components?: {
|
||||
|
@ -21,7 +20,10 @@ interface DataTableColumnProps extends ColumnProps {
|
|||
},
|
||||
options?: any[]
|
||||
doubleClick?: (record: any, index: number, dataIndex: string) => boolean
|
||||
control?: (newValue: any, oldValue: any) => Boolean
|
||||
control?: (newValue: any, oldValue: any) => boolean
|
||||
|
||||
filter?: boolean
|
||||
sort?: Record<string, any>
|
||||
}
|
||||
|
||||
const SourceMap = {
|
||||
|
@ -36,6 +38,8 @@ const type = {
|
|||
report: '上报',
|
||||
};
|
||||
|
||||
const METADATA_UNIT = 'metadata-unit'
|
||||
|
||||
export const validatorConfig = (value: any, _isObject: boolean = false) => {
|
||||
|
||||
if (!value) {
|
||||
|
@ -53,6 +57,10 @@ export const validatorConfig = (value: any, _isObject: boolean = false) => {
|
|||
return Promise.reject('请添加参数')
|
||||
}
|
||||
|
||||
if (value.type === 'date' && !value.format) {
|
||||
return Promise.reject('请选择时间格式')
|
||||
}
|
||||
|
||||
if (value.type === 'file' && (!value.bodyType || (isObject(value.bodyType) && !Object.keys(value.bodyType).length))) {
|
||||
return Promise.reject('请选择文件类型')
|
||||
}
|
||||
|
@ -93,9 +101,8 @@ export const handleTypeValue = (type:string, value: any = {}) => {
|
|||
obj.format = value
|
||||
break;
|
||||
case 'string':
|
||||
obj.maxLength = JSON.stringify(value) === '{}' ? undefined : value
|
||||
case 'password':
|
||||
obj.maxLength = JSON.stringify(value) === '{}' ? undefined : value
|
||||
obj.expands.maxLength = JSON.stringify(value) === '{}' ? undefined : value
|
||||
break;
|
||||
default:
|
||||
obj = value
|
||||
|
@ -133,7 +140,7 @@ export const typeSelectChange = (type: string) => {
|
|||
break;
|
||||
case 'string':
|
||||
case 'password':
|
||||
obj.maxLength = undefined
|
||||
obj.expands.maxLength = undefined
|
||||
break;
|
||||
case 'boolean':
|
||||
obj.trueText = '是'
|
||||
|
@ -155,27 +162,28 @@ const isExtendsProduct = (id: string, productKeys: string, type: string) => {
|
|||
return false
|
||||
}
|
||||
|
||||
export const useColumns = (type?: MetadataType, target?: 'device' | 'product', noEdit?: Ref<any>, productNoEdit?: Ref<any>) => {
|
||||
export const useColumns = (dataSource: Ref<MetadataItem[]>, type?: MetadataType, target?: 'device' | 'product', noEdit?: Ref<any>, productNoEdit?: Ref<any>) => {
|
||||
|
||||
const BaseColumns: DataTableColumnProps[] = [
|
||||
{
|
||||
title: '标识',
|
||||
dataIndex: 'id',
|
||||
type: 'text',
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
callback(rule:any,value: any, dataSource: any[]) {
|
||||
asyncValidator(rule:any,value: any, ...setting: any) {
|
||||
if (value) {
|
||||
const field = rule.field.split('.')
|
||||
const fieldIndex = Number(field[1])
|
||||
const hasId = dataSource.some((item, index) => item.id === value && fieldIndex !== index)
|
||||
if (hasId) {
|
||||
const option = setting[2]
|
||||
|
||||
const isSome = dataSource.value.some((item) => {
|
||||
return item.__dataIndex !== option.index && item.id && item.id === value
|
||||
})
|
||||
if (isSome) {
|
||||
return Promise.reject('该标识已存在')
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
return Promise.reject('请输入标识')
|
||||
return Promise.resolve()
|
||||
// return Promise.reject('请输入标识')
|
||||
},
|
||||
},
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
|
@ -185,273 +193,41 @@ export const useColumns = (type?: MetadataType, target?: 'device' | 'product', n
|
|||
},
|
||||
]
|
||||
},
|
||||
doubleClick(record) {
|
||||
if (isExtendsProduct(record.id, productNoEdit?.value, 'id')) {
|
||||
return false
|
||||
}
|
||||
const ids = (noEdit?.value?.id || []) as any[]
|
||||
return !ids.includes(record.id)
|
||||
}
|
||||
filter: true
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
width: 300,
|
||||
type: 'text',
|
||||
form: {
|
||||
required: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入名称'
|
||||
},
|
||||
{ max: 64, message: '最多可输入64个字符' },
|
||||
{
|
||||
asyncValidator(_: any, value: any) {
|
||||
|
||||
if (!value) {
|
||||
return Promise.reject('请输入名称')
|
||||
} else if (value.length > 64) {
|
||||
return Promise.reject('最多可输入64个字符')
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
]
|
||||
// rules:[{
|
||||
// callback(rule:any,value: any, dataSource: any[]) {
|
||||
// console.log(rule,value,dataSource,123)
|
||||
// return value
|
||||
// }
|
||||
// }]
|
||||
},
|
||||
doubleClick(record) {
|
||||
if (isExtendsProduct(record.id, productNoEdit?.value, 'name')) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
filter: true
|
||||
},
|
||||
];
|
||||
|
||||
const EventColumns: DataTableColumnProps[] = BaseColumns.concat([
|
||||
{
|
||||
title: '事件级别',
|
||||
dataIndex: 'expands',
|
||||
type: 'components',
|
||||
components: {
|
||||
name: SelectColumn,
|
||||
props: {
|
||||
options: EventLevel
|
||||
}
|
||||
},
|
||||
doubleClick(record) {
|
||||
if (isExtendsProduct(record.id, productNoEdit?.value, 'expands')) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
control(newValue, oldValue) {
|
||||
return newValue.expands.level !== oldValue?.expands?.level
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '输出参数',
|
||||
dataIndex: 'outInput',
|
||||
},
|
||||
{
|
||||
title: '配置参数',
|
||||
dataIndex: 'properties',
|
||||
width: 100,
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
callback(rule: any, value: any, dataSource: any[]) {
|
||||
const field = rule.field.split('.')
|
||||
const fieldIndex = Number(field[1])
|
||||
const record = dataSource.find((item, index) => index === fieldIndex)
|
||||
|
||||
if (!record.valueType.properties.length) {
|
||||
return Promise.reject('请添加配置参数')
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
}]
|
||||
},
|
||||
control(newValue, oldValue) {
|
||||
if (newValue && !oldValue) {
|
||||
return true
|
||||
} else if (newValue && oldValue) {
|
||||
return JSON.stringify(newValue.valueType) !== JSON.stringify(oldValue.valueType)
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '其它配置',
|
||||
dataIndex: 'other',
|
||||
width: 100,
|
||||
control(newValue, oldValue) {
|
||||
if (!oldValue) {
|
||||
return true
|
||||
} else if (newValue && oldValue) {
|
||||
// 仅留下存储和指标值
|
||||
const keys = ['source', 'type', 'virtualRule', 'required']
|
||||
const newObj = omit(cloneDeep(newValue.expands), keys)
|
||||
const oldObj = omit(cloneDeep(oldValue.expands), keys)
|
||||
return JSON.stringify(newObj) !== JSON.stringify(oldObj)
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
type: 'text',
|
||||
form: {
|
||||
rules: [
|
||||
{ max: 20, message: '最多可输入20个字符' },
|
||||
]},
|
||||
doubleClick(record) {
|
||||
if (isExtendsProduct(record.id, productNoEdit?.value, 'description')) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
const FunctionColumns: DataTableColumnProps[] = BaseColumns.concat([
|
||||
{
|
||||
title: '是否异步',
|
||||
dataIndex: 'async',
|
||||
type: 'components',
|
||||
components: {
|
||||
name: AsyncSelect,
|
||||
props: {
|
||||
options: [
|
||||
{ label: '是', value: true },
|
||||
{ label: '否', value: false }
|
||||
]
|
||||
}
|
||||
},
|
||||
doubleClick(record) {
|
||||
return !isExtendsProduct(record.id, productNoEdit?.value, 'async');
|
||||
|
||||
},
|
||||
control(newValue, oldValue) {
|
||||
return newValue.async !== oldValue?.async
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '输入参数',
|
||||
dataIndex: 'inputs',
|
||||
width: 120,
|
||||
// form: {
|
||||
// required: true,
|
||||
// rules: [{
|
||||
// callback(rule:any,value: any, dataSource: any[]) {
|
||||
// const field = rule.field.split('.')
|
||||
// const fieldIndex = Number(field[1])
|
||||
//
|
||||
// const values = dataSource.find((item, index) => index === fieldIndex)
|
||||
//
|
||||
// return validatorConfig({
|
||||
// type: 'object',
|
||||
// properties: values.inputs
|
||||
// }, true)
|
||||
// }
|
||||
// }]
|
||||
// },
|
||||
control(newValue, oldValue) {
|
||||
if (newValue && !oldValue) {
|
||||
return true
|
||||
} else if (newValue && oldValue) {
|
||||
return JSON.stringify(newValue.inputs) !== JSON.stringify(oldValue.inputs)
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '输出参数',
|
||||
dataIndex: 'output',
|
||||
type: 'components',
|
||||
components: {
|
||||
name: OutputParams
|
||||
},
|
||||
form: {
|
||||
rules: [{
|
||||
callback(rule:any,value: any, dataSource: any[]) {
|
||||
const field = rule.field.split('.')
|
||||
const fieldIndex = Number(field[1])
|
||||
const values = dataSource.find((item, index) => index === fieldIndex)
|
||||
return validatorConfig(values?.output)
|
||||
}
|
||||
}]
|
||||
},
|
||||
doubleClick(record) {
|
||||
return !isExtendsProduct(record.id, productNoEdit?.value, 'output');
|
||||
},
|
||||
control(newValue, oldValue) {
|
||||
if (newValue && !oldValue) {
|
||||
return true
|
||||
} else if (newValue && oldValue) {
|
||||
return JSON.stringify(newValue.output) !== JSON.stringify(oldValue.output)
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '其它配置',
|
||||
dataIndex: 'other',
|
||||
width: 100,
|
||||
control(newValue, oldValue) {
|
||||
if (!oldValue) {
|
||||
return true
|
||||
} else if (newValue && oldValue) {
|
||||
// 仅留下存储和指标值
|
||||
const keys = ['source', 'type', 'virtualRule', 'required']
|
||||
const newObj = omit(cloneDeep(newValue.expands), keys)
|
||||
const oldObj = omit(cloneDeep(oldValue.expands), keys)
|
||||
return JSON.stringify(newObj) !== JSON.stringify(oldObj)
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
type: 'text',
|
||||
form: {
|
||||
rules: [
|
||||
{ max: 20, message: '最多可输入20个字符' },
|
||||
]},
|
||||
doubleClick(record) {
|
||||
if (isExtendsProduct(record.id, productNoEdit?.value, 'description')) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
width: 120
|
||||
}
|
||||
// {
|
||||
// title: '读写类型',
|
||||
// dataIndex: 'expands',
|
||||
// render: (text: any) => (text?.type || []).map((item: string | number) => <Tag>{type[item]}</Tag>),
|
||||
// },
|
||||
]);
|
||||
|
||||
const PropertyColumns: DataTableColumnProps[] = BaseColumns.concat([
|
||||
{
|
||||
title: '数据类型',
|
||||
dataIndex: 'valueType',
|
||||
type: 'components',
|
||||
components: {
|
||||
name: DataType
|
||||
},
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
validator(_: any, value: any) {
|
||||
asyncValidator(_: any, value: any) {
|
||||
|
||||
if (!value?.type) {
|
||||
return Promise.reject('请选择数据类型')
|
||||
}
|
||||
|
@ -459,49 +235,35 @@ export const useColumns = (type?: MetadataType, target?: 'device' | 'product', n
|
|||
}
|
||||
}]
|
||||
},
|
||||
width: 230,
|
||||
doubleClick(record) {
|
||||
if (isExtendsProduct(record.id, productNoEdit?.value, 'valueType')) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
// sort: {
|
||||
// sortKey: ['valueType', 'type'],
|
||||
// dataSource: () => {
|
||||
// const group = groupBy(dataSource.value.filter(item => item.id && item.valueType.type), (e) => e.valueType.type)
|
||||
// const typeMap = getTypeMap()
|
||||
// return Object.keys(group).map((key, index) => {
|
||||
// return {
|
||||
// name: typeMap[key],
|
||||
// key: key,
|
||||
// total: group[key].length
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// },
|
||||
width: 260,
|
||||
},
|
||||
{
|
||||
title: '属性来源',
|
||||
dataIndex: 'expands',
|
||||
type: 'components',
|
||||
components: {
|
||||
name: Source,
|
||||
props: {
|
||||
noEdit: noEdit?.value?.source || [],
|
||||
target: target,
|
||||
productNoEdit: productNoEdit?.value
|
||||
}
|
||||
},
|
||||
doubleClick(record){
|
||||
if (record.expands.source === 'rule') {
|
||||
return true
|
||||
}
|
||||
return !isExtendsProduct(record.id, productNoEdit?.value, 'expands')
|
||||
},
|
||||
form: {
|
||||
required: true,
|
||||
rules: [
|
||||
{
|
||||
callback: async (rule: any, value: any, dataSource: any[]) => {
|
||||
const field = rule.field.split('.')
|
||||
const fieldIndex = Number(field[1])
|
||||
asyncValidator: async (rule: any, value: any) => {
|
||||
|
||||
const values = dataSource.find((item, index) => index === fieldIndex)
|
||||
const virtualRule = values.expands?.virtualRule
|
||||
const source = value.source
|
||||
console.log(source, value)
|
||||
if (source) {
|
||||
if (source === 'device' && !value.type?.length) {
|
||||
return Promise.reject('请选择读写类型');
|
||||
} else if( source === 'rule' && !virtualRule){
|
||||
return Promise.reject('请配置规则');
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
|
@ -512,55 +274,176 @@ export const useColumns = (type?: MetadataType, target?: 'device' | 'product', n
|
|||
},
|
||||
]
|
||||
},
|
||||
control(newValue, oldValue) {
|
||||
if (newValue && !oldValue) {
|
||||
return true
|
||||
} else if (newValue && oldValue) {
|
||||
const keys = ['source', 'type', 'virtualRule']
|
||||
const newObj = pick(cloneDeep(newValue.expands), keys)
|
||||
const oldObj = pick(cloneDeep(oldValue.expands), keys)
|
||||
return JSON.stringify(newObj) !== JSON.stringify(oldObj)
|
||||
}
|
||||
return false
|
||||
},
|
||||
width: 150
|
||||
// sort: {
|
||||
// sortKey: ['expands', 'source'],
|
||||
// dataSource: () => {
|
||||
// const group = groupBy(dataSource.value.filter(item => item.id), (e) => e.expands.source)
|
||||
// const sourceMap = getSourceMap()
|
||||
//
|
||||
// return Object.keys(group).map(key => {
|
||||
// return {
|
||||
// name: sourceMap[key],
|
||||
// key: key,
|
||||
// total: group[key].length
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// },
|
||||
width: 220
|
||||
},
|
||||
// {
|
||||
// title: '属性分组',
|
||||
// dataIndex: 'group',
|
||||
// width: 140,
|
||||
// },
|
||||
{
|
||||
title: '其它配置',
|
||||
dataIndex: 'other',
|
||||
width: 100,
|
||||
control(newValue, oldValue) {
|
||||
if (!oldValue) {
|
||||
return true
|
||||
} else if (newValue && oldValue) {
|
||||
// 仅留下存储和指标值
|
||||
const keys = ['source', 'type', 'virtualRule', 'required']
|
||||
const newObj = omit(cloneDeep(newValue.expands), keys)
|
||||
const oldObj = omit(cloneDeep(oldValue.expands), keys)
|
||||
return JSON.stringify(newObj) !== JSON.stringify(oldObj)
|
||||
}
|
||||
return false
|
||||
},
|
||||
width: 110,
|
||||
},
|
||||
]);
|
||||
|
||||
const FunctionColumns: DataTableColumnProps[] = BaseColumns.concat([
|
||||
{
|
||||
title: '是否异步',
|
||||
dataIndex: 'async',
|
||||
width: 120,
|
||||
// sort: {
|
||||
// sortKey: ['async'],
|
||||
// dataSource: () => {
|
||||
// const group = groupBy(dataSource.value.filter(item => item.id), (e) => e.async)
|
||||
//
|
||||
// return Object.keys(group).map(key => {
|
||||
// return {
|
||||
// name: key ? '是' : '否',
|
||||
// key: key,
|
||||
// total: group[key].length
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// },
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
width: 120
|
||||
}
|
||||
title: '输入参数',
|
||||
dataIndex: 'inputs',
|
||||
width: 110,
|
||||
},
|
||||
{
|
||||
title: '输出参数',
|
||||
dataIndex: 'output',
|
||||
width: 240,
|
||||
form: {
|
||||
rules: [{
|
||||
asyncValidator: async (rule: any, value: any) => {
|
||||
return validatorConfig(value)
|
||||
}
|
||||
}]
|
||||
},
|
||||
// sort: {
|
||||
// sortKey: ['output', 'type'],
|
||||
// dataSource: () => {
|
||||
// const group = groupBy(dataSource.value.filter(item => item.id && item.output.type), (e) => e.output.type)
|
||||
// const typeMap = getTypeMap()
|
||||
// return Object.keys(group).map(key => {
|
||||
// return {
|
||||
// name: typeMap[key],
|
||||
// key: key,
|
||||
// total: group[key].length
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
},
|
||||
// {
|
||||
// title: '属性分组',
|
||||
// dataIndex: 'group',
|
||||
// width: 140,
|
||||
// },
|
||||
{
|
||||
title: '其它配置',
|
||||
dataIndex: 'other',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
width: 220,
|
||||
form: {
|
||||
rules: [
|
||||
{ max: 20, message: '最多可输入20个字符' },
|
||||
]},
|
||||
},
|
||||
]);
|
||||
|
||||
const EventColumns: DataTableColumnProps[] = BaseColumns.concat([
|
||||
{
|
||||
title: '事件级别',
|
||||
dataIndex: 'expands',
|
||||
width: 150,
|
||||
// sort: {
|
||||
// sortKey: ['expands', 'level'],
|
||||
// dataSource: () => {
|
||||
// const group = groupBy(dataSource.value.filter(item => item.id), (e) => e.expands.level)
|
||||
// const typeMap = getEventLevelMap()
|
||||
// return Object.keys(group).map(key => {
|
||||
// return {
|
||||
// name: typeMap[key],
|
||||
// key: key,
|
||||
// total: group[key].length
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
},
|
||||
{
|
||||
title: '输出参数',
|
||||
dataIndex: 'valueType',
|
||||
width: 110,
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
asyncValidator: async (rule: any, value: any) => {
|
||||
|
||||
if (!value.properties?.length) {
|
||||
return Promise.reject('请添加配置参数')
|
||||
}
|
||||
|
||||
return Promise.resolve()
|
||||
}
|
||||
}]
|
||||
},
|
||||
},
|
||||
// {
|
||||
// title: '属性分组',
|
||||
// dataIndex: 'group',
|
||||
// width: 140,
|
||||
// },
|
||||
{
|
||||
title: '其它配置',
|
||||
dataIndex: 'other',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
width: 220,
|
||||
form: {
|
||||
rules: [
|
||||
{ max: 20, message: '最多可输入20个字符' },
|
||||
]},
|
||||
},
|
||||
]);
|
||||
|
||||
const TagColumns: DataTableColumnProps[] = BaseColumns.concat([
|
||||
{
|
||||
title: '数据类型',
|
||||
dataIndex: 'valueType',
|
||||
type: 'components',
|
||||
components: {
|
||||
name: DataType,
|
||||
},
|
||||
width: 240,
|
||||
form: {
|
||||
required: true,
|
||||
rules: [{
|
||||
validator(_: any, value: any) {
|
||||
asyncValidator: async (rule: any, value: any) => {
|
||||
|
||||
if (!value?.type) {
|
||||
return Promise.reject('请选择数据类型')
|
||||
}
|
||||
|
@ -568,78 +451,56 @@ export const useColumns = (type?: MetadataType, target?: 'device' | 'product', n
|
|||
}
|
||||
}]
|
||||
},
|
||||
doubleClick(record) {
|
||||
if (isExtendsProduct(record.id, productNoEdit?.value, 'valueType')) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
control(newValue, oldValue) {
|
||||
if (newValue && !oldValue) {
|
||||
return true
|
||||
} else if (newValue && oldValue) {
|
||||
return JSON.stringify(newValue.valueType) !== JSON.stringify(oldValue.valueType)
|
||||
}
|
||||
return false
|
||||
},
|
||||
// sort: {
|
||||
// sortKey: ['valueType', 'type'],
|
||||
// dataSource: () => {
|
||||
// const group = groupBy(dataSource.value.filter(item => item.id && item.valueType.type), (e) => e.valueType.type)
|
||||
// const typeMap = getTypeMap()
|
||||
// return Object.keys(group).map(key => {
|
||||
// return {
|
||||
// name: typeMap[key],
|
||||
// key: key,
|
||||
// total: group[key].length
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
},
|
||||
// {
|
||||
// title: '读写类型',
|
||||
// dataIndex: 'expands',
|
||||
// width: 190,
|
||||
// form: {
|
||||
// rules: [
|
||||
// {
|
||||
// asyncValidator: async (rule: any, value: any) => {
|
||||
// if (!value?.type?.length) {
|
||||
// return Promise.reject('请选择读写类型')
|
||||
// }
|
||||
// return Promise.resolve()
|
||||
// }
|
||||
// }]
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// title: '属性分组',
|
||||
// dataIndex: 'group',
|
||||
// width: 140,
|
||||
// },
|
||||
{
|
||||
title: '读写类型',
|
||||
dataIndex: 'readType',
|
||||
type: 'components',
|
||||
components: {
|
||||
name: TagsType
|
||||
},
|
||||
doubleClick(record) {
|
||||
if (isExtendsProduct(record.id, productNoEdit?.value, 'readType')) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
form: {
|
||||
required: true,
|
||||
rules: [
|
||||
{
|
||||
callback(rule:any,value: any, dataSource: any[]) {
|
||||
const field = rule.field.split('.')
|
||||
const fieldIndex = Number(field[1])
|
||||
const values = dataSource.find((item, index) => index === fieldIndex)
|
||||
if (!values?.expands?.type?.length) {
|
||||
return Promise.reject('请选择读写类型')
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
}]
|
||||
},
|
||||
control(newValue, oldValue) {
|
||||
if (newValue && !oldValue) {
|
||||
return true
|
||||
} else if (newValue && oldValue) {
|
||||
return JSON.stringify(newValue.expands) !== JSON.stringify(oldValue.expands)
|
||||
}
|
||||
return false
|
||||
},
|
||||
title: '其它配置',
|
||||
dataIndex: 'other',
|
||||
width: 110,
|
||||
},
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
type: 'text',
|
||||
width: 250,
|
||||
form: {
|
||||
rules: [
|
||||
{ max: 20, message: '最多可输入20个字符' },
|
||||
]},
|
||||
doubleClick(record) {
|
||||
if (isExtendsProduct(record.id, productNoEdit?.value, 'description')) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
const columns = ref<any[]>([])
|
||||
|
@ -680,10 +541,30 @@ export const useUnit = (type: Ref<string>) => {
|
|||
}
|
||||
}, { immediate: true })
|
||||
|
||||
|
||||
return { unitOptions }
|
||||
}
|
||||
|
||||
export const useSaveUnit = () => {
|
||||
const unitOptions = ref<Array<{ label: string, value: any }>>([])
|
||||
|
||||
provide(METADATA_UNIT, unitOptions)
|
||||
|
||||
getUnit().then((res) => {
|
||||
if (res.success) {
|
||||
unitOptions.value = res.result.map((item: any) => ({
|
||||
label: item.description,
|
||||
value: item.id,
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
unitOptions
|
||||
}
|
||||
}
|
||||
|
||||
export const useGetUnit = () => inject(METADATA_UNIT)
|
||||
|
||||
|
||||
export const TypeStringMap = {
|
||||
int: 'int(整数型)',
|
||||
|
|
|
@ -1,22 +1,33 @@
|
|||
<template>
|
||||
<j-popconfirm-modal
|
||||
<PopoverModal
|
||||
v-if="!disabled"
|
||||
v-model:visible="modalVisible"
|
||||
body-style="padding-top:4px;width:600px;"
|
||||
placement="bottomRight"
|
||||
:disabled="disabled"
|
||||
:get-popup-container="(node) => fullRef || node"
|
||||
@confirm="confirm"
|
||||
@ok="confirm"
|
||||
@cancel="cancel"
|
||||
@visibleChange="visibleChange"
|
||||
>
|
||||
<template v-if="visible" #content>
|
||||
<j-scrollbar height="350" v-if="showMetrics || config.length > 0">
|
||||
<j-collapse v-model:activeKey="activeKey">
|
||||
<template #content>
|
||||
<j-scrollbar height="350" v-if="showContent">
|
||||
<j-collapse v-model:activeKey="activeKey" v-if="visible">
|
||||
<j-collapse-panel
|
||||
v-if="!(props.isProduct && target === 'device')"
|
||||
v-for="(item, index) in config"
|
||||
:key="'store_' + index"
|
||||
:header="item.name"
|
||||
>
|
||||
<template #header>
|
||||
{{ item.name }}
|
||||
<j-tooltip
|
||||
v-if="item.description"
|
||||
:title="item.description"
|
||||
>
|
||||
<AIcon
|
||||
type="ExclamationCircleOutlined"
|
||||
style="padding-left: 12px; padding-top: 4px"
|
||||
/>
|
||||
</j-tooltip>
|
||||
</template>
|
||||
<j-table
|
||||
:columns="columns"
|
||||
:data-source="item.properties"
|
||||
|
@ -35,18 +46,37 @@
|
|||
:itemType="
|
||||
item.properties[index].type?.type
|
||||
"
|
||||
:options="(item.properties[index].type?.elements || []).map((a:any) => ({
|
||||
label: a.text,
|
||||
value: a.value,
|
||||
}))"
|
||||
:get-popup-container="
|
||||
(node) => fullRef || node
|
||||
:extra="{
|
||||
dropdownStyle: {
|
||||
zIndex: 1071,
|
||||
},
|
||||
popupStyle: {
|
||||
zIndex: 1071,
|
||||
},
|
||||
}"
|
||||
:getPopupContainer="
|
||||
(node) => tableWrapperRef || node
|
||||
"
|
||||
:options="
|
||||
(
|
||||
item.properties[index].type
|
||||
?.elements || []
|
||||
).map((a) => ({
|
||||
label: a.text,
|
||||
value: a.value,
|
||||
}))
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
</j-table>
|
||||
</j-collapse-panel>
|
||||
<j-collapse-panel key="metrics" v-if="showMetrics">
|
||||
<j-collapse-panel
|
||||
key="metrics"
|
||||
v-if="
|
||||
showMetrics &&
|
||||
!(props.isProduct && target === 'device')
|
||||
"
|
||||
>
|
||||
<template #header>
|
||||
指标配置
|
||||
<j-tooltip
|
||||
|
@ -65,6 +95,136 @@
|
|||
:value="myValue.metrics"
|
||||
/>
|
||||
</j-collapse-panel>
|
||||
<j-collapse-panel key="extra" v-if="showExtra">
|
||||
<template #header>
|
||||
<a-space>
|
||||
<div>阈值限制</div>
|
||||
<template
|
||||
v-if="
|
||||
props.isProduct && target === 'device'
|
||||
"
|
||||
>
|
||||
<a-button
|
||||
type="link"
|
||||
style="padding: 0 4px; height: 22px"
|
||||
@click="(e) => resetThreshold(e)"
|
||||
>
|
||||
<template #icon>
|
||||
<AIcon type="RedoOutlined" />
|
||||
</template>
|
||||
重置
|
||||
</a-button>
|
||||
<a-tooltip>
|
||||
<template #title
|
||||
>重置后阀值限制继承于产品</template
|
||||
>
|
||||
<AIcon type="QuestionCircleOutlined" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-button
|
||||
v-else
|
||||
type="link"
|
||||
style="padding: 0 4px; height: 22px"
|
||||
@click="(e) => resetThreshold(e)"
|
||||
>
|
||||
<template #icon>
|
||||
<AIcon type="RedoOutlined" />
|
||||
</template>
|
||||
清空
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<a-form
|
||||
:model="extraForm"
|
||||
layout="vertical"
|
||||
ref="ThresholdRef"
|
||||
>
|
||||
<a-form-item>
|
||||
<div class="extra-limit extra-check-group">
|
||||
<CardSelect
|
||||
v-model:value="extraForm.type"
|
||||
:options="[
|
||||
{
|
||||
label: '上下限',
|
||||
value: 'number-range',
|
||||
},
|
||||
]"
|
||||
:showImage="false"
|
||||
@select="limitSelect"
|
||||
/>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="extraForm.type"
|
||||
name="limit"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入上下限',
|
||||
},
|
||||
{
|
||||
validator: validateLimit,
|
||||
trigger: 'change',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #label
|
||||
><div class="extra-title">
|
||||
阈值
|
||||
</div></template
|
||||
>
|
||||
<a-space
|
||||
v-if="extraForm.type === 'number-range'"
|
||||
>
|
||||
<a-input-number
|
||||
v-model:value="extraForm.limit.lower"
|
||||
style="width: 178px"
|
||||
placeholder="请输入下限"
|
||||
/>
|
||||
<span>~</span>
|
||||
<a-input-number
|
||||
v-model:value="extraForm.limit.upper"
|
||||
style="width: 178px"
|
||||
:min="extraForm.limit.lower"
|
||||
placeholder="请输入上限"
|
||||
/>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="extraForm.type"
|
||||
name="mode"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请选择处理方式',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<template #label>
|
||||
<div class="extra-title">
|
||||
超出阈值数据处理方式
|
||||
</div></template
|
||||
>
|
||||
<div class="extra-handle extra-check-group">
|
||||
<CardSelect
|
||||
v-model:value="extraForm.mode"
|
||||
:options="[
|
||||
{ label: '忽略', value: 'ignore' },
|
||||
{ label: '记录', value: 'record' },
|
||||
{
|
||||
label: '告警',
|
||||
value: 'device-alarm',
|
||||
},
|
||||
]"
|
||||
:showImage="false"
|
||||
/>
|
||||
<div style="margin: 8px 0">
|
||||
{{ handleTip }}
|
||||
</div>
|
||||
</div></a-form-item
|
||||
>
|
||||
</a-form>
|
||||
</j-collapse-panel>
|
||||
</j-collapse>
|
||||
</j-scrollbar>
|
||||
<div v-else style="padding-top: 24px">
|
||||
|
@ -76,23 +236,21 @@
|
|||
:disabled="disabled"
|
||||
:has-permission="hasPermission"
|
||||
:tooltip="tooltip"
|
||||
style="padding-left: 0"
|
||||
type="link"
|
||||
type="primary"
|
||||
>
|
||||
<AIcon type="SettingOutlined" />
|
||||
<AIcon type="EditOutlined" />
|
||||
配置
|
||||
</PermissionButton>
|
||||
</j-popconfirm-modal>
|
||||
</PopoverModal>
|
||||
<PermissionButton
|
||||
v-else
|
||||
key="setting"
|
||||
:disabled="disabled"
|
||||
:has-permission="hasPermission"
|
||||
:tooltip="tooltip"
|
||||
style="padding-left: 0"
|
||||
type="link"
|
||||
type="primary"
|
||||
>
|
||||
<AIcon type="SettingOutlined" />
|
||||
<AIcon type="EditOutlined" />
|
||||
配置
|
||||
</PermissionButton>
|
||||
</template>
|
||||
|
@ -106,9 +264,10 @@ import {
|
|||
getMetadataConfig,
|
||||
getMetadataDeviceConfig,
|
||||
} from '@/api/device/product';
|
||||
import ModelButton from '@/views/device/components/Metadata/Base/components/ModelButton.vue';
|
||||
import { omit, cloneDeep } from 'lodash-es';
|
||||
import { FULL_CODE } from 'jetlinks-ui-components/es/DataTable';
|
||||
import { PopoverModal } from '@/components/Metadata/Table';
|
||||
import { useTableWrapper } from '@/components/Metadata/Table/context';
|
||||
import { useThreshold } from './hooks';
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
|
@ -127,37 +286,87 @@ const props = defineProps({
|
|||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
record: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
medataType: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
hasPermission: String,
|
||||
tooltip: Object,
|
||||
metadataType: {
|
||||
type: String,
|
||||
default: 'properties',
|
||||
},
|
||||
target: String,
|
||||
isProduct: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const fullRef = inject(FULL_CODE);
|
||||
|
||||
const type = inject('_metadataType');
|
||||
|
||||
const productStore = useProductStore();
|
||||
const deviceStore = useInstanceStore();
|
||||
const tableWrapperRef = useTableWrapper();
|
||||
|
||||
const emit = defineEmits(['update:value']);
|
||||
const {
|
||||
thresholdUpdate,
|
||||
thresholdDetail,
|
||||
thresholdDetailQuery,
|
||||
thresholdDelete,
|
||||
} = useThreshold(props);
|
||||
|
||||
const emit = defineEmits(['update:value', 'change']);
|
||||
|
||||
const ThresholdRef = ref();
|
||||
const activeKey = ref();
|
||||
const storageRef = ref();
|
||||
const metricsRef = ref();
|
||||
|
||||
const myValue = ref(props.value);
|
||||
const visible = ref(false);
|
||||
const modalVisible = ref(false);
|
||||
|
||||
const config = ref<any>([]);
|
||||
const configValue = ref(props.value?.expands);
|
||||
|
||||
const extraForm = reactive({
|
||||
limit: {
|
||||
upper: 0,
|
||||
lower: 0,
|
||||
},
|
||||
mode: 'ignore',
|
||||
type: '',
|
||||
});
|
||||
|
||||
const typeMap = {
|
||||
properties: 'property',
|
||||
functions: 'function',
|
||||
events: 'event',
|
||||
tags: 'tag',
|
||||
};
|
||||
|
||||
const handleTip = computed(() => {
|
||||
if (extraForm.mode === 'ignore') {
|
||||
return '平台将忽略超出阈值的数据,无法查看上报记录';
|
||||
} else if (extraForm.mode === 'record') {
|
||||
return '您可以在预处理数据-无效数据页面查看超出阈值的数据上报记录';
|
||||
}
|
||||
return '您可以在预处理数据-告警数据页面查看告警情况';
|
||||
});
|
||||
|
||||
const showContent = computed(() => {
|
||||
if (props.isProduct && props.target === 'device') {
|
||||
// 继承的物模型
|
||||
return showExtra.value;
|
||||
}
|
||||
|
||||
return showMetrics.value || config.value.length > 0;
|
||||
});
|
||||
|
||||
const showMetrics = computed(() => {
|
||||
return [
|
||||
'int',
|
||||
|
@ -170,6 +379,13 @@ const showMetrics = computed(() => {
|
|||
].includes(props.type as any);
|
||||
});
|
||||
|
||||
const showExtra = computed(() => {
|
||||
return (
|
||||
['int', 'long', 'float', 'double'].includes(props.type as any) &&
|
||||
props.metadataType === 'properties'
|
||||
);
|
||||
});
|
||||
|
||||
const booleanOptions = ref([
|
||||
{ label: '否', value: 'false' },
|
||||
{ label: '是', value: 'true' },
|
||||
|
@ -193,16 +409,41 @@ const columns = ref([
|
|||
},
|
||||
]);
|
||||
|
||||
const getType = () => {
|
||||
const _typeMap = {
|
||||
'propertys': 'property',
|
||||
'functions': 'function',
|
||||
'events': 'event',
|
||||
'tags': 'tag',
|
||||
}
|
||||
const limitSelect = (keys: string[], key: string, isSelected: boolean) => {
|
||||
if (!isSelected) {
|
||||
// 删除
|
||||
if (key === 'number-range') {
|
||||
extraForm.limit.lower = 0;
|
||||
extraForm.limit.upper = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return _typeMap[props.type] || 'property'
|
||||
}
|
||||
if (keys.length === 0) {
|
||||
extraForm.mode = 'ignore';
|
||||
}
|
||||
};
|
||||
const resetValue = () => {
|
||||
extraForm.mode = 'ignore';
|
||||
extraForm.type = '';
|
||||
extraForm.limit.lower = 0;
|
||||
extraForm.limit.upper = 0;
|
||||
};
|
||||
const resetThreshold = async (e: any) => {
|
||||
e.stopPropagation();
|
||||
await thresholdDelete();
|
||||
resetValue();
|
||||
thresholdDetailQuery();
|
||||
};
|
||||
|
||||
const validateLimit = (_: any, value: any) => {
|
||||
if (value.lower !== null && value.upper !== null) {
|
||||
return value.upper < value.lower
|
||||
? Promise.reject('上限必须大于下限')
|
||||
: Promise.resolve();
|
||||
} else {
|
||||
return Promise.reject('请输入上下限');
|
||||
}
|
||||
};
|
||||
|
||||
const getConfig = async () => {
|
||||
const id =
|
||||
|
@ -226,7 +467,7 @@ const getConfig = async () => {
|
|||
deviceId: id,
|
||||
metadata: {
|
||||
id: props.id,
|
||||
type: getType(),
|
||||
type: typeMap[props.metadataType],
|
||||
dataType: props.type,
|
||||
},
|
||||
};
|
||||
|
@ -235,14 +476,16 @@ const getConfig = async () => {
|
|||
type === 'product'
|
||||
? await getMetadataConfig(params)
|
||||
: await getMetadataDeviceConfig(params);
|
||||
if (resp.status === 200) {
|
||||
if (resp.success) {
|
||||
config.value = resp.result;
|
||||
if (resp.result.length) {
|
||||
activeKey.value = ['store_0'];
|
||||
} else if (showMetrics.value) {
|
||||
activeKey.value = ['metrics'];
|
||||
}
|
||||
|
||||
if(showExtra.value){
|
||||
activeKey.value = ['extra']
|
||||
}
|
||||
if (resp.result.length && !configValue.value) {
|
||||
resp.result.forEach((a) => {
|
||||
if (a.properties) {
|
||||
|
@ -253,6 +496,7 @@ const getConfig = async () => {
|
|||
});
|
||||
}
|
||||
}
|
||||
visible.value = true;
|
||||
};
|
||||
|
||||
const confirm = () => {
|
||||
|
@ -268,43 +512,95 @@ const confirm = () => {
|
|||
if (metrics) {
|
||||
expands.metrics = metrics;
|
||||
}
|
||||
emit('update:value', {
|
||||
...props.value,
|
||||
...expands,
|
||||
});
|
||||
resolve(true);
|
||||
if (showExtra.value && extraForm.type) {
|
||||
ThresholdRef.value?.validate().then(async () => {
|
||||
await thresholdUpdate(extraForm);
|
||||
expands.otherEdit = true;
|
||||
emit('update:value', {
|
||||
...props.value,
|
||||
...expands,
|
||||
});
|
||||
emit('change');
|
||||
modalVisible.value = false;
|
||||
resolve(true);
|
||||
});
|
||||
} else {
|
||||
expands.otherEdit = true;
|
||||
emit('update:value', {
|
||||
...props.value,
|
||||
...expands,
|
||||
});
|
||||
emit('change');
|
||||
modalVisible.value = false;
|
||||
resolve(true);
|
||||
}
|
||||
} catch (err) {
|
||||
reject(false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const visibleChange = (e: boolean) => {
|
||||
visible.value = e;
|
||||
console.log('visibleChange',e)
|
||||
if (e) {
|
||||
configValue.value = omit(props.value, [
|
||||
'source',
|
||||
'type',
|
||||
'metrics',
|
||||
'required',
|
||||
]);
|
||||
getConfig();
|
||||
}
|
||||
};
|
||||
watch(
|
||||
() => modalVisible.value,
|
||||
async () => {
|
||||
if (modalVisible.value) {
|
||||
configValue.value = omit(props.value, [
|
||||
'source',
|
||||
'type',
|
||||
'metrics',
|
||||
'required',
|
||||
]);
|
||||
getConfig();
|
||||
if (showExtra.value) {
|
||||
thresholdDetailQuery();
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const cancel = () => {
|
||||
myValue.value = cloneDeep(props.value);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
() => thresholdDetail,
|
||||
() => {
|
||||
console.log(props.value);
|
||||
myValue.value = cloneDeep(props.value);
|
||||
if (
|
||||
thresholdDetail.value &&
|
||||
JSON.stringify(thresholdDetail.value) !== '{}'
|
||||
) {
|
||||
extraForm.mode = thresholdDetail.value?.mode;
|
||||
extraForm.type = thresholdDetail.value?.type || '';
|
||||
extraForm.limit = thresholdDetail.value?.limit;
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => JSON.stringify(props.value),
|
||||
() => {
|
||||
myValue.value = cloneDeep(props.value);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped lang="less">
|
||||
.extra-tip {
|
||||
padding: 8px;
|
||||
background-color: rgba(#000, 0.05);
|
||||
}
|
||||
|
||||
.extra-title {
|
||||
font-size: 16px;
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
.extra-check-group {
|
||||
:deep(.j-card-item) {
|
||||
padding: 12px 14px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,31 +1,31 @@
|
|||
import { onlyMessage } from "@/utils/comm"
|
||||
export const testProperties = (data:any) =>{
|
||||
|
||||
|
||||
}
|
||||
|
||||
export const testType = (data:any,index:number,isArray?:boolean,isObject?:boolean)=>{
|
||||
if(data.type === 'boolean'){
|
||||
if(!data?.trueText || !data?.trueValue || !data?.falseText || !data?.falseValue){
|
||||
onlyMessage(`方法定义inputs第${index+1}个数组ValueType中缺失必填属性`,'error')
|
||||
return true
|
||||
}
|
||||
}
|
||||
if(data.type === 'enum' && !isObject){
|
||||
if(data?.elements?.length > 0){
|
||||
data.elements.forEach((a:any,b:number)=>{
|
||||
if(!a.value || !a.text){
|
||||
onlyMessage(`方法定义inputs第${index+1}个数组ValueType中elements缺失必填属性`,'error')
|
||||
return true
|
||||
}
|
||||
})
|
||||
}else{
|
||||
onlyMessage(`方法定义inputs第${index+1}个数组ValueType中缺失elements属性`,'error')
|
||||
return true
|
||||
}
|
||||
}
|
||||
// if(data.type === 'boolean'){
|
||||
// if(!data?.trueText || !data?.trueValue || !data?.falseText || !data?.falseValue){
|
||||
// onlyMessage(`方法定义inputs第${index+1}个数组ValueType中缺失必填属性`,'error')
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// if(data.type === 'enum' && !isObject){
|
||||
// if(data?.elements?.length > 0){
|
||||
// data.elements.forEach((a:any,b:number)=>{
|
||||
// if(!a.value || !a.text){
|
||||
// onlyMessage(`方法定义inputs第${index+1}个数组ValueType中elements缺失必填属性`,'error')
|
||||
// return true
|
||||
// }
|
||||
// })
|
||||
// }else{
|
||||
// onlyMessage(`方法定义inputs第${index+1}个数组ValueType中缺失elements属性`,'error')
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
if(data.type === 'array' && !isArray && !isObject){
|
||||
if(data?.elementType){
|
||||
testType(data.elementType,index,true)
|
||||
return testType(data.elementType,index,true)
|
||||
}else{
|
||||
onlyMessage(`方法定义inputs第${index+1}个数组ValueType中缺失elementType属性`,'error')
|
||||
return true
|
||||
|
@ -52,20 +52,20 @@ export const testObject = (data:any,index:number)=>{
|
|||
if(!i?.id){
|
||||
onlyMessage(`方法定义inputs第${index+1}个数组中缺失id属性`,'error')
|
||||
check = true
|
||||
return
|
||||
return
|
||||
}
|
||||
if(!i?.name){
|
||||
onlyMessage(`方法定义inputs第${index+1}个数组中缺失name属性`,'error')
|
||||
check = true
|
||||
return
|
||||
return
|
||||
}
|
||||
if(!i?.valueType?.type){
|
||||
onlyMessage(`方法定义inputs第${index+1}个数组中缺失valueType.type属性`,'error')
|
||||
check = true
|
||||
return
|
||||
return
|
||||
}else{
|
||||
check = testType(i,index,false,true)
|
||||
return
|
||||
return
|
||||
}
|
||||
})
|
||||
return check
|
||||
|
@ -121,21 +121,21 @@ export const testAliObject = (data:any,index:number)=>{
|
|||
if(!i?.identifier){
|
||||
onlyMessage(`方法定义inputs第${index+1}个数组中缺失id属性`,'error')
|
||||
check = true
|
||||
return
|
||||
return
|
||||
}
|
||||
if(!i?.name){
|
||||
onlyMessage(`方法定义inputs第${index+1}个数组中缺失name属性`,'error')
|
||||
check = true
|
||||
return
|
||||
return
|
||||
}
|
||||
if(!i?.dataType?.type){
|
||||
onlyMessage(`方法定义inputs第${index+1}个数组中缺失dataType.type属性`,'error')
|
||||
check = true
|
||||
return
|
||||
return
|
||||
}else{
|
||||
check = testAliType(i,index,false,true)
|
||||
return
|
||||
return
|
||||
}
|
||||
})
|
||||
return check
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,203 +1,487 @@
|
|||
<!-- 物联卡查看 -->
|
||||
<template>
|
||||
<page-container>
|
||||
<page-container v-if="type === 'card'">
|
||||
<!-- 新增、编辑 -->
|
||||
<Save
|
||||
v-if="visible"
|
||||
:type="saveType"
|
||||
:data="current"
|
||||
@change="saveChange"
|
||||
/>
|
||||
<j-row :gutter="[24, 24]">
|
||||
<j-col :span="24">
|
||||
<j-card>
|
||||
<j-descriptions size="small" :column="3" bordered>
|
||||
<template #title>
|
||||
<Guide>
|
||||
<template #title>
|
||||
<span>基本信息</span>
|
||||
<j-button
|
||||
type="link"
|
||||
@click="
|
||||
() => {
|
||||
visible = true;
|
||||
current = detail;
|
||||
saveType = 'edit';
|
||||
}
|
||||
"
|
||||
>
|
||||
<AIcon type="EditOutlined"></AIcon>
|
||||
编辑
|
||||
</j-button>
|
||||
</template>
|
||||
</Guide>
|
||||
</template>
|
||||
<div>
|
||||
<Save
|
||||
v-if="visible"
|
||||
:type="saveType"
|
||||
:data="current"
|
||||
@change="saveChange"
|
||||
/>
|
||||
<j-row :gutter="[24, 24]">
|
||||
<j-col :span="24">
|
||||
<j-card>
|
||||
<j-descriptions size="small" :column="3" bordered>
|
||||
<template #title>
|
||||
<Guide>
|
||||
<template #title>
|
||||
<span>基本信息</span>
|
||||
<j-button
|
||||
type="link"
|
||||
@click="
|
||||
() => {
|
||||
visible = true;
|
||||
current = detail;
|
||||
saveType = 'edit';
|
||||
}
|
||||
"
|
||||
>
|
||||
<AIcon type="EditOutlined"></AIcon>
|
||||
编辑
|
||||
</j-button>
|
||||
</template>
|
||||
</Guide>
|
||||
</template>
|
||||
|
||||
<j-descriptions-item label="卡号">{{
|
||||
detail.id
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="ICCID">{{
|
||||
detail.iccId
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="绑定设备">{{
|
||||
detail.deviceName
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="平台类型">{{
|
||||
detail.operatorPlatformType?.text
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="平台名称">{{
|
||||
detail.platformConfigName
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="运营商">{{
|
||||
detail.operatorName
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="类型">{{
|
||||
detail.cardType?.text
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="激活日期">{{
|
||||
detail.activationDate
|
||||
? moment(detail.activationDate).format(
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
)
|
||||
: ''
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="更新时间">{{
|
||||
detail.updateTime
|
||||
? moment(detail.updateTime).format(
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
)
|
||||
: ''
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="总流量">{{
|
||||
detail.totalFlow
|
||||
? detail.totalFlow.toFixed(2) + ' M'
|
||||
: '0 M'
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="使用流量">{{
|
||||
detail.usedFlow
|
||||
? detail.usedFlow.toFixed(2) + ' M'
|
||||
: '0 M'
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="剩余流量">{{
|
||||
detail.residualFlow
|
||||
? detail.residualFlow.toFixed(2) + ' M'
|
||||
: '0 M'
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="状态">
|
||||
{{
|
||||
detail?.cardState?.text
|
||||
}}
|
||||
<span v-if="deactivateData.show" style="padding-left: 8px;">
|
||||
<a-tooltip
|
||||
:title="deactivateData.tip"
|
||||
>
|
||||
<AIcon type="ExclamationCircleOutlined" style="color: var(--ant-error-color);"/>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
</j-descriptions-item>
|
||||
<j-descriptions-item label="说明">{{
|
||||
detail?.describe
|
||||
}}</j-descriptions-item>
|
||||
</j-descriptions>
|
||||
</j-card>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<!-- 流量统计 -->
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="16">
|
||||
<div class="card">
|
||||
<Guide title="流量统计">
|
||||
<template #extra>
|
||||
<TimeSelect
|
||||
:type="'week'"
|
||||
:quickBtnList="quickBtnList"
|
||||
@change="getEcharts"
|
||||
/>
|
||||
</template>
|
||||
</Guide>
|
||||
<LineChart
|
||||
:showX="true"
|
||||
:showY="true"
|
||||
style="min-height: 490px"
|
||||
:chartData="flowData"
|
||||
/>
|
||||
</div>
|
||||
</j-col>
|
||||
<j-col :span="8">
|
||||
<div class="card">
|
||||
<Guide title="数据统计" />
|
||||
<div class="static-info" style="min-height: 490px">
|
||||
<div class="data-statistics-item">
|
||||
<div class="flow-info" style="width: 100%">
|
||||
<div class="label">昨日流量消耗</div>
|
||||
<j-tooltip placement="bottomLeft">
|
||||
<template #title>
|
||||
<span>{{ dayTotal }} M</span>
|
||||
</template>
|
||||
<div class="value">
|
||||
{{ dayTotal }}
|
||||
<span class="unit">M</span>
|
||||
<j-descriptions-item label="卡号">{{
|
||||
detail.id
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="ICCID">{{
|
||||
detail.iccId
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="绑定设备">{{
|
||||
detail.deviceName
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="平台类型">{{
|
||||
platformTypeList.find(
|
||||
(item) =>
|
||||
item.value ===
|
||||
detail.operatorPlatformType?.text,
|
||||
)?.label || detail.operatorPlatformType?.text
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="平台名称">{{
|
||||
detail.platformConfigName
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="运营商">{{
|
||||
OperatorList.find(
|
||||
(item) =>
|
||||
item.value === detail.operatorName,
|
||||
)?.label || detail.operatorName
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="类型">{{
|
||||
detail.cardType?.text
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="激活日期">{{
|
||||
detail.activationDate
|
||||
? moment(detail.activationDate).format(
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
)
|
||||
: ''
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="更新时间">{{
|
||||
detail.updateTime
|
||||
? moment(detail.updateTime).format(
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
)
|
||||
: ''
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="总流量">{{
|
||||
detail.totalFlow
|
||||
? detail.totalFlow.toFixed(2) + ' M'
|
||||
: '0 M'
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="使用流量">{{
|
||||
detail.usedFlow
|
||||
? detail.usedFlow.toFixed(2) + ' M'
|
||||
: '0 M'
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="剩余流量">{{
|
||||
detail.residualFlow
|
||||
? detail.residualFlow.toFixed(2) + ' M'
|
||||
: '0 M'
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="运营商状态">
|
||||
{{ detail?.cardState?.text }}
|
||||
<span
|
||||
v-if="deactivateData.show"
|
||||
style="padding-left: 8px"
|
||||
>
|
||||
<a-tooltip :title="deactivateData.tip">
|
||||
<AIcon
|
||||
type="ExclamationCircleOutlined"
|
||||
style="
|
||||
color: var(--ant-error-color);
|
||||
"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
</j-descriptions-item>
|
||||
<j-descriptions-item label="平台状态">
|
||||
{{ detail?.cardStateType?.text }}
|
||||
</j-descriptions-item>
|
||||
<j-descriptions-item label="说明">{{
|
||||
detail?.describe
|
||||
}}</j-descriptions-item>
|
||||
</j-descriptions>
|
||||
</j-card>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<!-- 流量统计 -->
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="16">
|
||||
<div class="card">
|
||||
<Guide title="流量统计">
|
||||
<template #extra>
|
||||
<TimeSelect
|
||||
:type="'week'"
|
||||
:quickBtnList="quickBtnList"
|
||||
@change="getEcharts"
|
||||
/>
|
||||
</template>
|
||||
</Guide>
|
||||
<LineChart
|
||||
:showX="true"
|
||||
:showY="true"
|
||||
style="min-height: 490px"
|
||||
:chartData="flowData"
|
||||
/>
|
||||
</div>
|
||||
</j-col>
|
||||
<j-col :span="8">
|
||||
<div class="card">
|
||||
<Guide title="数据统计" />
|
||||
<div
|
||||
class="static-info"
|
||||
style="min-height: 490px"
|
||||
>
|
||||
<div class="data-statistics-item">
|
||||
<div
|
||||
class="flow-info"
|
||||
style="width: 100%"
|
||||
>
|
||||
<div class="label">
|
||||
昨日流量消耗
|
||||
</div>
|
||||
</j-tooltip>
|
||||
<j-tooltip placement="bottomLeft">
|
||||
<template #title>
|
||||
<span
|
||||
>{{ dayTotal }} M</span
|
||||
>
|
||||
</template>
|
||||
<div class="value">
|
||||
{{ dayTotal }}
|
||||
<span class="unit">M</span>
|
||||
</div>
|
||||
</j-tooltip>
|
||||
</div>
|
||||
<LineChart
|
||||
color="#FBA500"
|
||||
:chartData="dayOptions"
|
||||
/>
|
||||
</div>
|
||||
<LineChart
|
||||
color="#FBA500"
|
||||
:chartData="dayOptions"
|
||||
/>
|
||||
</div>
|
||||
<div class="data-statistics-item">
|
||||
<div class="flow-info" style="width: 100%">
|
||||
<div class="label">当月流量消耗</div>
|
||||
<j-tooltip placement="bottomLeft">
|
||||
<template #title>
|
||||
<span>{{ monthTotal }} M</span>
|
||||
</template>
|
||||
<div class="value">
|
||||
{{ monthTotal }}
|
||||
<span class="unit">M</span>
|
||||
<div class="data-statistics-item">
|
||||
<div
|
||||
class="flow-info"
|
||||
style="width: 100%"
|
||||
>
|
||||
<div class="label">
|
||||
当月流量消耗
|
||||
</div>
|
||||
</j-tooltip>
|
||||
<j-tooltip placement="bottomLeft">
|
||||
<template #title>
|
||||
<span
|
||||
>{{
|
||||
monthTotal
|
||||
}}
|
||||
M</span
|
||||
>
|
||||
</template>
|
||||
<div class="value">
|
||||
{{ monthTotal }}
|
||||
<span class="unit">M</span>
|
||||
</div>
|
||||
</j-tooltip>
|
||||
</div>
|
||||
<LineChart :chartData="monthOptions" />
|
||||
</div>
|
||||
<LineChart :chartData="monthOptions" />
|
||||
</div>
|
||||
<div class="data-statistics-item">
|
||||
<div class="flow-info" style="width: 100%">
|
||||
<div class="label">本年流量消耗</div>
|
||||
<j-tooltip placement="bottomLeft">
|
||||
<template #title>
|
||||
<span>{{ yearTotal }} M</span>
|
||||
</template>
|
||||
<div class="value">
|
||||
{{ yearTotal }}
|
||||
<span class="unit">M</span>
|
||||
<div class="data-statistics-item">
|
||||
<div
|
||||
class="flow-info"
|
||||
style="width: 100%"
|
||||
>
|
||||
<div class="label">
|
||||
本年流量消耗
|
||||
</div>
|
||||
</j-tooltip>
|
||||
<j-tooltip placement="bottomLeft">
|
||||
<template #title>
|
||||
<span
|
||||
>{{ yearTotal }} M</span
|
||||
>
|
||||
</template>
|
||||
<div class="value">
|
||||
{{ yearTotal }}
|
||||
<span class="unit">M</span>
|
||||
</div>
|
||||
</j-tooltip>
|
||||
</div>
|
||||
<LineChart
|
||||
color="#58E1D3"
|
||||
:chartData="yearOptions"
|
||||
/>
|
||||
</div>
|
||||
<LineChart
|
||||
color="#58E1D3"
|
||||
:chartData="yearOptions"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</div>
|
||||
</page-container>
|
||||
<div v-else>
|
||||
<div v-if="cardId">
|
||||
<Save
|
||||
v-if="visible"
|
||||
:type="saveType"
|
||||
:data="current"
|
||||
@change="saveChange"
|
||||
/>
|
||||
<j-row :gutter="[24, 24]">
|
||||
<j-col :span="24">
|
||||
<j-card>
|
||||
<j-descriptions size="small" :column="3" bordered>
|
||||
<template #title>
|
||||
<Guide>
|
||||
<template #title>
|
||||
<span>基本信息</span>
|
||||
<j-button
|
||||
type="link"
|
||||
@click="
|
||||
() => {
|
||||
visible = true;
|
||||
current = detail;
|
||||
saveType = 'edit';
|
||||
}
|
||||
"
|
||||
>
|
||||
<AIcon type="EditOutlined"></AIcon>
|
||||
编辑
|
||||
</j-button>
|
||||
</template>
|
||||
</Guide>
|
||||
</template>
|
||||
|
||||
<j-descriptions-item label="卡号">{{
|
||||
detail.id
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="ICCID">{{
|
||||
detail.iccId
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="绑定设备">{{
|
||||
detail.deviceName
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="平台类型">{{
|
||||
platformTypeList.find(
|
||||
(item) =>
|
||||
item.value ===
|
||||
detail.operatorPlatformType?.text,
|
||||
)?.label || detail.operatorPlatformType?.text
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="平台名称">{{
|
||||
detail.platformConfigName
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="运营商">{{
|
||||
OperatorList.find(
|
||||
(item) =>
|
||||
item.value === detail.operatorName,
|
||||
)?.label || detail.operatorName
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="类型">{{
|
||||
detail.cardType?.text
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="激活日期">{{
|
||||
detail.activationDate
|
||||
? moment(detail.activationDate).format(
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
)
|
||||
: ''
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="更新时间">{{
|
||||
detail.updateTime
|
||||
? moment(detail.updateTime).format(
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
)
|
||||
: ''
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="总流量">{{
|
||||
detail.totalFlow
|
||||
? detail.totalFlow.toFixed(2) + ' M'
|
||||
: '0 M'
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="使用流量">{{
|
||||
detail.usedFlow
|
||||
? detail.usedFlow.toFixed(2) + ' M'
|
||||
: '0 M'
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="剩余流量">{{
|
||||
detail.residualFlow
|
||||
? detail.residualFlow.toFixed(2) + ' M'
|
||||
: '0 M'
|
||||
}}</j-descriptions-item>
|
||||
<j-descriptions-item label="运营商状态">
|
||||
{{ detail?.cardState?.text }}
|
||||
<span
|
||||
v-if="deactivateData.show"
|
||||
style="padding-left: 8px"
|
||||
>
|
||||
<a-tooltip :title="deactivateData.tip">
|
||||
<AIcon
|
||||
type="ExclamationCircleOutlined"
|
||||
style="
|
||||
color: var(--ant-error-color);
|
||||
"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
</j-descriptions-item>
|
||||
<j-descriptions-item label="平台状态">
|
||||
{{ detail?.cardStateType?.text }}
|
||||
</j-descriptions-item>
|
||||
<j-descriptions-item label="说明">{{
|
||||
detail?.describe
|
||||
}}</j-descriptions-item>
|
||||
</j-descriptions>
|
||||
</j-card>
|
||||
</j-col>
|
||||
<j-col :span="24">
|
||||
<!-- 流量统计 -->
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="16">
|
||||
<div class="card">
|
||||
<Guide title="流量统计">
|
||||
<template #extra>
|
||||
<TimeSelect
|
||||
:type="'week'"
|
||||
:quickBtnList="quickBtnList"
|
||||
@change="getEcharts"
|
||||
/>
|
||||
</template>
|
||||
</Guide>
|
||||
<LineChart
|
||||
:showX="true"
|
||||
:showY="true"
|
||||
style="min-height: 490px"
|
||||
:chartData="flowData"
|
||||
/>
|
||||
</div>
|
||||
</j-col>
|
||||
<j-col :span="8">
|
||||
<div class="card">
|
||||
<Guide title="数据统计" />
|
||||
<div
|
||||
class="static-info"
|
||||
style="min-height: 490px"
|
||||
>
|
||||
<div class="data-statistics-item">
|
||||
<div
|
||||
class="flow-info"
|
||||
style="width: 100%"
|
||||
>
|
||||
<div class="label">
|
||||
昨日流量消耗
|
||||
</div>
|
||||
<j-tooltip placement="bottomLeft">
|
||||
<template #title>
|
||||
<span
|
||||
>{{ dayTotal }} M</span
|
||||
>
|
||||
</template>
|
||||
<div class="value">
|
||||
{{ dayTotal }}
|
||||
<span class="unit">M</span>
|
||||
</div>
|
||||
</j-tooltip>
|
||||
</div>
|
||||
<LineChart
|
||||
color="#FBA500"
|
||||
:chartData="dayOptions"
|
||||
/>
|
||||
</div>
|
||||
<div class="data-statistics-item">
|
||||
<div
|
||||
class="flow-info"
|
||||
style="width: 100%"
|
||||
>
|
||||
<div class="label">
|
||||
当月流量消耗
|
||||
</div>
|
||||
<j-tooltip placement="bottomLeft">
|
||||
<template #title>
|
||||
<span
|
||||
>{{
|
||||
monthTotal
|
||||
}}
|
||||
M</span
|
||||
>
|
||||
</template>
|
||||
<div class="value">
|
||||
{{ monthTotal }}
|
||||
<span class="unit">M</span>
|
||||
</div>
|
||||
</j-tooltip>
|
||||
</div>
|
||||
<LineChart :chartData="monthOptions" />
|
||||
</div>
|
||||
<div class="data-statistics-item">
|
||||
<div
|
||||
class="flow-info"
|
||||
style="width: 100%"
|
||||
>
|
||||
<div class="label">
|
||||
本年流量消耗
|
||||
</div>
|
||||
<j-tooltip placement="bottomLeft">
|
||||
<template #title>
|
||||
<span
|
||||
>{{ yearTotal }} M</span
|
||||
>
|
||||
</template>
|
||||
<div class="value">
|
||||
{{ yearTotal }}
|
||||
<span class="unit">M</span>
|
||||
</div>
|
||||
</j-tooltip>
|
||||
</div>
|
||||
<LineChart
|
||||
color="#58E1D3"
|
||||
:chartData="yearOptions"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</div>
|
||||
<JEmpty v-else></JEmpty>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="CardDetail">
|
||||
import moment from 'moment';
|
||||
import type { CardManagement } from '../typing';
|
||||
import {queryDeactivate, queryDetail} from '@/api/iot-card/cardManagement';
|
||||
import {
|
||||
queryDeactivate,
|
||||
queryDetail,
|
||||
query,
|
||||
} from '@/api/iot-card/cardManagement';
|
||||
import Save from '../Save.vue';
|
||||
import Guide from '@/views/iot-card/components/Guide.vue';
|
||||
import LineChart from '@/views/iot-card/components/LineChart.vue';
|
||||
import { queryFlow } from '@/api/iot-card/home';
|
||||
import TimeSelect from '@/views/iot-card/components/TimeSelect.vue';
|
||||
import { OperatorList, platformTypeList } from '@/views/iot-card/data';
|
||||
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: 'card',
|
||||
},
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
const cardId = ref();
|
||||
|
||||
const visible = ref<boolean>(false);
|
||||
const current = ref<Partial<CardManagement>>({});
|
||||
|
@ -213,9 +497,9 @@ const monthOptions = ref<any[]>([]);
|
|||
const yearOptions = ref<any[]>([]);
|
||||
|
||||
const deactivateData = reactive({
|
||||
show: false,
|
||||
tip: ''
|
||||
})
|
||||
show: false,
|
||||
tip: '',
|
||||
});
|
||||
|
||||
const quickBtnList = [
|
||||
{ label: '昨日', value: 'yesterday' },
|
||||
|
@ -225,18 +509,18 @@ const quickBtnList = [
|
|||
];
|
||||
|
||||
const getDetail = () => {
|
||||
queryDetail(route.params.id).then((resp: any) => {
|
||||
queryDetail(cardId.value).then((resp: any) => {
|
||||
if (resp.success) {
|
||||
detail.value = resp.result;
|
||||
|
||||
if (resp.result.cardStateType?.value === 'deactivate') {
|
||||
deactivateData.show = true
|
||||
// 获取停机原因
|
||||
queryDeactivate(route.params.id as string).then((deacResp: any) => {
|
||||
if (deacResp.success && deacResp.result?.message) {
|
||||
deactivateData.tip = deacResp.result.message.toString()
|
||||
}
|
||||
})
|
||||
if (resp.result.cardStateType?.value === 'deactivate' && detail.value.operatorName === 'onelink') {
|
||||
deactivateData.show = true;
|
||||
// 获取停机原因
|
||||
queryDeactivate(cardId.value).then((deacResp: any) => {
|
||||
if (deacResp.success && deacResp.result?.message) {
|
||||
deactivateData.tip = deacResp.result.message.toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -254,18 +538,17 @@ const saveChange = (val: any) => {
|
|||
}
|
||||
};
|
||||
|
||||
const getData = (
|
||||
start: number,
|
||||
end: number,
|
||||
): Promise<{ sortArray: any[]}> => {
|
||||
const getData = (start: number, end: number): Promise<{ sortArray: any[] }> => {
|
||||
return new Promise((resolve) => {
|
||||
queryFlow(start, end, {
|
||||
orderBy: 'date',
|
||||
terms: [{
|
||||
column : "cardId",
|
||||
termType: "eq",
|
||||
value: route.params.id
|
||||
}]
|
||||
terms: [
|
||||
{
|
||||
column: 'cardId',
|
||||
termType: 'eq',
|
||||
value: cardId.value,
|
||||
},
|
||||
],
|
||||
}).then((resp: any) => {
|
||||
if (resp.status === 200) {
|
||||
const sortArray = resp.result.sort(
|
||||
|
@ -278,7 +561,6 @@ const getData = (
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -308,16 +590,15 @@ const getDataTotal = () => {
|
|||
.reduce((r, n) => r + Number(n.value), 0)
|
||||
.toFixed(2);
|
||||
monthOptions.value = resp.sortArray;
|
||||
})
|
||||
});
|
||||
getData(yTime[0], yTime[1]).then((resp) => {
|
||||
yearTotal.value = resp.sortArray
|
||||
.reduce((r, n) => r + Number(n.value), 0)
|
||||
.toFixed(2);
|
||||
yearOptions.value = resp.sortArray;
|
||||
yearOptions.value = resp.sortArray;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 流量统计
|
||||
* @param data
|
||||
|
@ -334,8 +615,35 @@ const getEcharts = (data: any) => {
|
|||
});
|
||||
};
|
||||
|
||||
getDetail();
|
||||
getDataTotal();
|
||||
/**
|
||||
* 获取绑定设备的物联卡的信息
|
||||
*/
|
||||
const queryCard = async () => {
|
||||
const res: any = await query({
|
||||
terms: [
|
||||
{
|
||||
column: 'deviceId',
|
||||
termType: 'eq',
|
||||
value: route.params.id,
|
||||
},
|
||||
],
|
||||
});
|
||||
if (res.success && res.result?.data) {
|
||||
cardId.value = res.result?.data?.[0]?.id;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
if (props.type === 'device') {
|
||||
await queryCard();
|
||||
} else {
|
||||
cardId.value = route.params.id;
|
||||
}
|
||||
if (cardId.value) {
|
||||
getDetail();
|
||||
getDataTotal();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.card {
|
||||
|
|
|
@ -40,101 +40,6 @@
|
|||
:actions="batchActions"
|
||||
@change="onCheckChange"
|
||||
/>
|
||||
<!-- <j-dropdown>
|
||||
<j-button>
|
||||
批量操作
|
||||
<AIcon type="DownOutlined" />
|
||||
</j-button>
|
||||
<template #overlay>
|
||||
<j-menu>
|
||||
<j-menu-item>
|
||||
<PermissionButton
|
||||
@click="exportVisible = true"
|
||||
:hasPermission="'iot-card/CardManagement:export'"
|
||||
>
|
||||
<AIcon type="ExportOutlined" />
|
||||
批量导出
|
||||
</PermissionButton>
|
||||
</j-menu-item>
|
||||
<j-menu-item>
|
||||
<PermissionButton
|
||||
@click="importVisible = true"
|
||||
:hasPermission="'iot-card/CardManagement:import'"
|
||||
>
|
||||
<AIcon type="ImportOutlined" />批量导入
|
||||
</PermissionButton>
|
||||
</j-menu-item>
|
||||
<j-menu-item>
|
||||
<PermissionButton
|
||||
:popConfirm="{
|
||||
title: '确认激活吗?',
|
||||
onConfirm: handleActive,
|
||||
}"
|
||||
:hasPermission="'iot-card/CardManagement:active'"
|
||||
>
|
||||
<AIcon type="CheckCircleOutlined" />
|
||||
批量激活
|
||||
</PermissionButton>
|
||||
</j-menu-item>
|
||||
<j-menu-item>
|
||||
<PermissionButton
|
||||
:popConfirm="{
|
||||
title: '确认停用吗?',
|
||||
onConfirm: handleStop,
|
||||
}"
|
||||
ghost
|
||||
type="primary"
|
||||
:hasPermission="'iot-card/CardManagement:action'"
|
||||
>
|
||||
<AIcon type="StopOutlined" />
|
||||
批量停用
|
||||
</PermissionButton>
|
||||
</j-menu-item>
|
||||
<j-menu-item>
|
||||
<PermissionButton
|
||||
:popConfirm="{
|
||||
title: '确认复机吗?',
|
||||
onConfirm: handleResumption,
|
||||
}"
|
||||
ghost
|
||||
type="primary"
|
||||
:hasPermission="'iot-card/CardManagement:action'"
|
||||
>
|
||||
<AIcon type="PoweroffOutlined" />
|
||||
批量复机
|
||||
</PermissionButton>
|
||||
</j-menu-item>
|
||||
<j-menu-item>
|
||||
<PermissionButton
|
||||
:popConfirm="{
|
||||
title: '确认同步状态吗?',
|
||||
onConfirm: handleSync,
|
||||
}"
|
||||
ghost
|
||||
type="primary"
|
||||
:hasPermission="'iot-card/CardManagement:sync'"
|
||||
>
|
||||
<AIcon type="SwapOutlined" />
|
||||
同步状态
|
||||
</PermissionButton>
|
||||
</j-menu-item>
|
||||
<j-menu-item v-if="_selectedRowKeys.length > 0">
|
||||
<PermissionButton
|
||||
:popConfirm="{
|
||||
title: '确认删除吗?',
|
||||
onConfirm: handelRemove,
|
||||
}"
|
||||
ghost
|
||||
type="primary"
|
||||
:hasPermission="'iot-card/CardManagement:delete'"
|
||||
>
|
||||
<AIcon type="SwapOutlined" />
|
||||
批量删除
|
||||
</PermissionButton>
|
||||
</j-menu-item>
|
||||
</j-menu>
|
||||
</template>
|
||||
</j-dropdown> -->
|
||||
</j-space>
|
||||
</template>
|
||||
<template #card="slotProps">
|
||||
|
@ -166,7 +71,7 @@
|
|||
</Ellipsis>
|
||||
</span>
|
||||
<j-row style="margin-top: 20px">
|
||||
<j-col :span="8">
|
||||
<j-col :span="6">
|
||||
<div class="card-item-content-text">
|
||||
平台对接
|
||||
</div>
|
||||
|
@ -176,6 +81,20 @@
|
|||
</div>
|
||||
</Ellipsis>
|
||||
</j-col>
|
||||
<j-col :span="6">
|
||||
<div class="card-item-content-text">
|
||||
运营商状态
|
||||
</div>
|
||||
<BadgeStatus
|
||||
:status="slotProps.cardState?.value"
|
||||
:text="slotProps.cardState?.text"
|
||||
:statusNames="{
|
||||
using: 'processing',
|
||||
toBeActivated: 'default',
|
||||
deactivate: 'error',
|
||||
}"
|
||||
/>
|
||||
</j-col>
|
||||
<j-col :span="6">
|
||||
<div class="card-item-content-text">
|
||||
类型
|
||||
|
@ -296,6 +215,17 @@
|
|||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #cardState="slotProps">
|
||||
<BadgeStatus
|
||||
:status="slotProps.cardState?.value"
|
||||
:text="slotProps.cardState?.text"
|
||||
:statusNames="{
|
||||
using: 'processing',
|
||||
toBeActivated: 'default',
|
||||
deactivate: 'error',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
<template #activationDate="slotProps">
|
||||
{{
|
||||
slotProps.activationDate
|
||||
|
@ -371,11 +301,7 @@
|
|||
@change="saveChange"
|
||||
/>
|
||||
<!-- 批量同步 -->
|
||||
<SyncModal
|
||||
v-if="syncVisible"
|
||||
:params="params"
|
||||
@close="syncClose"
|
||||
/>
|
||||
<SyncModal v-if="syncVisible" :params="params" @close="syncClose" />
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
|
@ -409,7 +335,8 @@ import { BatchActionsType } from '@/components/BatchDropdown/types';
|
|||
import { usePermissionStore } from 'store/permission';
|
||||
import { useRouterParams } from '@/utils/hooks/useParams';
|
||||
import { OperatorMap } from '@/views/iot-card/data';
|
||||
import SyncModal from './Sync.vue'
|
||||
import SyncModal from './Sync.vue';
|
||||
import { OperatorList } from '../data';
|
||||
|
||||
const router = useRouter();
|
||||
const menuStory = useMenuStore();
|
||||
|
@ -425,7 +352,7 @@ const cardId = ref<any>();
|
|||
const current = ref<Partial<CardManagement>>({});
|
||||
const saveType = ref<string>('');
|
||||
const isCheck = ref<boolean>(false);
|
||||
const syncVisible = ref(false)
|
||||
const syncVisible = ref(false);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
|
@ -447,7 +374,6 @@ const columns = [
|
|||
width: 200,
|
||||
search: {
|
||||
type: 'string',
|
||||
termOptions: ['eq'],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -494,20 +420,7 @@ const columns = [
|
|||
search: {
|
||||
type: 'select',
|
||||
options: async () => {
|
||||
return [
|
||||
{
|
||||
label: '移动',
|
||||
value: '移动',
|
||||
},
|
||||
{
|
||||
label: '电信',
|
||||
value: '电信',
|
||||
},
|
||||
{
|
||||
label: '联通',
|
||||
value: '联通',
|
||||
},
|
||||
];
|
||||
return OperatorList;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -567,7 +480,7 @@ const columns = [
|
|||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
title: '平台状态',
|
||||
dataIndex: 'cardStateType',
|
||||
key: 'cardStateType',
|
||||
width: 180,
|
||||
|
@ -584,6 +497,45 @@ const columns = [
|
|||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '运营商状态',
|
||||
dataIndex: 'cardState',
|
||||
key: 'cardState',
|
||||
width: 180,
|
||||
scopedSlots: true,
|
||||
},
|
||||
{
|
||||
title: '运营商状态',
|
||||
dataIndex: 'operatorState',
|
||||
key: 'operatorState',
|
||||
hidden: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: '激活(正常)', value: 'using' },
|
||||
{ label: '测试激活', value: 'testActivation' },
|
||||
{ label: '拆机', value: 'disassemble' },
|
||||
{ label: '停用(已停用)', value: 'deactivate' },
|
||||
{ label: '运营商管理状态', value: 'operatorManagement' },
|
||||
{ label: '可激活(电信)', value: 'beActivated' },
|
||||
{ label: '待激活', value: 'toBeActivated' },
|
||||
{ label: '测试去激活', value: 'testToActivation' },
|
||||
{ label: '可测试', value: 'testable' },
|
||||
{ label: '库存(移动)', value: 'inStock' },
|
||||
{ label: '预销户', value: 'preSeller' },
|
||||
{ label: '单向停机', value: 'oneWayShutdown' },
|
||||
{ label: '预销号', value: 'preSale' },
|
||||
{ label: '过户', value: 'transfer' },
|
||||
{ label: '休眠', value: 'dormant' },
|
||||
{ label: '可激活(联通)', value: 'activatable' },
|
||||
{ label: '已失效', value: 'expired' },
|
||||
{ label: '已清除', value: 'cleared' },
|
||||
{ label: '已更换', value: 'replaced' },
|
||||
{ label: '库存(联通)', value: 'stock' },
|
||||
{ label: '开始', value: 'start' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
|
@ -632,13 +584,15 @@ const getActions = (
|
|||
title: '确认解绑设备?',
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
onConfirm: async () => {
|
||||
unbind(data.id).then((resp: any) => {
|
||||
onConfirm: () => {
|
||||
const response = unbind(data.id);
|
||||
response.then((resp: any) => {
|
||||
if (resp.status === 200) {
|
||||
onlyMessage('操作成功');
|
||||
cardManageRef.value?.reload();
|
||||
}
|
||||
});
|
||||
return response;
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
|
@ -683,29 +637,34 @@ const getActions = (
|
|||
: '确认停用?',
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
onConfirm: async () => {
|
||||
onConfirm: () => {
|
||||
let response;
|
||||
if (data.cardStateType?.value === 'toBeActivated') {
|
||||
changeDeploy(data.id).then((resp) => {
|
||||
response = changeDeploy(data.id);
|
||||
response.then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
onlyMessage('操作成功');
|
||||
cardManageRef.value?.reload();
|
||||
}
|
||||
});
|
||||
} else if (data.cardStateType?.value === 'deactivate') {
|
||||
resumption(data.id).then((resp) => {
|
||||
response = resumption(data.id);
|
||||
response.then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
onlyMessage('操作成功');
|
||||
cardManageRef.value?.reload();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
unDeploy(data.id).then((resp) => {
|
||||
response = unDeploy(data.id);
|
||||
response.then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
onlyMessage('操作成功');
|
||||
cardManageRef.value?.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
return response;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -719,20 +678,23 @@ const getActions = (
|
|||
title: '确认删除?',
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
onConfirm: async () => {
|
||||
const resp: any = await del(data.id);
|
||||
if (resp.status === 200) {
|
||||
onlyMessage('操作成功');
|
||||
const index = _selectedRowKeys.value.findIndex(
|
||||
(id: any) => id === data.id,
|
||||
);
|
||||
if (index !== -1) {
|
||||
_selectedRowKeys.value.splice(index, 1);
|
||||
onConfirm: () => {
|
||||
const response: any = del(data.id);
|
||||
response.then((resp: any) => {
|
||||
if (resp.status === 200) {
|
||||
onlyMessage('操作成功');
|
||||
const index = _selectedRowKeys.value.findIndex(
|
||||
(id: any) => id === data.id,
|
||||
);
|
||||
if (index !== -1) {
|
||||
_selectedRowKeys.value.splice(index, 1);
|
||||
}
|
||||
cardManageRef.value?.reload();
|
||||
} else {
|
||||
onlyMessage('操作失败!', 'error');
|
||||
}
|
||||
cardManageRef.value?.reload();
|
||||
} else {
|
||||
onlyMessage('操作失败!', 'error');
|
||||
}
|
||||
});
|
||||
return response;
|
||||
},
|
||||
},
|
||||
icon: 'DeleteOutlined',
|
||||
|
@ -860,11 +822,13 @@ const handleStop = () => {
|
|||
_selectedRowKeys.value.length >= 10 &&
|
||||
_selectedRowKeys.value.length <= 100
|
||||
) {
|
||||
unDeployBatch(_selectedRowKeys.value).then((res: any) => {
|
||||
const response = unDeployBatch(_selectedRowKeys.value);
|
||||
response.then((res: any) => {
|
||||
if (res.status === 200) {
|
||||
onlyMessage('操作成功');
|
||||
}
|
||||
});
|
||||
return response;
|
||||
} else {
|
||||
onlyMessage(
|
||||
'仅支持同一个运营商下且最少10条数据,最多100条数据',
|
||||
|
@ -881,11 +845,13 @@ const handleResumption = () => {
|
|||
_selectedRowKeys.value.length >= 10 &&
|
||||
_selectedRowKeys.value.length <= 100
|
||||
) {
|
||||
resumptionBatch(_selectedRowKeys.value).then((res: any) => {
|
||||
const response = resumptionBatch(_selectedRowKeys.value);
|
||||
response.then((res: any) => {
|
||||
if (res.status === 200) {
|
||||
onlyMessage('操作成功');
|
||||
}
|
||||
});
|
||||
return response;
|
||||
} else {
|
||||
onlyMessage(
|
||||
'仅支持同一个运营商下且最少10条数据,最多100条数据',
|
||||
|
@ -897,8 +863,8 @@ const handleResumption = () => {
|
|||
/**
|
||||
* 同步状态
|
||||
*/
|
||||
const handleSync = async() => {
|
||||
syncVisible.value = true
|
||||
const handleSync = async () => {
|
||||
syncVisible.value = true;
|
||||
// if (!_selectedRowKeys.value.length) {
|
||||
// onlyMessage('请选择数据', 'error');
|
||||
// return;
|
||||
|
@ -925,20 +891,23 @@ const handleResumption = () => {
|
|||
/**
|
||||
* 批量删除
|
||||
*/
|
||||
const handelRemove = async () => {
|
||||
const handelRemove = () => {
|
||||
if (!_selectedRowKeys.value.length) {
|
||||
onlyMessage('请选择数据', 'error');
|
||||
return;
|
||||
}
|
||||
const resp = await removeCards(
|
||||
const response = removeCards(
|
||||
_selectedRowKeys.value.map((v) => ({ id: v })),
|
||||
);
|
||||
if (resp.status === 200) {
|
||||
onlyMessage('操作成功');
|
||||
_selectedRowKeys.value = [];
|
||||
// _selectedRow.value = [];
|
||||
cardManageRef.value?.reload();
|
||||
}
|
||||
response.then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
onlyMessage('操作成功');
|
||||
_selectedRowKeys.value = [];
|
||||
// _selectedRow.value = [];
|
||||
cardManageRef.value?.reload();
|
||||
}
|
||||
});
|
||||
return response;
|
||||
};
|
||||
const batchActions: BatchActionsType[] = [
|
||||
{
|
||||
|
@ -959,18 +928,6 @@ const batchActions: BatchActionsType[] = [
|
|||
importVisible.value = true;
|
||||
},
|
||||
},
|
||||
// {
|
||||
// key: 'active',
|
||||
// text: '批量激活',
|
||||
// permission: 'iot-card/CardManagement:active',
|
||||
// icon: 'CheckCircleOutlined',
|
||||
// selected: {
|
||||
// popConfirm: {
|
||||
// title: '确认激活吗?',
|
||||
// onConfirm: handleActive,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
{
|
||||
key: 'stop',
|
||||
text: '批量停用',
|
||||
|
@ -1004,7 +961,7 @@ const batchActions: BatchActionsType[] = [
|
|||
type: 'primary',
|
||||
permission: 'iot-card/CardManagement:sync',
|
||||
icon: 'SwapOutlined',
|
||||
onClick: handleSync
|
||||
onClick: handleSync,
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
|
@ -1022,9 +979,9 @@ const batchActions: BatchActionsType[] = [
|
|||
];
|
||||
|
||||
const syncClose = () => {
|
||||
syncVisible.value = false
|
||||
cardManageRef.value?.reload();
|
||||
}
|
||||
syncVisible.value = false;
|
||||
cardManageRef.value?.reload();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (routerParams.params.value.type === 'add' && paltformPermission) {
|
||||
|
|
|
@ -142,7 +142,7 @@
|
|||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入接入密码',
|
||||
message: '请输入接入地址',
|
||||
},
|
||||
{
|
||||
max: 64,
|
||||
|
@ -152,6 +152,7 @@
|
|||
>
|
||||
<j-input
|
||||
v-model:value="formData.others.onvifUrl"
|
||||
placeholder="请输入接入地址"
|
||||
></j-input>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
|
@ -160,7 +161,7 @@
|
|||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入接入密码',
|
||||
message: '请输入接入账户',
|
||||
},
|
||||
{
|
||||
max: 64,
|
||||
|
@ -172,6 +173,7 @@
|
|||
v-model:value="
|
||||
formData.others.onvifUsername
|
||||
"
|
||||
placeholder="请输入接入账户"
|
||||
></j-input>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
|
@ -192,9 +194,71 @@
|
|||
v-model:value="
|
||||
formData.others.onvifPassword
|
||||
"
|
||||
placeholder="请输入接入密码"
|
||||
></j-input-password>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-if="formData.channel === 'media-plugin'">
|
||||
<j-form-item
|
||||
:name="['others', item.property]"
|
||||
v-for="item in metadata?.properties || []"
|
||||
:key="item"
|
||||
:label="item.name"
|
||||
:rules="[
|
||||
{
|
||||
required:
|
||||
!!item?.type?.expands?.required,
|
||||
message: `${
|
||||
item.type.type === 'enum' ||
|
||||
'boolean'
|
||||
? '请选择'
|
||||
: '请输入'
|
||||
}${item.name}`,
|
||||
},
|
||||
]"
|
||||
>
|
||||
<j-input
|
||||
placeholder="请输入"
|
||||
v-if="item.type.type === 'string'"
|
||||
v-model:value="
|
||||
formData.others[item.property]
|
||||
"
|
||||
></j-input>
|
||||
<j-input-password
|
||||
placeholder="请输入"
|
||||
v-if="item.type.type === 'password'"
|
||||
v-model:value="
|
||||
formData.others[item.property]
|
||||
"
|
||||
></j-input-password>
|
||||
<j-select
|
||||
placeholder="请选择"
|
||||
v-if="
|
||||
item.type.type === 'enum' ||
|
||||
item.type.type === 'boolean'
|
||||
"
|
||||
v-model:value="
|
||||
formData.others[item.property]
|
||||
"
|
||||
:options="getOptions(item)"
|
||||
>
|
||||
</j-select>
|
||||
<j-input-number
|
||||
v-if="
|
||||
[
|
||||
'int',
|
||||
'float',
|
||||
'double',
|
||||
'long',
|
||||
].includes(item.type.type)
|
||||
"
|
||||
v-model:value="
|
||||
formData.others[item.property]
|
||||
"
|
||||
placeholder="请输入"
|
||||
></j-input-number>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-if="!!route.query.id">
|
||||
<j-form-item
|
||||
v-if="formData.channel === 'gb28181-2016'"
|
||||
|
@ -266,7 +330,6 @@
|
|||
/>
|
||||
</j-form-item>
|
||||
</template>
|
||||
|
||||
<j-form-item label="说明">
|
||||
<j-textarea
|
||||
v-model:value="formData.description"
|
||||
|
@ -288,7 +351,11 @@
|
|||
</j-form>
|
||||
</j-col>
|
||||
<j-col :span="12">
|
||||
<div v-if="1" class="doc" style="height: 800">
|
||||
<div
|
||||
v-if="formData.channel === 'gb28181-2016'"
|
||||
class="doc"
|
||||
style="height: 800"
|
||||
>
|
||||
<h1>1.概述</h1>
|
||||
<div>
|
||||
视频设备通过GB/T28181接入平台整体分为2部分,包括平台端配置和设备端配置,不同的设备端配置的路径或页面存在差异,但配置项基本大同小异。
|
||||
|
@ -354,7 +421,7 @@
|
|||
<div>不影响设备接入平台,可保持设备初始化值。</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="doc" style="height: 600">
|
||||
<div v-else-if="formData.channel === 'fixed-media'" class="doc" style="height: 600">
|
||||
<h1>1.概述</h1>
|
||||
<div>
|
||||
视频设备通过RTSP、RTMP固定地址接入平台分为2步。
|
||||
|
@ -374,6 +441,55 @@
|
|||
只能选择接入方式为固定地址的产品,若当前无对应产品,可点击右侧快速添加按钮,填写产品名称和选择固定地址类型的网关完成产品创建。
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="formData.channel === 'onvif'" class="doc" style="height: 600">
|
||||
<h1>1.概述</h1>
|
||||
<div>
|
||||
JetLinks平台支持通过Onvif方式接入视频设备。分为两个部分,包括平台端配置和设备端配置。本文通过海康摄像头为例将onvif视频接入到平台播放。
|
||||
</div>
|
||||
<h1>2.配置说明</h1>
|
||||
<div>
|
||||
设备端配置
|
||||
</div>
|
||||
<div>1.本文以海康监控为例演示,登录海康监控设备后台,进入配置>网络>高级配置>集成协议,用户自定义输入用户名和密码,完成用户添加。</div>
|
||||
<div class="image">
|
||||
<j-image
|
||||
width="100%"
|
||||
:src="getImage('/media/doc5.png')"
|
||||
/>
|
||||
</div>
|
||||
<div>平台端配置</div>
|
||||
<div>ID:设备唯一标识,若不填写,系统将自动生成唯一标识</div>
|
||||
<div>设备名称:用户自定义输入小于或等于64位字符</div>
|
||||
<div>所属产品:选择接入方式为Onvif的产品,若当前无对应产品,可点击右侧快速添加按钮,填写产品名称和选择Onvif类型的网关完成产品创建</div>
|
||||
<div>接入地址:不同平台的摄像头接入地址组合方式不一致,请参考对应品牌接入Onvif的地址设置。如海康:http://ip/onvif/device_service。IP地址来自于海康监控设备端后台:配置>网络>基本配置>TCP/IP</div>
|
||||
<div class="image">
|
||||
<j-image
|
||||
width="100%"
|
||||
:src="getImage('/media/doc6.png')"
|
||||
/>
|
||||
</div>
|
||||
<div>接入账户:输入设备端配置时添加的用户名</div>
|
||||
<div>接入密码:输入设备端配置时添加的密码</div>
|
||||
<div class="image">
|
||||
<j-image
|
||||
width="100%"
|
||||
:src="getImage('/media/doc7.png')"
|
||||
/>
|
||||
</div>
|
||||
<h1>3.所有配置项填写完成,点击保存。</h1>
|
||||
</div>
|
||||
<div v-else-if="formData.channel === 'media-plugin'" class="doc" style="height: 600">
|
||||
<h1>1.概述</h1>
|
||||
<div>
|
||||
JetLinks平台支持通过调用SDK或API请求将第三方系统视频设备数据接入到平台。
|
||||
</div>
|
||||
<h1>2.配置说明</h1>
|
||||
<div>2.1平台端配置</div>
|
||||
<div>ID:设备唯一标识,若不填写,系统将自动生成唯一标识</div>
|
||||
<div>设备名称:用户自定义输入小于或等于64位字符</div>
|
||||
<div>所属产品:选择接入方式为插件视频接入的产品,若当前无对应产品,可点击右侧快速添加按钮,填写产品名称和选择插件类型的网关完成产品创建。</div>
|
||||
<h1>3.所有配置项填写完成,点击保存。</h1>
|
||||
</div>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</j-card>
|
||||
|
@ -396,12 +512,12 @@ import type { ProductType } from '@/views/media/Device/typings';
|
|||
import SaveProduct from './SaveProduct.vue';
|
||||
import { notification } from 'jetlinks-ui-components';
|
||||
import { omit } from 'lodash-es';
|
||||
import { queryDeviceConfig } from '@/api/device/instance';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
// 表单数据
|
||||
const formData = ref({
|
||||
const formData = ref<any>({
|
||||
id: '',
|
||||
name: '',
|
||||
channel: 'gb28181-2016',
|
||||
|
@ -421,6 +537,9 @@ const formData = ref({
|
|||
firmware: '',
|
||||
});
|
||||
|
||||
const metadata = ref<any>({
|
||||
properties: [],
|
||||
});
|
||||
const handleChannelChange = () => {
|
||||
formData.value.productId = undefined;
|
||||
getProductList();
|
||||
|
@ -441,16 +560,46 @@ const getProductList = async () => {
|
|||
};
|
||||
const { result } = await DeviceApi.queryProductList(params);
|
||||
productList.value = result;
|
||||
if(result.length && !route.query.id){
|
||||
formData.value.productId = result[0]?.id
|
||||
formData.value.others.access_pwd = result[0]?.configuration?.access_pwd
|
||||
formData.value.streamMode = result[0]?.configuration?.stream_mode
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const handleProductChange = () => {
|
||||
formData.value.others.access_pwd =
|
||||
productList.value.find((f: any) => f.id === formData.value.productId)
|
||||
?.configuration.access_pwd || '';
|
||||
formData.value.streamMode = productList.value.find((f: any) => f.id === formData.value.productId)
|
||||
formData.value.streamMode =
|
||||
productList.value.find((f: any) => f.id === formData.value.productId)
|
||||
?.configuration.stream_mode || '';
|
||||
};
|
||||
|
||||
//获取物模型下拉选项
|
||||
const getOptions = (i: any) => {
|
||||
if (i.type.type === 'enum') {
|
||||
return (i.type?.elements || []).map((item) => {
|
||||
return {
|
||||
label: item?.text,
|
||||
value: item?.value,
|
||||
};
|
||||
});
|
||||
} else if (i.type.type === 'boolean') {
|
||||
return [
|
||||
{
|
||||
label: i.type?.falseText,
|
||||
value: i.type?.falseValue,
|
||||
},
|
||||
{
|
||||
label: i.type?.trueText,
|
||||
value: i.type?.trueValue,
|
||||
},
|
||||
];
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
/**
|
||||
* 新增产品
|
||||
*/
|
||||
|
@ -460,15 +609,15 @@ const saveProductVis = ref(false);
|
|||
* 获取详情
|
||||
*/
|
||||
const getDetail = async () => {
|
||||
if (!route.query.id) return;
|
||||
const res = await DeviceApi.detail(route.query.id as string);
|
||||
Object.assign(formData.value, res.result);
|
||||
formData.value.channel = res.result.provider;
|
||||
await getProductList();
|
||||
if (formData.value.productId) {
|
||||
const productData = productList.value.find((i: any) => {
|
||||
return i.id === formData.value.productId;
|
||||
});
|
||||
if (productData) {
|
||||
if (productData && formData.value.channel !== 'media-plugin') {
|
||||
formData.value.others.access_pwd = formData.value.others.access_pwd
|
||||
? formData.value.others.access_pwd
|
||||
: productData?.configuration?.access_pwd;
|
||||
|
@ -476,12 +625,26 @@ const getDetail = async () => {
|
|||
? formData.value.streamMode
|
||||
: productData?.configuration?.stream_mode;
|
||||
}
|
||||
if (productData && formData.value.channel === 'media-plugin') {
|
||||
if(!res.result.others || JSON.stringify(res.result?.others) === "{}"){
|
||||
formData.value.others = productData?.configuration;
|
||||
}
|
||||
const resp: any = await queryDeviceConfig(formData.value.id);
|
||||
if (resp.success) {
|
||||
metadata.value = resp?.result[0] || {
|
||||
properties: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDetail();
|
||||
getProductList();
|
||||
onMounted(async () => {
|
||||
if (!route.query.id) {
|
||||
getProductList();
|
||||
} else {
|
||||
getDetail();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -508,7 +671,7 @@ const handleSubmit = () => {
|
|||
} else if (formData.value.channel === 'gb28181-2016') {
|
||||
// 国标
|
||||
others = omit(others, ['onvifUrl', 'onvifPassword', 'onvifUsername']);
|
||||
const getParmas = () => {
|
||||
const getParams = () => {
|
||||
if (others?.stream_mode) {
|
||||
others.stream_mode = streamMode;
|
||||
}
|
||||
|
@ -522,8 +685,8 @@ const handleSubmit = () => {
|
|||
...extraParams,
|
||||
};
|
||||
};
|
||||
params = !id ? { others, id, ...extraParams } : getParmas();
|
||||
} else {
|
||||
params = !id ? { others, id, ...extraParams } : getParams();
|
||||
} else if (formData.value.channel === 'onvif') {
|
||||
others = omit(others, ['access_pwd']);
|
||||
params = !id
|
||||
? { others, ...extraParams }
|
||||
|
@ -536,6 +699,18 @@ const handleSubmit = () => {
|
|||
others,
|
||||
...extraParams,
|
||||
};
|
||||
} else if (formData.value.channel === 'media-plugin') {
|
||||
params = !id
|
||||
? extraParams
|
||||
: {
|
||||
id,
|
||||
streamMode,
|
||||
manufacturer,
|
||||
model,
|
||||
firmware,
|
||||
others,
|
||||
...extraParams,
|
||||
};
|
||||
}
|
||||
|
||||
formRef.value
|
||||
|
|
|
@ -152,7 +152,7 @@ const handleTypeChange = (record: IVariable) => {
|
|||
|
||||
<style lang="less" scoped>
|
||||
.table-wrapper {
|
||||
background-color: #1d39c4;
|
||||
background-color: @primary-color;
|
||||
.has-error {
|
||||
border-color: rgba(255, 77, 79);
|
||||
&:focus {
|
||||
|
|
|
@ -4,142 +4,74 @@
|
|||
title="新增"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
:maskClosable="false"
|
||||
:width="1000"
|
||||
:loading="loading"
|
||||
@cancel="closeModal"
|
||||
@ok="saveCorrelation"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<!-- <div v-if="type !== 'other'" style="padding: 0 24px">
|
||||
<j-steps :current="current" >
|
||||
<j-step title="选择场景"></j-step>
|
||||
<j-step title="选择条件"></j-step>
|
||||
</j-steps>
|
||||
</div> -->
|
||||
<!-- <template v-if="current === 0 || type === 'other' "> -->
|
||||
<pro-search :columns="columns" type="simple" @search="handleSearch"/>
|
||||
<div style="height: 500px; overflow-y: auto">
|
||||
<JProTable
|
||||
model="CARD"
|
||||
:request="query"
|
||||
:rowSelection="{
|
||||
selectedRowKeys: _selectedRowKeys,
|
||||
onChange: onSelectChange
|
||||
}"
|
||||
:gridColumns="[1, 1, 1]"
|
||||
:defaultParams="{
|
||||
<pro-search :columns="columns" type="simple" @search="handleSearch"/>
|
||||
<div style="height: 500px; overflow-y: auto">
|
||||
<JProTable
|
||||
model="CARD"
|
||||
ref="tableRef"
|
||||
:request="query"
|
||||
:gridColumns="[1, 1, 1]"
|
||||
:defaultParams="{
|
||||
sorts: [
|
||||
{
|
||||
name: 'createTime',
|
||||
order: 'desc',
|
||||
},
|
||||
],
|
||||
terms,
|
||||
terms: [{
|
||||
column: 'triggerType',
|
||||
value: props.type === 'other' ? null : 'device'
|
||||
},
|
||||
{
|
||||
terms: [
|
||||
{
|
||||
column: 'features',
|
||||
termType: 'in',
|
||||
value: [ 'alarmTrigger', 'alarmReliever']
|
||||
},
|
||||
{
|
||||
column: 'features',
|
||||
termType: 'isnull',
|
||||
value: 1,
|
||||
type: 'or'
|
||||
}
|
||||
],
|
||||
type: 'and'
|
||||
}
|
||||
]
|
||||
}"
|
||||
:params="params"
|
||||
>
|
||||
<template #card="slotProps">
|
||||
<CardBox
|
||||
:value="slotProps"
|
||||
:status="slotProps.state?.value"
|
||||
:statusText="slotProps.state?.text"
|
||||
:active="_selectedRowKeys.includes(slotProps.id)"
|
||||
@click="handleClick"
|
||||
:statusNames="{
|
||||
started: 'processing',
|
||||
disable: 'error',
|
||||
}"
|
||||
>
|
||||
<template #type>
|
||||
<span
|
||||
><img
|
||||
:height="16"
|
||||
:src="
|
||||
typeMap.get(slotProps.triggerType)?.icon
|
||||
"
|
||||
style="margin-right: 5px"
|
||||
/>{{
|
||||
typeMap.get(slotProps.triggerType)?.text
|
||||
}}</span
|
||||
>
|
||||
</template>
|
||||
<template #img>
|
||||
<img
|
||||
:src="typeMap.get(slotProps.triggerType)?.img"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<Ellipsis style="width: calc(100% - 100px)">
|
||||
<span style="font-size: 16px; font-weight: 600">
|
||||
{{ slotProps.name }}
|
||||
</span>
|
||||
</Ellipsis>
|
||||
<Ellipsis :lineClamp="2">
|
||||
<div class="subTitle">
|
||||
说明:{{
|
||||
slotProps?.description ||
|
||||
typeMap.get(slotProps.triggerType)?.tip
|
||||
}}
|
||||
</div>
|
||||
</Ellipsis>
|
||||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
</JProTable>
|
||||
</div>
|
||||
<!-- </template> -->
|
||||
<!-- <template v-if="current === 1">
|
||||
<div class="branch-terms-items">
|
||||
<j-tree
|
||||
v-if="branchGroup.length"
|
||||
defaultExpandAll
|
||||
checkable
|
||||
:treeData="branchGroup"
|
||||
@check="branchCheck"
|
||||
>
|
||||
|
||||
</j-tree> -->
|
||||
<!-- <CardBox-->
|
||||
<!-- v-for="(item, index) in branchGroup.branches"-->
|
||||
<!-- :showStatus="false"-->
|
||||
<!-- :active="branchActiveKey.includes(item.id)"-->
|
||||
<!-- @click="() => branchClick(item.id)"-->
|
||||
<!-- >-->
|
||||
<!-- <template #content>-->
|
||||
<!-- <div class="condition-name">-->
|
||||
<!-- 条件 {{ index + 1 }}-->
|
||||
<!-- </div>-->
|
||||
<!-- <div style="height: 80px">-->
|
||||
<!-- <j-scrollbar >-->
|
||||
<!-- <div v-for="(b, bIndex) in item">-->
|
||||
<!-- <div style="font-weight: bold">-->
|
||||
<!-- <span v-if="bIndex === 0">当</span>-->
|
||||
<!-- <span v-else>否则</span>-->
|
||||
<!-- <span>{{ b.condition }}</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div style="padding-left: 16px" v-for="action in b.actions">-->
|
||||
<!-- 执行 {{ action }}-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </j-scrollbar>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<!-- </CardBox>-->
|
||||
<!-- </div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<j-button v-if="current === 0" @click="closeModal">取消</j-button>
|
||||
<j-button v-if="current === 0" type="primary" @click="next">下一步</j-button>
|
||||
<j-button v-if="current === 1" @click="prev">上一步</j-button>
|
||||
<j-button v-if="current === 1" type="primary" @click="saveCorrelation">完成</j-button>
|
||||
</template> -->
|
||||
:params="params"
|
||||
>
|
||||
<template #card="slotProps">
|
||||
<SceneCardBox
|
||||
:value="slotProps"
|
||||
:status="slotProps.state?.value"
|
||||
:statusText="slotProps.state?.text"
|
||||
:alarmId="id"
|
||||
:activeKeys="activeKeys[slotProps.id]"
|
||||
:selectedKeys="selectedKeysMap[slotProps.id]"
|
||||
:showMask="true"
|
||||
@change="(key, selected) => onAlarmChange(key, selected, slotProps)"
|
||||
@reload="reload"
|
||||
/>
|
||||
</template>
|
||||
</JProTable>
|
||||
</div>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
<script lang="ts" setup name="SceneSave">
|
||||
import {query} from '@/api/rule-engine/scene';
|
||||
import {bindScene} from '@/api/rule-engine/configuration';
|
||||
import {getImage, onlyMessage} from '@/utils/comm';
|
||||
import {handleSceneBranches} from './utils'
|
||||
import {bindScene, queryBindScene} from '@/api/rule-engine/configuration';
|
||||
import { onlyMessage} from '@/utils/comm';
|
||||
import SceneCardBox from './CardBox.vue'
|
||||
import { useRequest } from '@/hook'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
|
@ -148,39 +80,6 @@ const columns = [
|
|||
key: 'name',
|
||||
search: {
|
||||
type: 'string',
|
||||
// rename: "id",
|
||||
// options: async () => {
|
||||
// const res = await query(
|
||||
// {
|
||||
// sorts: [
|
||||
// {
|
||||
// name: 'createTime',
|
||||
// order: 'desc',
|
||||
// },
|
||||
// ],
|
||||
// terms: [
|
||||
// {
|
||||
// column: 'id',
|
||||
// termType: 'alarm-bind-rule$not',
|
||||
// value: props.id,
|
||||
// type: 'and',
|
||||
// },
|
||||
// {
|
||||
// column: 'triggerType',
|
||||
// termType: 'eq',
|
||||
// value: props.type === 'other' ? undefined : 'device',
|
||||
// },
|
||||
// ]
|
||||
// }
|
||||
// );
|
||||
// if (res.status === 200) {
|
||||
// return res.result.data.map((item: any) => ({
|
||||
// label: item.name,
|
||||
// value: item.id,
|
||||
// }));
|
||||
// }
|
||||
// return []
|
||||
// }
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -232,30 +131,6 @@ const props = defineProps({
|
|||
type: String,
|
||||
},
|
||||
});
|
||||
// 告警改造后的逻辑,后端没有接口暂时屏蔽
|
||||
// const current = ref(0)
|
||||
// const branchGroup = ref<any[]>([])
|
||||
// const branchActiveKey = ref([])
|
||||
// const branchCheckKeys = ref([])
|
||||
|
||||
// const terms = [
|
||||
// {
|
||||
// terms: [
|
||||
// {
|
||||
// column: 'id',
|
||||
// termType: 'alarm-bind-rule$not',
|
||||
// value: props.id,
|
||||
// type: 'and',
|
||||
// },
|
||||
// {
|
||||
// column: 'triggerType',
|
||||
// termType: 'eq',
|
||||
// value: props.type === 'other' ? undefined : 'device',
|
||||
// },
|
||||
// ],
|
||||
// type: 'and',
|
||||
// },
|
||||
// ];
|
||||
|
||||
const terms = [
|
||||
{
|
||||
|
@ -275,127 +150,86 @@ const terms = [
|
|||
type: 'and',
|
||||
},
|
||||
];
|
||||
|
||||
const params = ref();
|
||||
const typeMap = new Map();
|
||||
typeMap.set('manual', {
|
||||
text: '手动触发',
|
||||
img: getImage('/scene/scene-hand.png'),
|
||||
icon: getImage('/scene/trigger-type-icon/manual.png'),
|
||||
tip: '适用于第三方平台向物联网平台下发指令控制设备',
|
||||
});
|
||||
typeMap.set('timer', {
|
||||
text: '定时触发',
|
||||
img: getImage('/scene/scene-timer.png'),
|
||||
icon: getImage('/scene/trigger-type-icon/timing.png'),
|
||||
tip: '适用于定期执行固定任务',
|
||||
});
|
||||
typeMap.set('device', {
|
||||
text: '设备触发',
|
||||
img: getImage('/scene/scene-device.png'),
|
||||
icon: getImage('/scene/trigger-type-icon/device.png'),
|
||||
tip: '适用于设备数据或行为满足触发条件时,执行指定的动作',
|
||||
});
|
||||
const _selectedRowKeys = ref<string[]>([]);
|
||||
const handleClick = (dt: any) => {
|
||||
if (_selectedRowKeys.value.includes(dt.id)) {
|
||||
const _index = _selectedRowKeys.value.findIndex((i) => i === dt.id);
|
||||
_selectedRowKeys.value.splice(_index, 1);
|
||||
} else {
|
||||
_selectedRowKeys.value = [..._selectedRowKeys.value, dt.id];
|
||||
}
|
||||
// _selectedRowKeys.value = [dt.id]
|
||||
};
|
||||
/**
|
||||
* 取消选择事件
|
||||
*/
|
||||
const onSelectChange = (arr: any[]) => {
|
||||
_selectedRowKeys.value = arr
|
||||
};
|
||||
const tableRef = ref();
|
||||
|
||||
// const branchClick = (id: string) => {
|
||||
// const keys = new Set(branchActiveKey.value)
|
||||
const selectedKeysMap = reactive({})
|
||||
const loading = ref(false)
|
||||
|
||||
const { data: activeKeys } = useRequest(queryBindScene, {
|
||||
defaultParams: { terms: [{ column: 'alarmId', value: props.id}]},
|
||||
onSuccess(res) {
|
||||
const activeMap = res.result.data.reduce((prev, next) => {
|
||||
|
||||
// }
|
||||
if (prev[next.ruleId]) {
|
||||
prev[next.ruleId].push(next.branchIndex)
|
||||
} else {
|
||||
prev[next.ruleId] = [next.branchIndex]
|
||||
}
|
||||
return prev
|
||||
}, {})
|
||||
return activeMap || {}
|
||||
},
|
||||
defaultValue: {}
|
||||
})
|
||||
|
||||
const handleSearch = (e: any) => {
|
||||
params.value = e;
|
||||
};
|
||||
const emit = defineEmits(['closeSave', 'saveScene']);
|
||||
|
||||
// const next = () => {
|
||||
// if (_selectedRowKeys.value.length) {
|
||||
// query({
|
||||
// pageSize: 99,
|
||||
// terms: [{column: 'id', termType: 'in', value: _selectedRowKeys.value.join(',')}]
|
||||
// }).then(res => {
|
||||
// if (res.success) {
|
||||
// branchGroup.value = handleSceneBranches(res.result.data) || []
|
||||
|
||||
// console.log(branchGroup.value)
|
||||
// }
|
||||
// })
|
||||
// current.value += 1
|
||||
|
||||
// } else {
|
||||
// onlyMessage('请选择场景', 'warning')
|
||||
// }
|
||||
// }
|
||||
|
||||
// const branchCheck = (checkedKeys: string[], { checkedNodes }) => {
|
||||
// branchCheckKeys.value = checkedNodes.filter(item => item.branchId).map(item => ({
|
||||
// branchIndex: item.branchId,
|
||||
// ruleId: item.sceneId,
|
||||
// alarmId: props.id,
|
||||
// }))
|
||||
// }
|
||||
|
||||
// const prev = () => {
|
||||
// current.value -= 1
|
||||
// }
|
||||
/**
|
||||
* 保存选中关联场景
|
||||
*/
|
||||
// const saveCorrelation = async () => {
|
||||
// if (_selectedRowKeys.value.length === 0 && branchCheckKeys.value.length === 0) {
|
||||
// onlyMessage('请选择至少一条数据', 'error')
|
||||
// return
|
||||
// }
|
||||
|
||||
// const list = props.type === 'other' ? _selectedRowKeys.value.map((item: any) => {
|
||||
// return {
|
||||
// alarmId: props.id,
|
||||
// ruleId: item,
|
||||
// };
|
||||
// }) : branchCheckKeys.value
|
||||
|
||||
// const res = await bindScene([...list]);
|
||||
// if (res.status === 200) {
|
||||
// onlyMessage('操作成功');
|
||||
// emit('saveScene');
|
||||
// }
|
||||
// };
|
||||
|
||||
const saveCorrelation = async () => {
|
||||
if (_selectedRowKeys.value.length > 0) {
|
||||
const list = _selectedRowKeys.value.map((item: any) => {
|
||||
return {
|
||||
alarmId: props.id,
|
||||
ruleId: item,
|
||||
};
|
||||
});
|
||||
const res = await bindScene([...list]);
|
||||
if (res.status === 200) {
|
||||
onlyMessage('操作成功');
|
||||
emit('saveScene');
|
||||
if (Object.keys(selectedKeysMap).length > 0) {
|
||||
const list = Object.keys(selectedKeysMap).reduce((prev, next) => {
|
||||
const branches = selectedKeysMap[next].map(key => {
|
||||
return {
|
||||
alarmId: props.id,
|
||||
ruleId: next,
|
||||
branchIndex: key
|
||||
}
|
||||
} else {
|
||||
onlyMessage('请选择至少一条数据', 'error');
|
||||
})
|
||||
prev.push(...branches)
|
||||
return prev
|
||||
}, [])
|
||||
|
||||
loading.value = true
|
||||
const res = await bindScene(list).finally(() =>{
|
||||
loading.value = false
|
||||
});
|
||||
if (res.success) {
|
||||
onlyMessage('操作成功');
|
||||
emit('saveScene');
|
||||
}
|
||||
} else {
|
||||
onlyMessage('请选择至少一条数据', 'error');
|
||||
}
|
||||
};
|
||||
const closeModal = () => {
|
||||
emit('closeSave');
|
||||
};
|
||||
|
||||
const reload = () => {
|
||||
tableRef.value?.reload()
|
||||
}
|
||||
|
||||
const onAlarmChange = (key: string, selected: boolean, record: Record<string, any>) => {
|
||||
const keys = selectedKeysMap[record.id]
|
||||
|
||||
const keySet = new Set(keys)
|
||||
if (selected) {
|
||||
keySet.add(key)
|
||||
} else {
|
||||
keySet.delete(key)
|
||||
}
|
||||
|
||||
if (keySet.size === 0) {
|
||||
delete selectedKeysMap[record.id]
|
||||
}
|
||||
|
||||
selectedKeysMap[record.id] = [...keySet.values()]
|
||||
}
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.subTitle {
|
||||
|
|
|
@ -20,8 +20,15 @@
|
|||
<template #alarmTime="slotProps">{{
|
||||
dayjs(slotProps.alarmTime).format('YYYY-MM-DD HH:mm:ss')
|
||||
}}</template>
|
||||
<template #sourceId="slotProps"
|
||||
>设备ID:<a-button
|
||||
type="link"
|
||||
@click="() => gotoDevice(slotProps.sourceId)"
|
||||
>{{ slotProps.sourceId }}</a-button
|
||||
></template
|
||||
>
|
||||
<template #action="slotProps">
|
||||
<j-space
|
||||
<j-space :size="16"
|
||||
><template
|
||||
v-for="i in getActions(slotProps, 'table')"
|
||||
:key="i.key"
|
||||
|
@ -46,29 +53,41 @@
|
|||
</JProTable>
|
||||
</FullPage>
|
||||
<Info
|
||||
v-if="visible"
|
||||
v-if="visible && alarmType !== 'device'"
|
||||
:data="current"
|
||||
@close="close"
|
||||
/>
|
||||
<LogDetail
|
||||
v-if="visible && alarmType === 'device'"
|
||||
:data="current"
|
||||
@close="close"
|
||||
:description="description"
|
||||
/>
|
||||
</page-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {detail, queryHistoryLogList} from '@/api/rule-engine/log';
|
||||
import { detail, queryLogList } from '@/api/rule-engine/log';
|
||||
import { detail as configurationDetail } from '@/api/rule-engine/configuration';
|
||||
import { useRoute } from 'vue-router';
|
||||
import dayjs from 'dayjs';
|
||||
import { useAlarmStore } from '@/store/alarm';
|
||||
import Info from './info.vue';
|
||||
import { useRouterParams } from '@/utils/hooks/useParams';
|
||||
import { useMenuStore } from 'store/menu';
|
||||
import LogDetail from '../TabComponent/components/LogDetail.vue';
|
||||
const route = useRoute();
|
||||
const id = route.params?.id;
|
||||
const menuStory = useMenuStore();
|
||||
const { params: routerParams } = useRouterParams();
|
||||
let visible = ref(false);
|
||||
let description = ref<string>();
|
||||
const tableRef = ref()
|
||||
const columns = [
|
||||
const visible = ref(false);
|
||||
const tableRef = ref();
|
||||
const params = ref({});
|
||||
const alarmStore = useAlarmStore();
|
||||
const { data } = alarmStore;
|
||||
const current = ref(); // 当前告警记录信息
|
||||
const details = ref(); // 告警记录的详情
|
||||
const alarmType = ref();
|
||||
const columns = ref([
|
||||
{
|
||||
title: '告警时间',
|
||||
dataIndex: 'alarmTime',
|
||||
|
@ -94,7 +113,7 @@ const columns = [
|
|||
key: 'action',
|
||||
scopedSlots: true,
|
||||
},
|
||||
];
|
||||
]);
|
||||
const getActions = (
|
||||
data: Partial<Record<string, any>>,
|
||||
type?: 'table',
|
||||
|
@ -109,7 +128,7 @@ const getActions = (
|
|||
tooltip: {
|
||||
title: '查看',
|
||||
},
|
||||
icon: 'SearchOutlined',
|
||||
icon: 'EyeOutlined',
|
||||
onClick: () => {
|
||||
current.value = data;
|
||||
visible.value = true;
|
||||
|
@ -126,72 +145,105 @@ const terms = [
|
|||
type: 'and',
|
||||
},
|
||||
];
|
||||
let params = ref({});
|
||||
const alarmStore = useAlarmStore();
|
||||
const { data } = alarmStore;
|
||||
let current = ref(); // 当前告警记录信息
|
||||
let details = ref(); // 告警记录的详情
|
||||
/**
|
||||
* 获取详情列表
|
||||
*/
|
||||
const queryList = async (params: any) => {
|
||||
if(data.current?.alarmConfigId){
|
||||
const res = await queryHistoryLogList(data.current?.alarmConfigId,{
|
||||
...params,
|
||||
// sorts: [{ name: 'alarmTime', order: 'desc' }],
|
||||
});
|
||||
if (res.status === 200) {
|
||||
details.value = res.result.data[0];
|
||||
return {
|
||||
code: res.message,
|
||||
result: {
|
||||
data: res.result.data,
|
||||
pageIndex: res.result.pageIndex,
|
||||
pageSize: res.result.pageSize,
|
||||
total: res.result.total,
|
||||
},
|
||||
status: res.status,
|
||||
};
|
||||
if (data.current?.alarmConfigId) {
|
||||
const res: any = await queryLogList(data.current?.alarmConfigId, {
|
||||
...params,
|
||||
// sorts: [{ name: 'alarmTime', order: 'desc' }],
|
||||
});
|
||||
if (res.status === 200 && res.result?.data) {
|
||||
details.value = res.result.data[0];
|
||||
return {
|
||||
code: res.message,
|
||||
result: {
|
||||
data: res.result.data,
|
||||
pageIndex: res.result.pageIndex,
|
||||
pageSize: res.result.pageSize,
|
||||
total: res.result.total,
|
||||
},
|
||||
status: res.status,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
code: 200,
|
||||
result: {
|
||||
data: [],
|
||||
pageIndex: 0,
|
||||
pageSize: 0,
|
||||
total: 0,
|
||||
},
|
||||
status: 200,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
code: 200,
|
||||
result: {
|
||||
data: [],
|
||||
pageIndex: 0,
|
||||
pageSize: 0,
|
||||
total: 0,
|
||||
},
|
||||
status: 200,
|
||||
};
|
||||
}
|
||||
};
|
||||
const gotoDevice = (id) => {
|
||||
menuStory.jumpPage('device/Instance/Detail', { id, tab: 'Running' });
|
||||
};
|
||||
/**
|
||||
* 根据id初始化数据
|
||||
*/
|
||||
|
||||
watch(() => id, async () => {
|
||||
const res = await detail(id);
|
||||
if (res.status === 200) {
|
||||
data.current = res.result || {};
|
||||
tableRef.value?.reload()
|
||||
if (res.result?.targetType === 'device') {
|
||||
columns.splice(2, 0, {
|
||||
dataIndex: 'targetName',
|
||||
title: '告警设备',
|
||||
key: 'targetName',
|
||||
});
|
||||
}
|
||||
configurationDetail(res.result?.alarmConfigId).then((res: any) => {
|
||||
if (res.status === 200) {
|
||||
description.value = res.result?.description;
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
deep: true,
|
||||
immediate: true
|
||||
})
|
||||
watch(
|
||||
() => id,
|
||||
async () => {
|
||||
const res = await detail(id);
|
||||
if (res.status === 200) {
|
||||
data.current = res.result || {};
|
||||
tableRef.value?.reload();
|
||||
alarmType.value = res.result?.targetType;
|
||||
if (alarmType.value === 'device') {
|
||||
columns.value = [
|
||||
{
|
||||
title: '告警时间',
|
||||
dataIndex: 'alarmTime',
|
||||
key: 'alarmTime',
|
||||
scopedSlots: true,
|
||||
search: {
|
||||
type: 'date',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '触发条件',
|
||||
dataIndex: 'triggerDesc',
|
||||
key: 'triggerDesc',
|
||||
},
|
||||
{
|
||||
title: '告警源',
|
||||
dataIndex: 'sourceId',
|
||||
key: 'sourceId',
|
||||
scopedSlots: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '告警原因',
|
||||
dataIndex: 'actualDesc',
|
||||
key: 'actualDesc',
|
||||
scopedSlots: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
scopedSlots: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
const handleSearch = (_params: any) => {
|
||||
params.value = _params;
|
||||
};
|
||||
|
@ -210,5 +262,4 @@ watchEffect(() => {
|
|||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
</style>
|
||||
<style lang="less" scoped></style>
|
||||
|
|
|
@ -11,8 +11,9 @@
|
|||
:columns="columns"
|
||||
:request="handleSearch"
|
||||
:params="params"
|
||||
:gridColumns="[1, 1, 2]"
|
||||
:gridColumn="2"
|
||||
:gridColumns="[1, 1, 1]"
|
||||
:gridColumn="1"
|
||||
model="CARD"
|
||||
ref="tableRef"
|
||||
>
|
||||
<template #card="slotProps">
|
||||
|
@ -20,20 +21,13 @@
|
|||
:value="slotProps"
|
||||
v-bind="slotProps"
|
||||
:actions="getActions(slotProps, 'card')"
|
||||
:statusText="
|
||||
data.defaultLevel.find(
|
||||
(i) => i.level === slotProps.level,
|
||||
)?.title || slotProps.level
|
||||
"
|
||||
:status="slotProps.level"
|
||||
:status="slotProps.state.value"
|
||||
:statusNames="{
|
||||
1: 'level1',
|
||||
2: 'level2',
|
||||
3: 'level3',
|
||||
4: 'level4',
|
||||
5: 'level5',
|
||||
warning: 'error',
|
||||
normal: 'default',
|
||||
}"
|
||||
:customBadge="true"
|
||||
:statusText="slotProps.state.text"
|
||||
@click="() => showDrawer(slotProps)"
|
||||
>
|
||||
<template #img>
|
||||
<img
|
||||
|
@ -42,56 +36,121 @@
|
|||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<Ellipsis style="width: calc(100% - 100px)">
|
||||
<span style="font-weight: 500">
|
||||
{{ slotProps.alarmName }}
|
||||
</span>
|
||||
</Ellipsis>
|
||||
<div class="alarmTitle">
|
||||
<div class="alarmName">
|
||||
<Ellipsis style="width: 100%">
|
||||
<span
|
||||
style="
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
"
|
||||
>
|
||||
{{ slotProps.alarmName }}
|
||||
</span>
|
||||
</Ellipsis>
|
||||
</div>
|
||||
<!-- <div
|
||||
class="alarmLevel"
|
||||
:style="{
|
||||
backgroundColor: levelColorMap.get(
|
||||
'level' + slotProps.level,
|
||||
),
|
||||
}"
|
||||
>
|
||||
<Ellipsis>
|
||||
<span>
|
||||
{{
|
||||
levelMap?.[slotProps.level] ||
|
||||
slotProps.level
|
||||
}}
|
||||
</span>
|
||||
</Ellipsis>
|
||||
</div> -->
|
||||
<div style="display: flex;max-width: 50%;">
|
||||
<LevelIcon :level="slotProps.level" ></LevelIcon>
|
||||
<Ellipsis>
|
||||
{{ levelMap[slotProps.level] }}
|
||||
</Ellipsis>
|
||||
</div>
|
||||
</div>
|
||||
<j-row :gutter="24">
|
||||
<j-col :span="8" class="content-left">
|
||||
<div class="content-left-title">
|
||||
{{ titleMap.get(slotProps.targetType) }}
|
||||
</div>
|
||||
<j-col
|
||||
:span="
|
||||
props.type === 'device' ||
|
||||
slotProps.targetType === 'device'
|
||||
? 6
|
||||
: 8
|
||||
"
|
||||
class="content-left"
|
||||
>
|
||||
<div class="content-title">告警维度</div>
|
||||
<Ellipsis
|
||||
><div>
|
||||
{{ slotProps?.targetName }}
|
||||
</div></Ellipsis
|
||||
>
|
||||
</j-col>
|
||||
<j-col :span="8">
|
||||
<div class="content-right-title">
|
||||
<j-col
|
||||
:span="
|
||||
props.type === 'device' ||
|
||||
slotProps.targetType === 'device'
|
||||
? 6
|
||||
: 8
|
||||
"
|
||||
>
|
||||
<div class="content-title">
|
||||
最近告警时间
|
||||
</div>
|
||||
<Ellipsis
|
||||
><div>
|
||||
<Ellipsis>
|
||||
<div>
|
||||
{{
|
||||
dayjs(
|
||||
slotProps?.alarmTime,
|
||||
).format('YYYY-MM-DD HH:mm:ss')
|
||||
).format(
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
) +
|
||||
'至' +
|
||||
(slotProps?.state?.value ===
|
||||
'warning'
|
||||
? '当前时间'
|
||||
: dayjs(
|
||||
slotProps?.handleTime,
|
||||
).format(
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
))
|
||||
}}
|
||||
</div>
|
||||
</Ellipsis>
|
||||
</j-col>
|
||||
<j-col
|
||||
:span="
|
||||
props.type === 'device' ||
|
||||
slotProps.targetType === 'device'
|
||||
? 6
|
||||
: 8
|
||||
"
|
||||
>
|
||||
<div class="content-title">
|
||||
告警持续时长
|
||||
</div>
|
||||
<Ellipsis
|
||||
><Duration :data="slotProps"></Duration
|
||||
></Ellipsis>
|
||||
</j-col>
|
||||
<j-col
|
||||
:span="6"
|
||||
v-if="
|
||||
props.type === 'device' ||
|
||||
slotProps.targetType === 'device'
|
||||
"
|
||||
>
|
||||
<div class="content-title">告警原因</div>
|
||||
<Ellipsis
|
||||
><div>
|
||||
{{ slotProps?.actualDesc || '--' }}
|
||||
</div></Ellipsis
|
||||
>
|
||||
</j-col>
|
||||
<j-col :span="8">
|
||||
<div class="content-right-title">状态</div>
|
||||
<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>
|
||||
</j-col>
|
||||
</j-row>
|
||||
</template>
|
||||
<template #actions="item">
|
||||
|
@ -116,77 +175,21 @@
|
|||
</template>
|
||||
</CardBox>
|
||||
</template>
|
||||
<template #targetType="slotProps">
|
||||
{{ titleMap.get(slotProps.targetType) }}
|
||||
</template>
|
||||
<template #alarmTime="slotProps">
|
||||
{{
|
||||
dayjs(slotProps.alarmTime).format('YYYY-MM-DD HH:mm:ss')
|
||||
}}
|
||||
</template>
|
||||
<template #level="slotProps">
|
||||
<Ellipsis style="width: calc(100% - 20px)">
|
||||
{{
|
||||
data.defaultLevel.find((i) => {
|
||||
return i.level === slotProps.level;
|
||||
}).title
|
||||
}}
|
||||
</Ellipsis>
|
||||
</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>
|
||||
</FullPage>
|
||||
<SolveComponent
|
||||
:data="data"
|
||||
:data="data.current"
|
||||
v-if="data.solveVisible"
|
||||
@closeSolve="closeSolve"
|
||||
@refresh="refresh"
|
||||
/>
|
||||
<LogDrawer
|
||||
v-if="visibleDrawer"
|
||||
:logData="drawerData"
|
||||
:typeMap="titleMap"
|
||||
:levelMap="levelMap"
|
||||
@closeDrawer="visibleDrawer = false"
|
||||
@refreshTable="refreshTable"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -194,27 +197,22 @@
|
|||
<script lang="ts" setup>
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { getOrgList, query, getAlarmProduct } from '@/api/rule-engine/log';
|
||||
import { queryLevel } from '@/api/rule-engine/config';
|
||||
import { useAlarmStore } from '@/store/alarm';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import dayjs from 'dayjs';
|
||||
import type { ActionsType } from '@/components/Table';
|
||||
import SolveComponent from '../SolveComponent/index.vue';
|
||||
import { useMenuStore } from '@/store/menu';
|
||||
import { usePermissionStore } from '@/store/permission';
|
||||
import LogDrawer from './components/DetailDrawer.vue';
|
||||
import Duration from '../components/Duration.vue';
|
||||
import { useAlarmLevel } from '@/hook';
|
||||
const menuStory = useMenuStore();
|
||||
const tableRef = ref();
|
||||
const { levelMap, levelList } = useAlarmLevel();
|
||||
const alarmStore = useAlarmStore();
|
||||
const { data } = storeToRefs(alarmStore);
|
||||
|
||||
const getDefaultLevel = () => {
|
||||
queryLevel().then((res) => {
|
||||
if (res.status === 200) {
|
||||
data.value.defaultLevel = res.result?.levels || [];
|
||||
}
|
||||
});
|
||||
};
|
||||
getDefaultLevel();
|
||||
const drawerData = ref();
|
||||
const visibleDrawer = ref(false);
|
||||
const props = defineProps<{
|
||||
type: string;
|
||||
id?: string;
|
||||
|
@ -231,6 +229,7 @@ titleMap.set('product', '产品');
|
|||
titleMap.set('device', '设备');
|
||||
titleMap.set('other', '其他');
|
||||
titleMap.set('org', '组织');
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: '配置名称',
|
||||
|
@ -255,12 +254,9 @@ const columns = [
|
|||
width: 200,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: data.value.defaultLevel.map((item: any) => {
|
||||
return {
|
||||
label: item.title,
|
||||
value: item.level,
|
||||
};
|
||||
}),
|
||||
options: async () => {
|
||||
return levelList.value;
|
||||
},
|
||||
},
|
||||
scopedSlots: true,
|
||||
},
|
||||
|
@ -306,48 +302,6 @@ const newColumns = computed(() => {
|
|||
title: '产品名称',
|
||||
dataIndex: 'targetName',
|
||||
key: 'targetName',
|
||||
// search: {
|
||||
// type: 'select',
|
||||
// options: async () => {
|
||||
// const termType = [
|
||||
// {
|
||||
// column: 'targetType',
|
||||
// termType: 'eq',
|
||||
// type: 'and',
|
||||
// value: props.type,
|
||||
// },
|
||||
// ];
|
||||
|
||||
// if (props.id) {
|
||||
// termType.push({
|
||||
// termType: 'eq',
|
||||
// column: 'alarmConfigId',
|
||||
// value: props.id,
|
||||
// type: 'and',
|
||||
// });
|
||||
// }
|
||||
|
||||
// const resp: any = await handleSearch({
|
||||
// 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.targetId) {
|
||||
// listMap.set(item.targetId, {
|
||||
// label: item.targetName,
|
||||
// value: item.targetId,
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
|
||||
// return [...listMap.values()];
|
||||
// }
|
||||
// return [];
|
||||
// },
|
||||
// },
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
|
@ -449,16 +403,17 @@ const search = (data: any) => {
|
|||
type: 'and',
|
||||
});
|
||||
}
|
||||
if (
|
||||
props.type === 'device' &&
|
||||
data?.terms[0]?.terms[0]?.column === 'product_id'
|
||||
) {
|
||||
params.value.terms = [
|
||||
{
|
||||
column: 'targetId$dev-instance',
|
||||
value: [data?.terms[0]?.terms[0]],
|
||||
},
|
||||
];
|
||||
if (props.type === 'device') {
|
||||
data?.terms.forEach((i: any, _index: number) => {
|
||||
i.terms.forEach((item: any, index: number) => {
|
||||
if (item.column === 'product_id') {
|
||||
params.value.terms[_index].terms[index] = {
|
||||
column: 'targetId$dev-instance',
|
||||
value: [data?.terms[0]?.terms[0]],
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
if (props.id) {
|
||||
params.value.terms.push({
|
||||
|
@ -490,15 +445,6 @@ const getActions = (
|
|||
data.value.current = currentData;
|
||||
data.value.solveVisible = true;
|
||||
},
|
||||
// popConfirm: {
|
||||
// title: !usePermissionStore().hasPermission(
|
||||
// 'rule-engine/Alarm/Log:action',
|
||||
// )
|
||||
// ? '暂无权限,请联系管理员'
|
||||
// : data.state?.value === 'normal'
|
||||
// ? '无告警'
|
||||
// : '',
|
||||
// },
|
||||
},
|
||||
{
|
||||
key: 'log',
|
||||
|
@ -536,8 +482,21 @@ const getActions = (
|
|||
*/
|
||||
const closeSolve = () => {
|
||||
data.value.solveVisible = false;
|
||||
};
|
||||
const refresh = () => {
|
||||
data.value.solveVisible = false;
|
||||
tableRef.value.reload(params.value);
|
||||
};
|
||||
|
||||
const refreshTable = () => {
|
||||
tableRef.value.reload(params.value);
|
||||
};
|
||||
const showDrawer = (data: any) => {
|
||||
if (data.targetType === 'device') {
|
||||
drawerData.value = data;
|
||||
visibleDrawer.value = true;
|
||||
}
|
||||
};
|
||||
onMounted(() => {
|
||||
if (props.type !== 'all' && !props.id) {
|
||||
params.value.terms = [
|
||||
|
@ -565,14 +524,23 @@ onMounted(() => {
|
|||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.content-left {
|
||||
border-right: 0.2px solid rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.content-right-title {
|
||||
.content-title {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
}
|
||||
.content-left-title {
|
||||
font-size: 18px;
|
||||
.alarmTitle {
|
||||
display: flex;
|
||||
width: 60%;
|
||||
|
||||
.alarmLevel {
|
||||
width: 30%;
|
||||
text-align: center;
|
||||
padding: 5px;
|
||||
}
|
||||
.alarmName {
|
||||
max-width: 30%;
|
||||
color: #1a1a1a;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -66,6 +66,8 @@
|
|||
<li
|
||||
v-for="(item, i) in state.ranking"
|
||||
:key="item.targetId"
|
||||
style="cursor: pointer"
|
||||
@click="jumpToDetail(item.targetId)"
|
||||
>
|
||||
<img
|
||||
:src="
|
||||
|
@ -103,7 +105,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { Empty } from 'jetlinks-ui-components';
|
||||
import { getImage } from '@/utils/comm';
|
||||
import { getImage, onlyMessage } from '@/utils/comm';
|
||||
import Charts from './components/Charts.vue';
|
||||
import TopCard from './components/TopCard.vue';
|
||||
import NewAlarm from './components/NewAlarm.vue';
|
||||
|
@ -120,6 +122,10 @@ import {
|
|||
getAlarmLevel,
|
||||
} from '@/api/rule-engine/dashboard';
|
||||
import dayjs from 'dayjs';
|
||||
import { useMenuStore } from 'store/menu';
|
||||
import { query } from '@/api/rule-engine/scene';
|
||||
|
||||
const menuStory = useMenuStore();
|
||||
let currentMonAlarm = ref<Footer[]>([
|
||||
{
|
||||
title: '当月告警',
|
||||
|
@ -232,12 +238,11 @@ const getDashBoard = () => {
|
|||
dashboard([today, thisMonth, fifteen]).then((res) => {
|
||||
if (res.status == 200) {
|
||||
const _data = res.result as DashboardItem[];
|
||||
state.today = _data.find(
|
||||
(item) => item.group === 'today',
|
||||
)?.data.value || 0;
|
||||
state.thisMonth = _data.find(
|
||||
(item) => item.group === 'thisMonth',
|
||||
)?.data.value || 0;
|
||||
state.today =
|
||||
_data.find((item) => item.group === 'today')?.data.value || 0;
|
||||
state.thisMonth =
|
||||
_data.find((item) => item.group === 'thisMonth')?.data.value ||
|
||||
0;
|
||||
currentMonAlarm.value[0].value = state.thisMonth;
|
||||
const fifteenData = _data
|
||||
.filter((item) => item.group === '15day')
|
||||
|
@ -369,12 +374,12 @@ const selectChange = () => {
|
|||
const month = day * 30;
|
||||
const year = 365 * day;
|
||||
|
||||
if (dt <= (hour + 10)) {
|
||||
limit = 60
|
||||
format = 'HH:mm';
|
||||
if (dt <= hour + 10) {
|
||||
limit = 60;
|
||||
format = 'HH:mm';
|
||||
} else if (dt > hour && dt <= day) {
|
||||
time = '1h'
|
||||
limit = 24;
|
||||
time = '1h';
|
||||
limit = 24;
|
||||
} else if (dt > day && dt < year) {
|
||||
limit = Math.abs(Math.ceil(dt / day)) + 1;
|
||||
time = '1d';
|
||||
|
@ -438,17 +443,18 @@ const selectChange = () => {
|
|||
res.result
|
||||
.filter((item: any) => item.group === 'alarmTrend')
|
||||
.forEach((item: any) => {
|
||||
if(time === '1d'){
|
||||
item.data.timeString = item.data.timeString.split(' ')[0]
|
||||
if (time === '1d') {
|
||||
item.data.timeString =
|
||||
item.data.timeString.split(' ')[0];
|
||||
}
|
||||
xData.push(item.data.timeString);
|
||||
sData.push(item.data.value);
|
||||
});
|
||||
const data:any = JSON.parse(JSON.stringify(sData))
|
||||
if (data && data.length > 0 ) {
|
||||
const maxY = data.sort((a,b)=>{
|
||||
return b-a
|
||||
})[0]
|
||||
const data: any = JSON.parse(JSON.stringify(sData));
|
||||
if (data && data.length > 0) {
|
||||
const maxY = data.sort((a, b) => {
|
||||
return b - a;
|
||||
})[0];
|
||||
alarmStatisticsOption.value = {
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
|
@ -467,7 +473,7 @@ const selectChange = () => {
|
|||
grid: {
|
||||
top: '2%',
|
||||
bottom: '5%',
|
||||
left: maxY < 1000 ? 50 : maxY.toString().length * 10,
|
||||
left: maxY < 1000 ? 50 : maxY.toString().length * 10,
|
||||
right: '48px',
|
||||
},
|
||||
series: [
|
||||
|
@ -500,8 +506,8 @@ const selectChange = () => {
|
|||
},
|
||||
],
|
||||
};
|
||||
}else{
|
||||
console.log('data is empty ')
|
||||
} else {
|
||||
console.log('data is empty ');
|
||||
}
|
||||
state.ranking = res.result
|
||||
?.filter(
|
||||
|
@ -517,6 +523,40 @@ const selectChange = () => {
|
|||
}
|
||||
});
|
||||
};
|
||||
|
||||
const jumpToDetail = (id: string) => {
|
||||
switch (queryCodition.targetType) {
|
||||
case 'device':
|
||||
menuStory.jumpPage('device/Instance/Detail', { id });
|
||||
break;
|
||||
case 'product':
|
||||
menuStory.jumpPage('device/Product/Detail', { id });
|
||||
break;
|
||||
case 'org':
|
||||
menuStory.jumpPage('system/Department', {}, { id });
|
||||
break;
|
||||
case 'other':
|
||||
query({
|
||||
terms: [
|
||||
{
|
||||
column: 'id',
|
||||
termType: 'eq',
|
||||
value: id,
|
||||
},
|
||||
],
|
||||
}).then((res:any) => {
|
||||
if (res.success && res.result?.data) {
|
||||
if(res.result?.total){
|
||||
const scene = res.result.data[0]
|
||||
menuStory.jumpPage('rule-engine/Scene/Save',{}, { triggerType: scene.trigger.type, id: id });
|
||||
}else{
|
||||
onlyMessage('数据已经删除','error')
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.alarm-card {
|
||||
|
@ -606,4 +646,4 @@ const selectChange = () => {
|
|||
width: 100%;
|
||||
// height: 100%;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
v-model:value="paramsValue.termType"
|
||||
@select="termsTypeSelect"
|
||||
/>
|
||||
<div v-if="!['notnull', 'isnull'].includes(paramsValue.termType)">
|
||||
<div v-if="!['notnull', 'isnull'].includes(paramsValue.termType)" style="display: flex">
|
||||
<DoubleParamsDropdown
|
||||
v-if="showDouble"
|
||||
icon="icon-canshu"
|
||||
|
@ -64,7 +64,7 @@
|
|||
<ParamsDropdown
|
||||
v-else
|
||||
icon="icon-canshu"
|
||||
placeholder="参数值"
|
||||
:placeholder="tabsOptions[0]?.component === 'array' ? '多个值以英文逗号隔开':'参数值'"
|
||||
:options="valueOptions"
|
||||
:metricOptions="metricOption"
|
||||
:tabsOptions="tabsOptions"
|
||||
|
@ -74,15 +74,9 @@
|
|||
@select="valueSelect"
|
||||
/>
|
||||
</div>
|
||||
<j-popconfirm
|
||||
title="确认删除?"
|
||||
@confirm="onDelete"
|
||||
:overlayStyle="{ minWidth: '180px' }"
|
||||
>
|
||||
<div v-show="showDelete" class="button-delete">
|
||||
<AIcon type="CloseOutlined" />
|
||||
</div>
|
||||
</j-popconfirm>
|
||||
<ConfirmModal title="确认删除?" :onConfirm="onDelete" className="button-delete" :show="showDelete">
|
||||
<AIcon type="CloseOutlined" />
|
||||
</ConfirmModal>
|
||||
</div>
|
||||
<div class="term-add" @click.stop="termAdd" v-if="isLast">
|
||||
<div class="terms-content">
|
||||
|
@ -308,7 +302,7 @@ watch(
|
|||
|
||||
const showDouble = computed(() => {
|
||||
const isRange = paramsValue.termType
|
||||
? arrayParamsKey.includes(paramsValue.termType)
|
||||
? doubleParamsKey.includes(paramsValue.termType)
|
||||
: false;
|
||||
const isSourceMetric = paramsValue.value?.source === 'metric';
|
||||
if (metricsCacheOption.value.length) {
|
||||
|
@ -330,7 +324,7 @@ const showDouble = computed(() => {
|
|||
|
||||
const showArray = computed(()=>{
|
||||
const isRange = paramsValue.termType ? arrayParamsKey.includes(paramsValue.termType) : false;
|
||||
const isSourceMetric = paramsValue.value?.source === 'metric';
|
||||
const isSourceMetric = paramsValue.value?.source === 'metric';
|
||||
if (metricsCacheOption.value.length) {
|
||||
metricOption.value = metricsCacheOption.value.filter((item) =>
|
||||
isRange ? item.range : !item.range,
|
||||
|
@ -345,7 +339,7 @@ const showArray = computed(()=>{
|
|||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return false;
|
||||
})
|
||||
|
||||
const mouseover = () => {
|
||||
|
@ -411,9 +405,10 @@ const columnSelect = (option: any) => {
|
|||
nextTick(() => {
|
||||
formItemContext.onFieldChange();
|
||||
});
|
||||
|
||||
formModel.value.options!.when[props.branches_Index].terms[props.whenName].terms[
|
||||
props.termsName
|
||||
][0] = option.name;
|
||||
][0] = option.name || option.fullName;
|
||||
formModel.value.options!.when[props.branches_Index].terms[props.whenName].terms[
|
||||
props.termsName
|
||||
][1] = paramsValue.termType;
|
||||
|
@ -478,6 +473,7 @@ const valueSelect = (
|
|||
labelObj: Record<number, any>,
|
||||
option: any,
|
||||
) => {
|
||||
|
||||
if (paramsValue.value?.source === 'metric') {
|
||||
paramsValue.value.metric = option?.id;
|
||||
}
|
||||
|
@ -500,7 +496,7 @@ const typeSelect = (e: any) => {
|
|||
};
|
||||
|
||||
const termAdd = () => {
|
||||
const terms = {
|
||||
const termsData = {
|
||||
column: undefined,
|
||||
value: {
|
||||
source: 'manual',
|
||||
|
@ -512,10 +508,9 @@ const termAdd = () => {
|
|||
};
|
||||
formModel.value.branches?.[props.branchName]?.when?.[
|
||||
props.whenName
|
||||
]?.terms?.push(terms);
|
||||
formModel.value.options!.when[props.branchName].terms[props.whenName].terms[
|
||||
props.termsName
|
||||
].push(['', '', '', '并且']);
|
||||
]?.terms?.push(termsData);
|
||||
|
||||
formModel.value.options!.when[props.branchName].terms[props.whenName].terms.push(['', '', '', '并且']);
|
||||
};
|
||||
|
||||
const onDelete = () => {
|
||||
|
@ -527,6 +522,26 @@ const onDelete = () => {
|
|||
].terms.splice(props.termsName, 1);
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
const isRange = paramsValue.termType ? arrayParamsKey.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;
|
||||
})
|
||||
|
||||
nextTick(() => {
|
||||
Object.assign(
|
||||
paramsValue,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class='actions-terms'>
|
||||
<TitleComponent data='触发条件' style='font-size: 14px;' >
|
||||
<TitleComponent data='执行动作' style='font-size: 14px;' >
|
||||
<template #extra>
|
||||
<j-switch
|
||||
v-model:checked='open'
|
||||
|
@ -11,15 +11,20 @@
|
|||
/>
|
||||
</template>
|
||||
</TitleComponent>
|
||||
<template v-if='open'>
|
||||
<!-- <template v-if='open'>-->
|
||||
<div>
|
||||
<j-tabs type="editable-card" v-model:activeKey="activeKey" @edit="addGroup" @tabClick="showEditCondition">
|
||||
<j-tab-pane
|
||||
v-for="(b, i) in group"
|
||||
:key="b.id"
|
||||
:tab="b.branchName || `条件${i+1}`"
|
||||
:closable="false"
|
||||
:forceRender="true"
|
||||
>
|
||||
<template #tab>
|
||||
<TermsTabPane :showClose="group.length > 1" @close="() => addGroup(b.id, 'close')">
|
||||
{{ b.branchName || `条件${i+1}` }}
|
||||
</TermsTabPane>
|
||||
</template>
|
||||
<template v-for='(item, index) in data.branches'>
|
||||
<template v-if="index >= b.start && index < (b.start + b.len)">
|
||||
<Branches
|
||||
|
@ -29,12 +34,12 @@
|
|||
:name='index'
|
||||
:branches_Index='item.branches_Index'
|
||||
:groupLen="b.start + b.len"
|
||||
:groupIndex="i"
|
||||
:groupIndex="i + 1"
|
||||
:key='item.key'
|
||||
:showGroupDelete="group.length !== 1"
|
||||
@delete='branchesDelete'
|
||||
@delete='branchesDelete(index)'
|
||||
@deleteAll='branchesDeleteAll'
|
||||
@deleteGroup="() => groupDelete(b, i)"
|
||||
@add="branchesAdd"
|
||||
/>
|
||||
<div v-else class='actions-terms-warp' :style='{ marginTop: data.branches.length === 2 ? 0 : 24 }'>
|
||||
<div class='actions-terms-title' style='padding: 0;margin-bottom: 24px;'>
|
||||
|
@ -50,27 +55,26 @@
|
|||
</j-tabs>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<div v-else class='actions-branches-item'>
|
||||
<j-form-item
|
||||
:name='["branches", 0, "then"]'
|
||||
:rules='thenRules'
|
||||
>
|
||||
<Action
|
||||
:name='0'
|
||||
:openShakeLimit="true"
|
||||
:thenOptions='data.branches[0]?.then'
|
||||
/>
|
||||
</j-form-item>
|
||||
</div>
|
||||
<!-- </template>-->
|
||||
<!-- <div v-else class='actions-branches-item'>-->
|
||||
<!-- <j-form-item-->
|
||||
<!-- :name='["branches", 0, "then"]'-->
|
||||
<!-- :rules='thenRules'-->
|
||||
<!-- >-->
|
||||
<!-- <Action-->
|
||||
<!-- :name='0'-->
|
||||
<!-- :openShakeLimit="true"-->
|
||||
<!-- :thenOptions='data.branches[0]?.then'-->
|
||||
<!-- />-->
|
||||
<!-- </j-form-item>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
<j-modal v-if="editConditionVisible" title="编辑" visible @cancel="editConditionVisible = false" @ok="changeBranchName">
|
||||
<j-form layout='vertical'>
|
||||
<j-form-item label="条件名称:" :required="true">
|
||||
<j-input v-model:value="conditionName"></j-input>
|
||||
</j-form-item>
|
||||
</j-form>
|
||||
</j-modal>
|
||||
<BranchesNameEdit
|
||||
v-if="editConditionVisible"
|
||||
:name="conditionName"
|
||||
@cancel="editConditionVisible = false"
|
||||
@ok="changeBranchName"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='Terms'>
|
||||
|
@ -78,12 +82,15 @@ import { storeToRefs } from 'pinia';
|
|||
import { useSceneStore } from '@/store/scene'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { provide } from 'vue'
|
||||
import { ContextKey, handleParamsData, thenRules } from './util'
|
||||
import { getParseTerm } from '@/api/rule-engine/scene'
|
||||
import type { FormModelType } from '@/views/rule-engine/Scene/typings'
|
||||
import { ContextKey, handleParamsData } from './util'
|
||||
import {getParseTerm} from '@/api/rule-engine/scene'
|
||||
import type { FormModelType} from '@/views/rule-engine/Scene/typings'
|
||||
import Branches from './Branches.vue'
|
||||
import Action from '../../action/index.vue'
|
||||
import {randomString} from "@/utils/utils";
|
||||
import {randomNumber, randomString} from "@/utils/utils";
|
||||
import TermsTabPane from './TermsTabPane.vue'
|
||||
import BranchesNameEdit from "./BranchesNameEdit.vue";
|
||||
import {Modal} from "ant-design-vue";
|
||||
import {queryBindScene, unBindAlarmMultiple} from "@/api/rule-engine/configuration";
|
||||
|
||||
const sceneStore = useSceneStore()
|
||||
const { data } = storeToRefs(sceneStore)
|
||||
|
@ -92,7 +99,7 @@ const columnOptions = ref<any>([])
|
|||
const group = ref<Array<{ id: string, len: number}>>([])
|
||||
const activeKey = ref('')
|
||||
const editConditionVisible = ref(false);
|
||||
const conditionName = ref<any>('')
|
||||
const conditionName = ref<any>()
|
||||
|
||||
provide(ContextKey, columnOptions)
|
||||
|
||||
|
@ -129,14 +136,14 @@ const queryColumn = (dataModel: FormModelType) => {
|
|||
const cloneDevice = cloneDeep(dataModel)
|
||||
cloneDevice.branches = cloneDevice.branches?.filter(item => !!item)
|
||||
getParseTerm(cloneDevice).then(res => {
|
||||
columnOptions.value = handleParamsData(res.result as any[])
|
||||
columnOptions.value = handleParamsData(res.result as any[], 'column', '0')
|
||||
})
|
||||
}
|
||||
|
||||
const addBranches = (len: number) => {
|
||||
const branchesItem = {
|
||||
when: [],
|
||||
key: `branches_${new Date().getTime()}`,
|
||||
key: randomNumber(),
|
||||
shakeLimit: {
|
||||
enabled: false,
|
||||
time: 1,
|
||||
|
@ -144,100 +151,225 @@ const addBranches = (len: number) => {
|
|||
alarmFirst: false,
|
||||
},
|
||||
then: [],
|
||||
branchId: Math.floor(Math.random() * 100000000)
|
||||
branchId: randomNumber()
|
||||
}
|
||||
// const lastIndex = data.value.branches!.length - 1 || 0
|
||||
data.value.branches?.splice(len - 1, 1, branchesItem)
|
||||
data.value.options!.when.splice(len - 1, 1, {
|
||||
terms: []
|
||||
})
|
||||
// data.value.options!.when = []
|
||||
}
|
||||
|
||||
const branchesDelete = (index: number) => {
|
||||
if (data.value.branches?.length === 2) {
|
||||
data.value.branches?.splice(index, 1, null as any)
|
||||
} else {
|
||||
data.value.branches?.splice(index, 1)
|
||||
}
|
||||
data.value.options?.when?.splice(index, 1)
|
||||
const branchesDelete = (index: any) => {
|
||||
groupDelete({
|
||||
start: index,
|
||||
len: 1
|
||||
}, -1)
|
||||
}
|
||||
|
||||
const addGroup = () => {
|
||||
const branchesItem: any = {
|
||||
when: [
|
||||
{
|
||||
terms: [
|
||||
{
|
||||
column: undefined,
|
||||
value: {
|
||||
source: 'fixed',
|
||||
value: undefined
|
||||
const addGroup = (targetKey: string, action: string) => {
|
||||
if (action === 'add') {
|
||||
const lastGroup = group.value[group.value.length - 1]
|
||||
const lastIndex = (lastGroup?.groupIndex || group.value.length) + 1
|
||||
const key = randomNumber()
|
||||
|
||||
const branchesItem: any = {
|
||||
when: [
|
||||
{
|
||||
terms: [
|
||||
{
|
||||
column: undefined,
|
||||
value: {
|
||||
source: 'fixed',
|
||||
value: undefined
|
||||
},
|
||||
termType: undefined,
|
||||
key: `params_${randomString()}`,
|
||||
type: 'and',
|
||||
},
|
||||
termType: undefined,
|
||||
key: `params_${randomString()}`,
|
||||
type: 'and',
|
||||
},
|
||||
],
|
||||
type: 'and',
|
||||
key: `terms_${randomString()}`,
|
||||
],
|
||||
type: 'and',
|
||||
key: `terms_${randomString()}`,
|
||||
},
|
||||
],
|
||||
key: key,
|
||||
shakeLimit: {
|
||||
enabled: false,
|
||||
time: 1,
|
||||
threshold: 1,
|
||||
alarmFirst: false,
|
||||
},
|
||||
],
|
||||
key: `branches_${randomString()}`,
|
||||
shakeLimit: {
|
||||
enabled: false,
|
||||
time: 1,
|
||||
threshold: 1,
|
||||
alarmFirst: false,
|
||||
},
|
||||
then: [],
|
||||
executeAnyway: true,
|
||||
branchId: Math.floor(Math.random() * 100000000),
|
||||
branchName:''
|
||||
then: [],
|
||||
executeAnyway: true,
|
||||
branchId: key,
|
||||
branchName: ''
|
||||
}
|
||||
data.value.branches?.push(branchesItem, null)
|
||||
// data.value.branches?.push(null as any)
|
||||
activeKey.value = key
|
||||
data.value.options!.when.push({
|
||||
terms: [{
|
||||
terms: [['','eq','','and']],
|
||||
}],
|
||||
branchName: '',
|
||||
key,
|
||||
executeAnyway: true,
|
||||
groupIndex: lastIndex
|
||||
})
|
||||
} else {
|
||||
const index = group.value.findIndex(item => item.branchId === targetKey)
|
||||
groupDelete(group.value[index], index)
|
||||
}
|
||||
data.value.branches?.push(branchesItem)
|
||||
data.value.branches?.push(null as any)
|
||||
activeKey.value = `group_${branchesItem.key}`
|
||||
data.value.options!.when.push({
|
||||
terms: [{
|
||||
terms: [['','eq','','and']],
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
const branchesDeleteAll = () => {
|
||||
|
||||
}
|
||||
|
||||
const groupDelete = (g: any, index: number) => {
|
||||
let _index = index - 1
|
||||
if (_index < 0) { // 左移
|
||||
_index = 0
|
||||
const groupDelete = async (g: any, index: number) => {
|
||||
|
||||
// 校验当前条件下是否有数据
|
||||
let actionLen = 0
|
||||
let alarmTerms: Array<Record<string, string>> = []
|
||||
|
||||
for (let i = g.start; i < g.start + g.len; i++) {
|
||||
const item = data.value.branches[i]
|
||||
if (item) {
|
||||
item.then?.forEach(thenItem => {
|
||||
actionLen += thenItem.actions.length
|
||||
if (thenItem.actions) {
|
||||
thenItem.actions.forEach((actionItem) => {
|
||||
const _actionId = actionItem.actionId
|
||||
if (actionItem.executor === 'alarm') {
|
||||
alarmTerms.push({
|
||||
column: 'branchIndex',
|
||||
value: _actionId || item.branchId,
|
||||
type: 'or'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
group.value.splice(index, 1)
|
||||
data.value.branches.splice(g.start, g.len)
|
||||
data.value.options!.when.splice(g.start, g.len)
|
||||
activeKey.value = group.value[_index].id
|
||||
|
||||
if (actionLen) {
|
||||
if (alarmTerms.length) {
|
||||
const resp = await queryBindScene({
|
||||
terms: alarmTerms
|
||||
})
|
||||
|
||||
Modal.confirm({
|
||||
title: `已关联 ${resp.result.total} 条告警,删除该条件会同步解除对应的关联告警,确认删除?`,
|
||||
onOk() {
|
||||
const _data = resp.result.data.map(item => {
|
||||
return {
|
||||
"alarmId": item.alarmId,
|
||||
"ruleId": item.ruleId,
|
||||
"branchIndex": item.branchIndex
|
||||
}
|
||||
})
|
||||
unBindAlarmMultiple(_data)
|
||||
removeBranchesData(g, index)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Modal.confirm({
|
||||
title: '该条件下有执行动作,确认删除?',
|
||||
onOk() {
|
||||
removeBranchesData(g, index)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
removeBranchesData(g, index)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const removeBranchesData = (g: any, index: number) => {
|
||||
const removeBranches = data.value.branches.splice(g.start, g.len)
|
||||
|
||||
removeBranches.forEach(item => {
|
||||
if (item) {
|
||||
let _index = data.value.options!.when.findIndex(whenItem => whenItem.key === item.branchId)
|
||||
if (_index !== -1) {
|
||||
_index = item.branches_Index
|
||||
}
|
||||
data.value.options!.when.splice(_index, 1)
|
||||
}
|
||||
})
|
||||
|
||||
if (index >= 0) { // 删除整个条件组
|
||||
group.value.splice(index, 1)
|
||||
|
||||
if (g.id === activeKey.value) { //
|
||||
let _moveIndex = index - 1
|
||||
|
||||
if (_moveIndex < 0) { // 左移
|
||||
_moveIndex = 0
|
||||
}
|
||||
|
||||
activeKey.value = group.value[_moveIndex].id
|
||||
}
|
||||
} else { // 单个条件删除
|
||||
const groupItem = group.value.find(item => item.id === activeKey.value) // 获取当前条件组
|
||||
groupItem!.len -= 1
|
||||
const branchesItem = data.value.branches[g.start]
|
||||
if (branchesItem === undefined || branchesItem?.executeAnyway) { // 当前位置为undefined或者是下一个条件组的开始 就插入null
|
||||
data.value.branches?.splice(g.start, 0, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const branchesAdd = () => {
|
||||
// const groupItem = group.value.find(item => item.id === activeKey.value) // 获取当前条件的组
|
||||
// groupItem!.len += 1
|
||||
}
|
||||
|
||||
const showEditCondition = (key:any) =>{
|
||||
if(key === activeKey.value){
|
||||
editConditionVisible.value = true;
|
||||
conditionName.value = group.value.find((i:any)=>{
|
||||
return i.id === key
|
||||
return i.branchId === key
|
||||
})?.branchName
|
||||
}
|
||||
}
|
||||
|
||||
const changeBranchName = () =>{
|
||||
console.log(data.value)
|
||||
const changeBranchName = (name: string) =>{
|
||||
let _activeKey = activeKey.value
|
||||
|
||||
data.value.branches?.forEach((item:any)=>{
|
||||
if(item?.key === activeKey.value.slice(6)){
|
||||
item.branchName = conditionName.value
|
||||
if(item?.branchId === _activeKey){
|
||||
item.branchName = name
|
||||
}
|
||||
})
|
||||
|
||||
let optionsItem = data.value.options!.when.find(item => item.key === _activeKey)
|
||||
|
||||
if (!optionsItem) {
|
||||
const _index = group.value.findIndex(item => item.branchId === _activeKey)
|
||||
if (_index !== -1) {
|
||||
data.value.options!.when[_index].branchName = name
|
||||
}
|
||||
} else {
|
||||
optionsItem.branchName = name
|
||||
}
|
||||
|
||||
editConditionVisible.value =false
|
||||
}
|
||||
|
||||
const changePaneIndex = (index) => {
|
||||
const _groupItem = group.value.find(item => {
|
||||
return item.start >= index && index < (item.start + item.len)
|
||||
})
|
||||
|
||||
if (_groupItem) {
|
||||
activeKey.value = _groupItem.branchId
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
if (data.value.trigger?.device) {
|
||||
queryColumn({ trigger: data.value.trigger })
|
||||
|
@ -246,7 +378,6 @@ watchEffect(() => {
|
|||
|
||||
watchEffect(() => {
|
||||
const branches = data.value.branches
|
||||
|
||||
if (data.value.branches?.filter(item => item).length) {
|
||||
open.value = !!data.value.branches[0].when.length
|
||||
} else {
|
||||
|
@ -254,33 +385,43 @@ watchEffect(() => {
|
|||
}
|
||||
|
||||
let _group = []
|
||||
let _branchesIndex = 0
|
||||
if (branches) {
|
||||
branches.forEach((item, index) => {
|
||||
// if (index === 0) {
|
||||
// _group.push({
|
||||
// id: `group_${item.key}`,
|
||||
// len: 0,
|
||||
// start: 0,
|
||||
// })
|
||||
// }
|
||||
|
||||
const lastIndex = _group.length - 1
|
||||
|
||||
let whenItem = data.value.options!.when.find(when => item?.branchId === when.key)
|
||||
|
||||
if (!whenItem) {
|
||||
whenItem = data.value.options!.when[_branchesIndex]
|
||||
}
|
||||
|
||||
|
||||
if (index === 0 || item?.executeAnyway) {
|
||||
_group[lastIndex + 1] = {
|
||||
id: `group_${item.key}`,
|
||||
id: item.branchId,
|
||||
len: 1,
|
||||
start: index,
|
||||
branchName:item.branchName
|
||||
branchKey: item.key,
|
||||
branchId: item.branchId,
|
||||
// branchName: item.branchName || whenItem?.branchName || `条件 ${_branchesIndex + 1}`,
|
||||
branchName: item.branchName || whenItem?.branchName || `条件`,
|
||||
groupIndex: _branchesIndex
|
||||
}
|
||||
} else {
|
||||
_group[lastIndex].len += 1
|
||||
}
|
||||
|
||||
if (item) {
|
||||
item.branches_Index = _branchesIndex
|
||||
_branchesIndex += 1
|
||||
}
|
||||
})
|
||||
|
||||
branches.filter(item => item).forEach((item, index) => {
|
||||
item.branches_Index = index
|
||||
})
|
||||
// branches.filter(item => item).forEach((item, index) => {
|
||||
// item.branches_Index = index
|
||||
// })
|
||||
|
||||
group.value = _group
|
||||
if (!activeKey.value) {
|
||||
|
@ -289,8 +430,18 @@ watchEffect(() => {
|
|||
}
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
changePaneIndex
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang='less'>
|
||||
|
||||
.actions-terms {
|
||||
:deep(.ant-tabs-tab-active) {
|
||||
.ant-tabs-tab-remove {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
margin-bottom: 8px;
|
||||
"
|
||||
>
|
||||
{{ slotProps.name }}
|
||||
{{ slotProps.name }}
|
||||
</Ellipsis>
|
||||
<j-row>
|
||||
<j-col :span="12">
|
||||
|
@ -260,7 +260,6 @@ const columns = [
|
|||
ellipsis: true,
|
||||
search: {
|
||||
type: 'select',
|
||||
options: typeOptions,
|
||||
options: () =>
|
||||
new Promise((resolve) => {
|
||||
queryType().then((resp: any) => {
|
||||
|
@ -320,8 +319,8 @@ const tableRef = ref();
|
|||
const current = ref<any>({});
|
||||
const table = {
|
||||
refresh: () => {
|
||||
// tableRef.value.reload(queryParams.value);
|
||||
window.location.reload();
|
||||
tableRef.value.reload(queryParams.value);
|
||||
// window.location.reload();
|
||||
},
|
||||
toAdd: () => {
|
||||
visible.value = true;
|
||||
|
@ -332,20 +331,24 @@ const table = {
|
|||
},
|
||||
changeStatus: (row: any) => {
|
||||
const state = row.state.value === 'enabled' ? 'disabled' : 'enabled';
|
||||
changeApplyStatus_api(row.id, { state }).then((resp: any) => {
|
||||
const response = changeApplyStatus_api(row.id, { state });
|
||||
response.then((resp: any) => {
|
||||
if (resp.status === 200) {
|
||||
onlyMessage('操作成功');
|
||||
table.refresh();
|
||||
}
|
||||
});
|
||||
return response;
|
||||
},
|
||||
clickDel: (row: any) => {
|
||||
delApply_api(row.id).then((resp: any) => {
|
||||
const response = delApply_api(row.id);
|
||||
response.then((resp: any) => {
|
||||
if (resp.status === 200) {
|
||||
onlyMessage('操作成功');
|
||||
table.refresh();
|
||||
}
|
||||
});
|
||||
return response;
|
||||
},
|
||||
getActions: (
|
||||
data: Partial<Record<string, any>>,
|
||||
|
@ -475,6 +478,17 @@ const table = {
|
|||
const dialogVisible = ref(false);
|
||||
const selectId = ref<string>('');
|
||||
const selectProvider = ref<any>('');
|
||||
onMounted(() => {
|
||||
queryType().then((resp: any) => {
|
||||
if (resp.status === 200) {
|
||||
const arr = resp.result.map((item: any) => ({
|
||||
label: item.name,
|
||||
value: item.provider,
|
||||
}));
|
||||
typeOptions.value = arr;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -85,6 +85,8 @@ import {
|
|||
import { protocolList } from '@/utils/consts';
|
||||
import { getProviders } from '@/api/data-collect/channel';
|
||||
import { isNoCommunity } from '@/utils/utils';
|
||||
import { USER_CENTER_MENU_DATA } from '@/views/init-home/data/baseMenu'
|
||||
|
||||
const selectedKeys: any = ref([]);
|
||||
const treeData = ref<any>([]);
|
||||
const systemMenu: any = ref([]);
|
||||
|
@ -195,6 +197,7 @@ const handleOk = async () => {
|
|||
const _dataArr = dealTree(cloneDeep(treeData.value),selectedKeys.value)
|
||||
const _dataSorts = handleSorts(_dataArr)
|
||||
loading.value = true;
|
||||
_dataSorts.push(USER_CENTER_MENU_DATA)
|
||||
const res = await updateMenus(_dataSorts).catch(() => {});
|
||||
if (res?.status === 200) {
|
||||
onlyMessage('操作成功', 'success');
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
draggable
|
||||
block-node
|
||||
v-model:expandedKeys="expandedKeys"
|
||||
v-model:selectedKeys="selectedKeys"
|
||||
:selectedKeys="selectedKeys"
|
||||
:tree-data="_treeData"
|
||||
:show-line="{ showLeafIcon: false }"
|
||||
:show-icon="true"
|
||||
|
@ -62,18 +62,18 @@
|
|||
</j-button>
|
||||
</j-tooltip>
|
||||
<j-tooltip title="删除">
|
||||
<j-popconfirm
|
||||
@confirm="onRemove(_data?.id)"
|
||||
<PermissionButton
|
||||
type="link"
|
||||
style="margin: 0; padding: 0"
|
||||
danger
|
||||
:popConfirm="{
|
||||
title: '确认删除?',
|
||||
onConfirm: () =>
|
||||
onRemove(_data?.id),
|
||||
}"
|
||||
>
|
||||
<j-button
|
||||
@click.stop
|
||||
class="actions-btn"
|
||||
type="link"
|
||||
danger
|
||||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</j-button>
|
||||
</j-popconfirm>
|
||||
<AIcon type="DeleteOutlined"
|
||||
/></PermissionButton>
|
||||
</j-tooltip>
|
||||
</j-space>
|
||||
</div>
|
||||
|
@ -87,8 +87,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<Save
|
||||
:mode="mode"
|
||||
v-if="visible"
|
||||
:mode="mode"
|
||||
:data="current"
|
||||
:treeData="_treeData"
|
||||
:areaTree="areaTree"
|
||||
|
@ -100,11 +100,18 @@
|
|||
import { cloneDeep, debounce } from 'lodash-es';
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import Save from '../Save/index.vue';
|
||||
import { getRegionTree, delRegion } from '@/api/system/region';
|
||||
import { useArea } from '../hooks';
|
||||
import {
|
||||
getRegionTree,
|
||||
delRegion,
|
||||
updateRegion,
|
||||
saveRegion,
|
||||
} from '@/api/system/region';
|
||||
import { useArea, useRegion } from '../hooks';
|
||||
import ResizeObserver from 'ant-design-vue/lib/vc-resize-observer';
|
||||
import { onlyMessage } from '@/utils/comm';
|
||||
import { title } from 'process';
|
||||
|
||||
const regionState = useRegion();
|
||||
const treeData = ref<any[]>([]);
|
||||
const _treeData = ref<any[]>([]);
|
||||
const visible = ref<boolean>(false);
|
||||
|
@ -119,7 +126,7 @@ const type = ref<string | undefined>(undefined);
|
|||
|
||||
const { areaTree } = useArea();
|
||||
|
||||
const emit = defineEmits(['select']);
|
||||
const emit = defineEmits(['select', 'close']);
|
||||
|
||||
const filterTreeNodes = (tree: any[], condition: string) => {
|
||||
return tree.filter((item) => {
|
||||
|
@ -169,6 +176,7 @@ const onSave = () => {
|
|||
|
||||
const onClose = () => {
|
||||
visible.value = false;
|
||||
emit('close');
|
||||
};
|
||||
|
||||
const divResize = ({ height }) => {
|
||||
|
@ -181,14 +189,19 @@ const onEdit = (_data: any) => {
|
|||
mode.value = 'edit';
|
||||
current.value = _data;
|
||||
visible.value = true;
|
||||
selectedKeys.value = [_data.id];
|
||||
emit('select', _data?.code, _data);
|
||||
};
|
||||
|
||||
const onRemove = async (id: string) => {
|
||||
const resp = await delRegion(id);
|
||||
if (resp.success) {
|
||||
onlyMessage('操作成功!');
|
||||
handleSearch();
|
||||
}
|
||||
const onRemove = (id: string) => {
|
||||
const response = delRegion(id);
|
||||
response.then((resp) => {
|
||||
if (resp.success) {
|
||||
onlyMessage('操作成功!');
|
||||
handleSearch();
|
||||
}
|
||||
});
|
||||
return response
|
||||
};
|
||||
|
||||
const onAdd = (_data?: any) => {
|
||||
|
@ -217,13 +230,19 @@ const onDrop = (info: any) => {
|
|||
const dropPos = info.node.pos.split('-');
|
||||
const dropPosition =
|
||||
info.dropPosition - Number(dropPos[dropPos.length - 1]);
|
||||
const loop = (data: any, key: string | number, callback: any) => {
|
||||
|
||||
const loop = (
|
||||
data: any,
|
||||
key: string | number,
|
||||
callback: any,
|
||||
parent?: any,
|
||||
) => {
|
||||
data.forEach((item: any, index: number) => {
|
||||
if (item.key === key) {
|
||||
return callback(item, index, data);
|
||||
if (item.id === key) {
|
||||
return callback(item, index, data, parent);
|
||||
}
|
||||
if (item.children) {
|
||||
return loop(item.children, key, callback);
|
||||
return loop(item.children, key, callback, item);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -234,36 +253,67 @@ const onDrop = (info: any) => {
|
|||
arr.splice(index, 1);
|
||||
dragObj = item;
|
||||
});
|
||||
|
||||
if (!info.dropToGap) {
|
||||
// Drop on the content
|
||||
loop(data, dropKey, (item: any) => {
|
||||
item.children = item.children || [];
|
||||
/// where to insert 示例添加到头部,可以是随意位置
|
||||
dragObj.parentId = item.id;
|
||||
item.children.unshift(dragObj);
|
||||
item.children = item.children.map((cl: any, clIndex: number) => {
|
||||
cl.sortIndex = clIndex + 1;
|
||||
return cl;
|
||||
});
|
||||
updateRegion(dragObj);
|
||||
});
|
||||
} else if (
|
||||
(info.node.children || []).length > 0 && // Has children
|
||||
info.node.expanded && // Is expanded
|
||||
dropPosition === 1 // On the bottom gap
|
||||
) {
|
||||
loop(data, dropKey, (item: any) => {
|
||||
item.children = item.children || [];
|
||||
// where to insert 示例添加到头部,可以是随意位置
|
||||
item.children.unshift(dragObj);
|
||||
});
|
||||
loop(
|
||||
data,
|
||||
dropKey,
|
||||
(item: any, index: number, _data: any[], parent: any) => {
|
||||
item.children = item.children || [];
|
||||
// where to insert 示例添加到头部,可以是随意位置
|
||||
dragObj.parentId = item.parentId;
|
||||
item.children = item.children.map(
|
||||
(cl: any, clIndex: number) => {
|
||||
cl.sortIndex = clIndex + 1;
|
||||
return cl;
|
||||
},
|
||||
);
|
||||
|
||||
_data.splice(index + 1, 0, dragObj);
|
||||
// 获取item的父级,将dragObj放入同级
|
||||
updateRegion(item);
|
||||
updateRegion(dragObj);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
let ar: any[] = [];
|
||||
let i = 0;
|
||||
loop(data, dropKey, (_item: any, index: number, arr: any[]) => {
|
||||
ar = arr;
|
||||
i = index;
|
||||
});
|
||||
if (dropPosition === -1) {
|
||||
ar.splice(i, 0, dragObj);
|
||||
} else {
|
||||
ar.splice(i + 1, 0, dragObj);
|
||||
}
|
||||
loop(
|
||||
data,
|
||||
dropKey,
|
||||
(_item: any, index: number, arr: any[], parent: any) => {
|
||||
dragObj.parentId = parent ? parent.id : '';
|
||||
dragObj.sortIndex = dropPosition === -1 ? index : index + 1;
|
||||
arr.splice(dragObj.sortIndex, 0, dragObj);
|
||||
const sortArray = arr.map((cl: any, clIndex: number) => {
|
||||
cl.sortIndex = clIndex + 1;
|
||||
return cl;
|
||||
});
|
||||
if (parent) {
|
||||
parent.children = sortArray;
|
||||
updateRegion(parent);
|
||||
} else {
|
||||
updateRegion(arr);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
treeData.value = data;
|
||||
};
|
||||
|
||||
|
@ -286,8 +336,9 @@ watch(
|
|||
* 区域选择
|
||||
*/
|
||||
const areaSelect = (key, { node }) => {
|
||||
if (!key.length) return;
|
||||
selectedKeys.value = key;
|
||||
emit('select', node?.code);
|
||||
emit('select', node?.code, node);
|
||||
};
|
||||
|
||||
const handleSearch = async () => {
|
||||
|
@ -301,11 +352,24 @@ const handleSearch = async () => {
|
|||
const dt = treeData.value?.[0];
|
||||
if (dt) {
|
||||
selectedKeys.value = dt?.id ? [dt?.id] : [];
|
||||
emit('select', dt?.code);
|
||||
emit('select', dt?.code, dt);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const openSave = (geoJson: Record<string, any>) => {
|
||||
if (geoJson) {
|
||||
regionState.saveCache.geoJson = geoJson;
|
||||
}
|
||||
current.value = regionState.saveCache;
|
||||
visible.value = true;
|
||||
regionState.treeMask = false;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
openSave: openSave,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
handleSearch();
|
||||
});
|
||||
|
@ -318,9 +382,8 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
.tree-content {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
height: 0;
|
||||
flex: 1 1 0;
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
|
||||
.tree-empty {
|
||||
|
|
|
@ -1,91 +1,372 @@
|
|||
<template>
|
||||
<div class="region-map">
|
||||
<AMapComponent @init="initMap"/>
|
||||
<div class="region-map-loading" v-if="_loading || loading">
|
||||
<a-spin :spinning="_loading || loading"></a-spin>
|
||||
<AMapComponent
|
||||
ref="mapRef"
|
||||
>
|
||||
<el-amap-polygon
|
||||
v-if="showPolygon && pathData?.length"
|
||||
:path="pathData"
|
||||
:editable="isEdit"
|
||||
:key="layerId"
|
||||
@dragend="dragend"
|
||||
@adjust="dragend"
|
||||
@removenode="dragend"
|
||||
@addnode="dragend"
|
||||
@init="polygonInit"
|
||||
/>
|
||||
<el-amap-circle
|
||||
v-if="showCircle"
|
||||
:radius="pathData.radius"
|
||||
:center="pathData.center"
|
||||
:editable="isEdit"
|
||||
@dragend="dragend"
|
||||
/>
|
||||
<el-amap-rectangle
|
||||
v-if="showRectangle && pathData?.length"
|
||||
:bounds="pathData"
|
||||
:editable="isEdit"
|
||||
:key="layerId"
|
||||
@dragend="dragend"
|
||||
@adjust="dragend"
|
||||
@removenode="dragend"
|
||||
@addnode="dragend"
|
||||
@init="polygonInit"
|
||||
/>
|
||||
<DistrictSearch
|
||||
v-if="showDistrict"
|
||||
:adcode="adbode"
|
||||
:styles="{
|
||||
'stroke-width': 2,
|
||||
'fill': 'rgba(0,176,255, 0.2)'
|
||||
}"
|
||||
/>
|
||||
<GeoJson
|
||||
v-if="showGeoJson"
|
||||
:geo="pathData"
|
||||
/>
|
||||
<el-amap-mouse-tool
|
||||
v-if="showTool"
|
||||
:type="toolType"
|
||||
@draw="toolDraw"
|
||||
/>
|
||||
</AMapComponent>
|
||||
<div class="map-tool" v-if="showToolDom">
|
||||
<div class="map-tool-content">
|
||||
<div class="tool-item-group">
|
||||
<div class="tool-item" @click="toolSave">
|
||||
<j-tooltip title="保存描点" >
|
||||
<AIcon type="SaveOutlined" />
|
||||
</j-tooltip>
|
||||
</div>
|
||||
<div class="tool-item" @click="toolClose">
|
||||
<j-tooltip title="取消操作" >
|
||||
<AIcon type="CloseOutlined" />
|
||||
</j-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tool-item-group">
|
||||
<div :class="{'tool-item': true, 'active': toolType === 'rectangle'}" @click="changeToolType(MAP_TOOL.rectangle)">
|
||||
<j-tooltip title="矩形" >
|
||||
<AIcon type="icon-huajuxing" />
|
||||
</j-tooltip>
|
||||
</div>
|
||||
<div :class="{'tool-item': true, 'active': toolType === 'polygon'}" @click="changeToolType(MAP_TOOL.polygon)">
|
||||
<j-tooltip title="多边形" >
|
||||
<AIcon type="icon-huaduobianxing" />
|
||||
</j-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tool-item-group">
|
||||
<div :class="{'tool-item': true, 'disabled': !hasHistory }" @click="onRevoke">
|
||||
<j-tooltip title="撤销" >
|
||||
<AIcon type="RollbackOutlined" />
|
||||
</j-tooltip>
|
||||
</div>
|
||||
<div class="tool-item" @click="onDelete">
|
||||
<j-tooltip title="删除">
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</j-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script name="RegionMap" setup>
|
||||
const props = defineProps({
|
||||
selectCode: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
import {useHistory, useRegion} from "../hooks";
|
||||
import { MAP_TOOL } from '../util'
|
||||
import { DistrictSearch, GeoJson } from '@/components/AMapComponent'
|
||||
import { randomNumber } from '@/utils/utils'
|
||||
import {onlyMessage} from "@/utils/comm";
|
||||
|
||||
const regionState = useRegion()
|
||||
const { revoke, addRecord, reset, hasHistory } = useHistory()
|
||||
|
||||
const toolType = ref()
|
||||
const showTool = ref(false)
|
||||
const showToolDom = ref(false)
|
||||
const adbode = ref()
|
||||
const pathData = ref()
|
||||
const isEdit = ref(false)
|
||||
const layerId = ref('layer')
|
||||
const mapRef = ref()
|
||||
|
||||
const toolDrawCache = ref()
|
||||
|
||||
const showPolygon = computed(() => {
|
||||
return regionState.type === MAP_TOOL.polygon
|
||||
})
|
||||
|
||||
const MapRef = ref()
|
||||
const loading = ref(true)
|
||||
const _loading = ref(true)
|
||||
let polygon = null
|
||||
let district = null
|
||||
const initMap = (e) => {
|
||||
loading.value = true
|
||||
MapRef.value = e
|
||||
loading.value = false
|
||||
const showRectangle = computed(() => {
|
||||
return regionState.type === MAP_TOOL.rectangle
|
||||
})
|
||||
|
||||
const showCircle = computed(() => {
|
||||
return regionState.type === MAP_TOOL.circle
|
||||
})
|
||||
|
||||
const showDistrict = computed(() => {
|
||||
return regionState.type === MAP_TOOL.district
|
||||
})
|
||||
|
||||
const showGeoJson = computed(() => {
|
||||
return regionState.type === MAP_TOOL.geoJson
|
||||
})
|
||||
|
||||
const toolDraw = (e) => {
|
||||
|
||||
regionState.type = toolType.value
|
||||
isEdit.value = true
|
||||
pathData.value = e
|
||||
|
||||
handleGeoJson(toolType.value, e)
|
||||
|
||||
showTool.value = false
|
||||
toolType.value = undefined
|
||||
|
||||
if (!hasHistory.value) {
|
||||
addRecord(null)
|
||||
}
|
||||
addRecord({
|
||||
isEdit: isEdit.value,
|
||||
pathData: e,
|
||||
toolType: toolType.value,
|
||||
id: randomNumber()
|
||||
})
|
||||
}
|
||||
|
||||
const queryBounds = (code) => {
|
||||
_loading.value = true
|
||||
if (!district) {
|
||||
//实例化DistrictSearch
|
||||
const opts = {
|
||||
subdistrict: 0,
|
||||
extensions: 'all',
|
||||
level: 'district'
|
||||
};
|
||||
district = new AMap.DistrictSearch(opts);
|
||||
|
||||
const dragend = (e) => {
|
||||
let paths = []
|
||||
if (e.getPath) {
|
||||
paths = e.getPath()
|
||||
} else if(e.target?.getPath){
|
||||
paths = e.target.getPath()
|
||||
} if (e.bounds) {
|
||||
const { northEast, southWest} = e.bounds
|
||||
paths = [
|
||||
[northEast.lng, northEast.lat],
|
||||
[southWest.lng, southWest.lat],
|
||||
]
|
||||
}
|
||||
district.search(code, function (status, result) {
|
||||
if (polygon) {
|
||||
MapRef.value.remove(polygon)// 清除上次结果
|
||||
polygon = null;
|
||||
}
|
||||
const bounds = result?.districtList?.[0]?.boundaries;
|
||||
if (bounds) {
|
||||
//生成行政区划polygon
|
||||
for (let i = 0; i < bounds.length; i += 1) {// 构造MultiPolygon的path
|
||||
bounds[i] = [bounds[i]]
|
||||
|
||||
|
||||
addRecord({
|
||||
isEdit: isEdit.value,
|
||||
pathData: paths,
|
||||
toolType: regionState.type,
|
||||
id: randomNumber()
|
||||
})
|
||||
handleGeoJson(regionState.type, paths)
|
||||
}
|
||||
|
||||
|
||||
const handleGeoJson = (type, data) => {
|
||||
toolDrawCache.value = {
|
||||
type: 'Feature',
|
||||
toolType: type,
|
||||
features: [{
|
||||
type: 'FeatureCollection',
|
||||
properties: {
|
||||
type: type
|
||||
},
|
||||
geometry: {
|
||||
type:"Polygon",
|
||||
coordinates: type === MAP_TOOL.polygon ? [data] : data
|
||||
}
|
||||
polygon = new AMap.Polygon({
|
||||
strokeWeight: 1,
|
||||
path: bounds,
|
||||
fillOpacity: 0.4,
|
||||
fillColor: '#80d8ff',
|
||||
strokeColor: '#0091ea'
|
||||
});
|
||||
MapRef.value.add(polygon)
|
||||
MapRef.value.setFitView(polygon);// 视口自适应
|
||||
}
|
||||
_loading.value = false
|
||||
});
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => [props.selectCode, loading.value], () => {
|
||||
if (props.selectCode && !loading.value) {
|
||||
queryBounds(String(props.selectCode).padEnd(6, '0'))
|
||||
const changeToolType = (type) => {
|
||||
showTool.value = true
|
||||
toolType.value = type
|
||||
isEdit.value = false
|
||||
regionState.type = undefined
|
||||
}
|
||||
|
||||
const showToolFn = (geoJson) => {
|
||||
toolDrawCache.value = geoJson
|
||||
showToolDom.value = true
|
||||
}
|
||||
|
||||
const toolSave = () => {
|
||||
if (toolDrawCache.value) {
|
||||
regionState.openSave(toolDrawCache.value)
|
||||
} else {
|
||||
onlyMessage('请绘制区域范围','warning')
|
||||
}
|
||||
}, {immediate: true})
|
||||
}
|
||||
|
||||
const toolClose = () => { // 取消操作
|
||||
showTool.value = false
|
||||
toolType.value = undefined
|
||||
regionState.openSave()
|
||||
}
|
||||
|
||||
const showDistrictFn = (code) => {
|
||||
adbode.value = code.toString().padEnd(6, '0')
|
||||
}
|
||||
|
||||
const showGeoJsonFn = (geoJson) => {
|
||||
pathData.value = geoJson
|
||||
|
||||
addRecord({
|
||||
isEdit: true,
|
||||
pathData: geoJson,
|
||||
toolType: regionState.type,
|
||||
id: randomNumber()
|
||||
})
|
||||
}
|
||||
|
||||
const openEdit = () => {
|
||||
isEdit.value = true
|
||||
layerId.value = randomNumber()
|
||||
}
|
||||
|
||||
const onDelete = () => {
|
||||
isEdit.value = false
|
||||
regionState.type = undefined
|
||||
toolType.value = undefined
|
||||
pathData.value = undefined
|
||||
toolDrawCache.value = undefined
|
||||
|
||||
addRecord({
|
||||
isEdit: false,
|
||||
pathData: [],
|
||||
toolType: '',
|
||||
id: randomNumber()
|
||||
})
|
||||
}
|
||||
|
||||
const readOnly = () => {
|
||||
isEdit.value = false
|
||||
showToolDom.value = false
|
||||
}
|
||||
|
||||
const onRevoke = () => {
|
||||
const item = revoke()
|
||||
|
||||
isEdit.value = item?.isEdit || false
|
||||
pathData.value = item?.pathData || []
|
||||
regionState.type = item?.toolType
|
||||
layerId.value = item?.id || randomNumber()
|
||||
}
|
||||
|
||||
const initState = () => {
|
||||
isEdit.value = false
|
||||
showTool.value = false
|
||||
showToolDom.value = false
|
||||
adbode.value = undefined
|
||||
toolType.value = undefined
|
||||
pathData.value = undefined
|
||||
}
|
||||
|
||||
const polygonInit = (e) => {
|
||||
const bounds = e.getBounds()
|
||||
mapRef.value?.setBounds(bounds)
|
||||
}
|
||||
|
||||
const init = () => {
|
||||
initState()
|
||||
reset()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
showTool: showToolFn,
|
||||
showDistrict: showDistrictFn,
|
||||
showGeoJson: showGeoJsonFn,
|
||||
openEdit: openEdit,
|
||||
init: init,
|
||||
readOnly: readOnly,
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.region-map {
|
||||
position: relative;
|
||||
//height: 800px;
|
||||
height: 100%;
|
||||
|
||||
.region-map-loading {
|
||||
.map-tool{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
background-color: yellow;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: rgba(#000, 0.35);
|
||||
top: 20%;
|
||||
right: 20px;
|
||||
z-index: 3;
|
||||
|
||||
|
||||
.map-tool-content {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
flex-direction: column;
|
||||
|
||||
.tool-item-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid #e3e3e3;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 16px rgba(#000, .15);
|
||||
|
||||
.tool-item {
|
||||
padding: 4px 6px;
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
|
||||
&:first-child {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
&:not(:first-child) {
|
||||
border-top: 1px solid #e3e3e3;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: var(--ant-primary-color);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: not-allowed !important;
|
||||
background-color: #efefef;
|
||||
|
||||
> span {
|
||||
cursor: not-allowed !important;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
</template>
|
||||
</j-tree-select>
|
||||
<j-checkbox
|
||||
v-model:checked="_checked"
|
||||
v-model:checked="mySync"
|
||||
@change="onCheckChange"
|
||||
style="margin-top: 5px"
|
||||
>同步添加下一级区域</j-checkbox
|
||||
|
@ -43,13 +43,17 @@ const props = defineProps({
|
|||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
sync: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:value', 'update:name', 'update:children']);
|
||||
const emits = defineEmits(['update:value', 'update:name', 'update:children', 'update:sync']);
|
||||
|
||||
const features = ref<any>({});
|
||||
const _value = ref<string>();
|
||||
const _checked = ref<boolean>(props.children?.length ?? false);
|
||||
const mySync = ref<boolean>(props.sync);
|
||||
|
||||
|
||||
const findChildren = (data: any, code: string) => {
|
||||
|
@ -73,18 +77,18 @@ const findChildren = (data: any, code: string) => {
|
|||
}
|
||||
|
||||
const onCheckChange = (e: any) => {
|
||||
console.log('e',props.children, e.target.checked)
|
||||
if (e.target.checked) {
|
||||
const children = features.value?.children ? features.value?.children : findChildren(props.areaTree, _value.value)
|
||||
emits('update:children', children.map((item, index) => {
|
||||
if (!item.sortIndex) {
|
||||
item.sortIndex = index + 1
|
||||
}
|
||||
return item
|
||||
}));
|
||||
} else {
|
||||
emits('update:children', []);
|
||||
}
|
||||
// if (e.target.checked) {
|
||||
// const children = features.value?.children ? features.value?.children : findChildren(props.areaTree, _value.value)
|
||||
// emits('update:children', children.map((item, index) => {
|
||||
// if (!item.sortIndex) {
|
||||
// item.sortIndex = index + 1
|
||||
// }
|
||||
// return item
|
||||
// }));
|
||||
// } else {
|
||||
// emits('update:children', []);
|
||||
// }
|
||||
emits('update:sync', e.target.checked)
|
||||
};
|
||||
|
||||
const getObj = (node: any): any => {
|
||||
|
@ -104,19 +108,19 @@ const getObj = (node: any): any => {
|
|||
};
|
||||
|
||||
const onSelect = (val: string, node: any) => {
|
||||
features.value = getObj(node);
|
||||
// features.value = getObj(node);
|
||||
_value.value = val;
|
||||
|
||||
emits('update:name', features.value?.name);
|
||||
emits('update:value', features.value?.code);
|
||||
emits('update:name', node.name);
|
||||
emits('update:value', node.code);
|
||||
|
||||
if (_checked.value) {
|
||||
emits('update:children', node?.children.map(item => ({
|
||||
code: item.code,
|
||||
name: item.name,
|
||||
parentId: item.parentId,
|
||||
})));
|
||||
}
|
||||
// if (mySync.value) {
|
||||
// emits('update:children', node?.children.map(item => ({
|
||||
// code: item.code,
|
||||
// name: item.name,
|
||||
// parentId: item.parentId,
|
||||
// })));
|
||||
// }
|
||||
};
|
||||
|
||||
|
||||
|
@ -127,7 +131,7 @@ watch(
|
|||
_value.value = props.value
|
||||
} else {
|
||||
emits('update:name', '中国');
|
||||
emits('update:value', 100000);
|
||||
emits('update:value', '100000');
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -135,4 +139,8 @@ watch(
|
|||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
watch(() => props.sync, () => {
|
||||
mySync.value = props.sync
|
||||
}, { immediate: true})
|
||||
</script>
|
||||
|
|
|
@ -1,40 +1,59 @@
|
|||
<template>
|
||||
<j-modal
|
||||
:maskClosable="false"
|
||||
width="650px"
|
||||
:visible="true"
|
||||
:title="mode === 'edit' ? '编辑区域' : '新增区域'"
|
||||
:confirmLoading="loading"
|
||||
@ok="handleSave"
|
||||
@cancel="() => { handleCancel() }"
|
||||
:maskClosable="false"
|
||||
width="650px"
|
||||
:visible="true"
|
||||
:title="mode === 'edit' ? '编辑区域' : '新增区域'"
|
||||
:confirmLoading="loading"
|
||||
@ok="handleSave"
|
||||
@cancel="() => { handleCancel() }"
|
||||
>
|
||||
<div style="margin-top: 10px">
|
||||
<j-form :layout="'vertical'" ref="formRef" :model="modelRef">
|
||||
<j-form-item name="parentId" label="上级区域">
|
||||
<j-tree-select
|
||||
showSearch
|
||||
allowClear
|
||||
v-model:value="modelRef.parentId"
|
||||
placeholder="1级区域不需要选择"
|
||||
:tree-data="areaList"
|
||||
:field-names="{
|
||||
showSearch
|
||||
allowClear
|
||||
v-model:value="modelRef.parentId"
|
||||
placeholder="1级区域不需要选择"
|
||||
:tree-data="areaList"
|
||||
:field-names="{
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
}"
|
||||
tree-node-filter-prop="name"
|
||||
@change="treeSelect"
|
||||
tree-node-filter-prop="name"
|
||||
@change="treeSelect"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item label="内置行政区" v-if="modelRef.properties.type === 'builtin'">
|
||||
<BuildIn
|
||||
<j-form-item label="添加方式">
|
||||
<RadioButton
|
||||
v-model:value="modelRef.properties.type"
|
||||
:options="[
|
||||
{
|
||||
label: '内置行政区',
|
||||
value: 'builtin'
|
||||
},
|
||||
{
|
||||
label: '自定义数据',
|
||||
value: 'custom'
|
||||
},
|
||||
]"
|
||||
@select="typeChange"
|
||||
/>
|
||||
</j-form-item>
|
||||
<template v-if="modelRef.properties.type === 'builtin'">
|
||||
<j-form-item>
|
||||
<BuildIn
|
||||
v-model:value="modelRef.code"
|
||||
v-model:children="modelRef.children"
|
||||
v-model:name="modelRef.name"
|
||||
v-model:sync="modelRef.properties.sync"
|
||||
:areaTree="areaTree"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
/>
|
||||
</j-form-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<j-form-item
|
||||
label="区域名称"
|
||||
name="name"
|
||||
required
|
||||
|
@ -52,50 +71,97 @@
|
|||
trigger: 'blur',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<j-input
|
||||
>
|
||||
<j-input
|
||||
v-model:value="modelRef.name"
|
||||
placeholder="请输入区域名称"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
label="行政区划代码"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
label="区划代码"
|
||||
name="code"
|
||||
required
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入行政区划代码',
|
||||
message: '请输入区划代码',
|
||||
},
|
||||
{
|
||||
validator: vailCode,
|
||||
trigger: 'blur',
|
||||
},
|
||||
]"
|
||||
>
|
||||
<j-input-number
|
||||
>
|
||||
<j-input-number
|
||||
v-model:value="modelRef.code"
|
||||
style="width: 100%"
|
||||
:disabled="modelRef.properties.type === 'builtin'"
|
||||
placeholder="请输入行政区划代码"
|
||||
/>
|
||||
</j-form-item>
|
||||
placeholder="请输入区划代码"
|
||||
/>
|
||||
</j-form-item>
|
||||
<j-form-item
|
||||
label="区域划分"
|
||||
>
|
||||
<RadioButton
|
||||
v-model:value="modelRef.properties.partition"
|
||||
:options="[
|
||||
{
|
||||
label: '无',
|
||||
value: 'none'
|
||||
},
|
||||
{
|
||||
label: '手动描点',
|
||||
value: 'manual'
|
||||
},
|
||||
{
|
||||
label: 'GeoJson',
|
||||
value: 'geoJson'
|
||||
},
|
||||
]"
|
||||
@select="typeChange"
|
||||
/>
|
||||
</j-form-item>
|
||||
<div v-if="modelRef.properties.partition === 'manual'">
|
||||
<a-button v-if="!modelRef.geoJson" type="link" style="padding: 0" @click="showEditMap(false)">请在地图上描点</a-button>
|
||||
<template v-else>
|
||||
<a-space>
|
||||
<span>区域已圈定完成</span>
|
||||
<a-button type="link" style="padding: 0" @click="showEditMap(true)">编辑</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</div>
|
||||
<div v-else-if="modelRef.properties.partition === 'geoJson'">
|
||||
<a-button v-if="!modelRef.geoJson" type="link" style="padding: 0" @click="geoJsonVisible = true">点击上传GeoJson</a-button>
|
||||
<template v-else>
|
||||
<a-space>
|
||||
<span>已上传</span>
|
||||
<a-button type="link" style="padding: 0" @click="geoJsonVisible = true">编辑</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</j-form>
|
||||
|
||||
<GeoJsonModal
|
||||
v-if="geoJsonVisible"
|
||||
:value="modelRef.geoJson"
|
||||
@cancel="geoJsonVisible = false"
|
||||
@ok="updateGeoJson"
|
||||
/>
|
||||
</div>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="Save">
|
||||
import {ref, watch, reactive} from 'vue';
|
||||
import type {PropType} from 'vue';
|
||||
import {reactive, ref, watch} from 'vue';
|
||||
import BuildIn from './BuildIn.vue';
|
||||
import {
|
||||
validateName,
|
||||
validateCode,
|
||||
updateRegion,
|
||||
} from '@/api/system/region';
|
||||
import {updateRegion, validateName, validateCode} from '@/api/system/region';
|
||||
import {omit} from "lodash-es";
|
||||
import {onlyMessage} from "@/utils/comm";
|
||||
import RadioButton from '@/components/CardSelect/RadioButton.vue'
|
||||
import GeoJsonModal from './GeoJsonModal.vue'
|
||||
import {useRegion} from "@/views/system/Region/hooks";
|
||||
import {syncChildren} from "@/views/system/Region/util";
|
||||
|
||||
const emit = defineEmits(['close', 'save']);
|
||||
const props = defineProps({
|
||||
|
@ -117,10 +183,13 @@ const props = defineProps({
|
|||
default: 'add',
|
||||
},
|
||||
});
|
||||
|
||||
const areaList = ref<Record<string, any>[]>([]);
|
||||
const loading = ref<boolean>(false);
|
||||
const geoJsonVisible = ref<boolean>(false);
|
||||
|
||||
const formRef = ref();
|
||||
const regionState = useRegion()
|
||||
|
||||
const init = {
|
||||
parentId: undefined,
|
||||
|
@ -131,36 +200,52 @@ const init = {
|
|||
children: [],
|
||||
properties: {
|
||||
type: 'builtin',
|
||||
partition: 'none',
|
||||
sync: true
|
||||
},
|
||||
sortIndex: props.data.sortIndex || 1
|
||||
sortIndex: props.data.sortIndex || 1,
|
||||
geoJson: undefined,
|
||||
};
|
||||
|
||||
const modelRef = reactive(init);
|
||||
const modelRef = reactive({
|
||||
parentId: undefined,
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
code: undefined,
|
||||
features: undefined,
|
||||
children: [],
|
||||
properties: {
|
||||
type: 'builtin',
|
||||
partition: 'none',
|
||||
sync: true
|
||||
},
|
||||
sortIndex: props.data.sortIndex || 1,
|
||||
geoJson: undefined,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
() => {
|
||||
Object.assign(modelRef, {});
|
||||
if (props.mode === 'add') {
|
||||
// 添加子
|
||||
Object.assign(modelRef, {
|
||||
...init,
|
||||
...props.data,
|
||||
});
|
||||
} else if (props.mode === 'edit') {
|
||||
// 编辑
|
||||
Object.assign(modelRef, props.data);
|
||||
} else {
|
||||
Object.assign(modelRef, init);
|
||||
}
|
||||
},
|
||||
{immediate: true, deep: true},
|
||||
);
|
||||
const updateGeoJson = (json: string) => {
|
||||
modelRef.geoJson = json
|
||||
geoJsonVisible.value = false
|
||||
}
|
||||
|
||||
const handleCancel = (data: any) => {
|
||||
if (modelRef.properties.type === 'custom') {
|
||||
if (props.mode === 'add') {
|
||||
regionState.mapInit()
|
||||
} else {
|
||||
regionState.mapReadOnly(modelRef.geoJson)
|
||||
}
|
||||
}
|
||||
|
||||
emit('close', data);
|
||||
};
|
||||
|
||||
const typeChange = (type: string) => {
|
||||
modelRef.geoJson = undefined
|
||||
modelRef.children = []
|
||||
modelRef.properties.sync =false
|
||||
}
|
||||
|
||||
const traceEdit = () => {
|
||||
const newData: any = {
|
||||
...props.data,
|
||||
|
@ -170,6 +255,21 @@ const traceEdit = () => {
|
|||
handleCancel(newData)
|
||||
}
|
||||
|
||||
const showEditMap = (type: boolean) => {
|
||||
regionState.treeMask = true
|
||||
regionState.saveCache = modelRef
|
||||
regionState.showTool()
|
||||
|
||||
if (type) {
|
||||
regionState.layerSetData(modelRef.geoJson)
|
||||
} else {
|
||||
regionState.type = undefined
|
||||
}
|
||||
|
||||
regionState.editType = props.mode
|
||||
|
||||
emit('close')
|
||||
}
|
||||
const treeSelect = (id: string, label: string, extra: any) => {
|
||||
let children: any[]
|
||||
if (extra) {
|
||||
|
@ -177,47 +277,56 @@ const treeSelect = (id: string, label: string, extra: any) => {
|
|||
} else {
|
||||
children = props.treeData
|
||||
}
|
||||
const lastItem = children.length ? children[children.length-1] : {}
|
||||
const lastItem = children.length ? children[children.length - 1] : {}
|
||||
modelRef.sortIndex = lastItem.sortIndex ? lastItem.sortIndex + 1 : 1
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
formRef.value
|
||||
.validate()
|
||||
.then(async (_data: any) => {
|
||||
const newData: any = {
|
||||
...omit(props.data, ['parentFullName', 'parentId']),
|
||||
...modelRef,
|
||||
}
|
||||
newData.fullName = props.data.parentFullName ? props.data.parentFullName + modelRef.name : modelRef.name
|
||||
newData.parentId = newData.parentId || ''
|
||||
.validate()
|
||||
.then(async (_data: any) => {
|
||||
const newData: any = {
|
||||
...omit(props.data, ['parentFullName', 'parentId']),
|
||||
...modelRef,
|
||||
}
|
||||
newData.fullName = props.data.parentFullName ? props.data.parentFullName + modelRef.name : modelRef.name
|
||||
newData.parentId = newData.parentId || ''
|
||||
|
||||
const arr = areaList.value.map(item=>item.code)
|
||||
if (newData.properties.sync) {
|
||||
const _syncChildren = syncChildren(newData.code, props.areaTree)
|
||||
|
||||
if (newData.children?.length) {
|
||||
newData.children = newData.children.map(item => {
|
||||
if (!item.fullName) {
|
||||
item.fullName = newData.fullName + item.name
|
||||
}
|
||||
item = {
|
||||
...item,
|
||||
children: []
|
||||
}
|
||||
return item
|
||||
}).filter(item =>!arr.includes(item.code))
|
||||
}
|
||||
loading.value = true;
|
||||
const resp = await updateRegion(newData).finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
if (resp.status === 200) {
|
||||
onlyMessage('操作成功!');
|
||||
emit('save');
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
console.log('error', err);
|
||||
const different = _syncChildren.filter(item => {
|
||||
if (newData.children && newData.children.some(oldItem => oldItem.code === item.code)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!item.fullName) {
|
||||
item.fullName = newData.fullName + item.name
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
newData.children = [
|
||||
...(newData.children || []),
|
||||
...different
|
||||
]
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
|
||||
const resp = await updateRegion(newData).finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
if (resp.status === 200) {
|
||||
regionState.stateInit()
|
||||
onlyMessage('操作成功!');
|
||||
emit('save');
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
console.log('error', err);
|
||||
});
|
||||
}
|
||||
|
||||
const vailName = async (_: Record<string, any>, value: string) => {
|
||||
|
@ -250,11 +359,28 @@ const onChange = () => {
|
|||
modelRef.features = undefined;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => JSON.stringify(props.data),
|
||||
(val) => {
|
||||
|
||||
if (props.mode === 'add') {
|
||||
// 添加子
|
||||
Object.assign(modelRef, init, JSON.parse(val || '{}'));
|
||||
} else if (props.mode === 'edit') {
|
||||
// 编辑
|
||||
Object.assign(modelRef, JSON.parse(val || '{}'));
|
||||
} else {
|
||||
Object.assign(modelRef, init);
|
||||
}
|
||||
},
|
||||
{immediate: true},
|
||||
);
|
||||
|
||||
|
||||
watch(() => JSON.stringify(props.treeData), () => {
|
||||
const item = JSON.parse(JSON.stringify(props.treeData))
|
||||
areaList.value = item
|
||||
if(props.mode === 'add'){
|
||||
modelRef.children = props.areaTree?.[0]?.children
|
||||
}
|
||||
areaList.value = JSON.parse(JSON.stringify(props.treeData))
|
||||
// if (props.mode === 'add' && modelRef.properties.sync) {
|
||||
// // modelRef.children = props.areaTree?.[0]?.children
|
||||
// }
|
||||
}, {immediate: true})
|
||||
</script>
|
||||
|
|
|
@ -49,3 +49,61 @@ export const useArea = () => {
|
|||
getParentNameById
|
||||
}
|
||||
}
|
||||
|
||||
export const REGION_KEY = Symbol('region_key')
|
||||
|
||||
export const useRegion = () => {
|
||||
return inject(REGION_KEY, {
|
||||
edit: false,
|
||||
type: undefined, // MAP_TOOL
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
export const useHistory = () => {
|
||||
const history = ref([])
|
||||
|
||||
const addRecord = (record: Record<string, any>) => {
|
||||
history.value.push(record)
|
||||
if (history.value.length > 10) { // 最多记录10条
|
||||
history.value = history.value.slice(1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销
|
||||
*/
|
||||
const revoke = () => {
|
||||
if (!hasHistory.value) {
|
||||
return
|
||||
}
|
||||
|
||||
if (history.value.length) {
|
||||
// 删除最后一条
|
||||
history.value.pop()
|
||||
}
|
||||
|
||||
return getLastHistory()
|
||||
}
|
||||
|
||||
const getLastHistory = () => {
|
||||
return history.value[history.value.length - 1]
|
||||
}
|
||||
|
||||
const hasHistory = computed(() => {
|
||||
return history.value.length > 1
|
||||
})
|
||||
|
||||
const reset = () => {
|
||||
history.value = []
|
||||
}
|
||||
|
||||
return {
|
||||
revoke,
|
||||
getLastHistory,
|
||||
addRecord,
|
||||
reset,
|
||||
hasHistory
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,10 @@
|
|||
<full-page fixed>
|
||||
<div class="region">
|
||||
<div class="left">
|
||||
<LeftTree @select="onSelect" />
|
||||
<div v-if="regionState.treeMask" class="left-mask"></div>
|
||||
<div class="left-content">
|
||||
<LeftTree ref="treeRef" @select="onSelect" @close="close"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<Map ref="mapRef" :selectCode="selectCode" />
|
||||
|
@ -17,12 +20,92 @@
|
|||
import LeftTree from './LeftTree/index.vue'
|
||||
import Map from './MapTool/map.vue'
|
||||
import FullPage from "@/components/Layout/FullPage.vue";
|
||||
import {REGION_KEY} from "@/views/system/Region/hooks";
|
||||
import {MAP_TOOL} from "@/views/system/Region/util";
|
||||
|
||||
const selectCode = ref('')
|
||||
const mapRef = ref()
|
||||
const treeRef = ref()
|
||||
|
||||
const onSelect = (dt: string) => {
|
||||
selectCode.value = dt
|
||||
const regionState = reactive({
|
||||
showTool: showTool,
|
||||
openSave: openSave,
|
||||
openEdit: openEdit,
|
||||
layerSetData: layerSetData,
|
||||
mapInit: mapInit,
|
||||
edit: false,
|
||||
editType: 'add',
|
||||
treeMask: false,
|
||||
saveCache: undefined,
|
||||
stateInit: stateInit,
|
||||
mapReadOnly: mapReadOnly,
|
||||
prevSelect: {}
|
||||
})
|
||||
|
||||
provide(REGION_KEY, regionState)
|
||||
|
||||
const onSelect = (code: string, node: Record<string, any>) => {
|
||||
if (!node) return
|
||||
|
||||
if (node.properties?.partition === 'geoJson') {
|
||||
mapRef.value?.showGeoJson(node.geoJson)
|
||||
regionState.type = MAP_TOOL.geoJson
|
||||
} else if(node.properties?.partition === 'manual') {
|
||||
layerSetData(node.geoJson, false)
|
||||
} else {
|
||||
mapRef.value?.showDistrict(code)
|
||||
regionState.type = MAP_TOOL.district
|
||||
}
|
||||
|
||||
regionState.prevSelect = {
|
||||
code, node
|
||||
}
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
if (regionState.prevSelect.code) {
|
||||
onSelect(regionState.prevSelect.code, regionState.prevSelect.node)
|
||||
}
|
||||
}
|
||||
|
||||
function stateInit() {
|
||||
regionState.edit = false
|
||||
regionState.treeMask = false
|
||||
regionState.saveCache = undefined
|
||||
regionState.type = undefined
|
||||
regionState.editType = 'add'
|
||||
mapInit()
|
||||
}
|
||||
|
||||
function openSave(geoJson: Record<string, any>){
|
||||
treeRef.value?.openSave(geoJson)
|
||||
}
|
||||
|
||||
function showTool(type: string) {
|
||||
mapRef.value?.showTool(regionState.saveCache?.geoJson)
|
||||
}
|
||||
|
||||
function mapReadOnly(geoJson: any) {
|
||||
layerSetData(geoJson, false)
|
||||
mapRef.value?.readOnly(geoJson)
|
||||
}
|
||||
|
||||
function openEdit() {
|
||||
mapRef.value?.openEdit()
|
||||
}
|
||||
|
||||
function layerSetData(geoJson: Record<string, any>, edit = true) {
|
||||
regionState.type = geoJson.features[0].properties.type
|
||||
mapRef.value?.showGeoJson(geoJson.features[0].geometry.coordinates)
|
||||
if (edit) {
|
||||
mapRef.value?.openEdit()
|
||||
}
|
||||
}
|
||||
|
||||
function mapInit() {
|
||||
mapRef.value?.init()
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@ -36,6 +119,22 @@ const onSelect = (dt: string) => {
|
|||
width: 300px;
|
||||
position: relative;
|
||||
|
||||
.left-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.left-mask {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 2;
|
||||
background-color: rgba(0,0,0, .2);
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
margin: 18px 0;
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</j-button>
|
||||
<PermissionButton
|
||||
:popConfirm="{
|
||||
title: `是否批量解除绑定`,
|
||||
title: `确认批量解除绑定?`,
|
||||
placement: 'topRight',
|
||||
onConfirm: () => table.unbind(),
|
||||
}"
|
||||
|
@ -170,12 +170,14 @@ const table = {
|
|||
onlyMessage('请勾选数据', 'warning');
|
||||
return;
|
||||
}
|
||||
unbindUser_api(roleId, data).then((resp) => {
|
||||
const response = unbindUser_api(roleId, data)
|
||||
response.then((resp) => {
|
||||
if (resp.status === 200) {
|
||||
onlyMessage('操作成功');
|
||||
table.refresh();
|
||||
}
|
||||
});
|
||||
return response
|
||||
},
|
||||
// 刷新表格
|
||||
refresh: () => {
|
||||
|
|
|
@ -29,6 +29,11 @@
|
|||
<AIcon type="PlusOutlined" />新增
|
||||
</PermissionButton>
|
||||
</template>
|
||||
<template #username="slotProps">
|
||||
<div class="username">
|
||||
<Ellipsis>{{ slotProps.username }}</Ellipsis>
|
||||
</div>
|
||||
</template>
|
||||
<template #type="slotProps">
|
||||
{{ slotProps.type?.name }}
|
||||
</template>
|
||||
|
@ -74,9 +79,9 @@
|
|||
}`,
|
||||
}"
|
||||
:popConfirm="{
|
||||
title: `确定${
|
||||
title: `确认${
|
||||
slotProps.status ? '禁用' : '启用'
|
||||
}吗?`,
|
||||
}?`,
|
||||
onConfirm: () =>
|
||||
table.changeStatus(slotProps),
|
||||
}"
|
||||
|
@ -160,7 +165,7 @@ const columns = [
|
|||
title: '用户名',
|
||||
dataIndex: 'username',
|
||||
key: 'username',
|
||||
ellipsis: true,
|
||||
scopedSlots: true,
|
||||
search: {
|
||||
type: 'string',
|
||||
},
|
||||
|
@ -276,17 +281,21 @@ const table = {
|
|||
status: status === 0 ? 1 : 0,
|
||||
id,
|
||||
};
|
||||
changeUserStatus_api(params).then(() => {
|
||||
const response = changeUserStatus_api(params);
|
||||
response.then(() => {
|
||||
onlyMessage('操作成功');
|
||||
table.refresh();
|
||||
});
|
||||
return response;
|
||||
},
|
||||
// 删除
|
||||
clickDel: (id: string) => {
|
||||
deleteUser_api(id).then(() => {
|
||||
const response = deleteUser_api(id);
|
||||
response.then(() => {
|
||||
onlyMessage('操作成功');
|
||||
table.refresh();
|
||||
});
|
||||
return response;
|
||||
},
|
||||
// 刷新列表
|
||||
refresh: () => {
|
||||
|
@ -361,19 +370,22 @@ const handleParams = (params: any) => {
|
|||
};
|
||||
}
|
||||
}
|
||||
if(termsItem.column === 'roleList'){
|
||||
if(termsItem.termType === 'eq' || termsItem.termType === 'in'){
|
||||
if (termsItem.column === 'roleList') {
|
||||
if (
|
||||
termsItem.termType === 'eq' ||
|
||||
termsItem.termType === 'in'
|
||||
) {
|
||||
return {
|
||||
column: 'id$in-dimension$role',
|
||||
type: termsItem.type,
|
||||
value: termsItem.value
|
||||
}
|
||||
}else{
|
||||
value: termsItem.value,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
column: 'id$in-dimension$role$not',
|
||||
type: termsItem.type,
|
||||
value: termsItem.value
|
||||
}
|
||||
value: termsItem.value,
|
||||
};
|
||||
}
|
||||
}
|
||||
return termsItem;
|
||||
|
@ -401,4 +413,12 @@ const handleParams = (params: any) => {
|
|||
}
|
||||
}
|
||||
}
|
||||
.username {
|
||||
display: inline-block;
|
||||
border: 1px solid #91caff;
|
||||
padding: 0 8px;
|
||||
border-radius: 4px;
|
||||
color: #1677ff;
|
||||
background: #e6f4ff;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -94,14 +94,15 @@ export default defineConfig(({ mode}) => {
|
|||
[env.VITE_APP_BASE_API]: {
|
||||
// target: 'http://192.168.32.226:8844',
|
||||
// target: 'http://192.168.32.244:8881',
|
||||
// target: 'http://192.168.32.163:8844', //张本地
|
||||
// target: 'http://192.168.32.217:8844', //张本地
|
||||
// target: 'http://120.77.179.54:8844', // 120测试
|
||||
target: 'http://192.168.33.46:8844', // 本地开发环境
|
||||
// target: 'http://192.168.32.167:8844', // 本地开发环境1
|
||||
// target: 'http://192.168.33.6:8848', // 社区版开发环境
|
||||
// target: 'http://192.168.33.6:38848', // 社区版开发环境
|
||||
// target: 'http://192.168.32.207:8844', // 刘本地
|
||||
// target: 'http://192.168.32.187:8844', // 谭本地
|
||||
// target: 'http://192.168.33.66:8844', // 苟本地
|
||||
// target: 'http://192.168.35.155:8844', // 王本地
|
||||
ws: 'ws://192.168.33.46:8844',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, '')
|
||||
|
@ -113,6 +114,7 @@ export default defineConfig(({ mode}) => {
|
|||
less: {
|
||||
modifyVars: {
|
||||
'root-entry-name': 'variable',
|
||||
'primary-color': '#1677FF',
|
||||
hack: `true; @import (reference) "${path.resolve('src/style/variable.less')}";`,
|
||||
},
|
||||
javascriptEnabled: true,
|
||||
|
|
Loading…
Reference in New Issue