fix: 修复公网host多次校验提示
* fix: 处理额外子路由 * fix: 优化物模型-全屏编辑 * fix: 优化物模型-枚举校验 * fix: 修改设备透传默认脚本 * fix: 优化个人中心菜单 * fix: 修改物模型-指标值float,double值问题 * fix: bug#16782;修改数采值为COLLECTOR_GATEWAY; * fix: 修改bug * fix: 新增通道添加circuitBreaker属性 * fix: 16802 * fix: 修改运维管理的dashboard颜色 * fix: 修改通道状态 * fix: 16719 * fix: 修改首页视图图片 * feat: 视频预置点位 * refactor: 预置点位 * fix: 修改预置点位 * fix: 修改物模型页面路由跳转无法触发修改未保存提示 * fix: bug#16796 * fix: bug#16963 * fix: 修改auth页面获取参数 * fix: 修改设备物模型-属性来源可以选择规则 * fix: 优化设备物模型-属性来源可以修改产品规则 * fix: 优化设备物模型-规则调试 * fix: 修改设备物模型-属性来源可以选择规则 * feat: 修改bug优化ui * fix: 修改物模型导入bug * fix: 优化设备物模型-规则调试 * fix: 优化设备物模型-属性来源可以修改产品规则 * fix: 修改设备物模型-属性来源可以选择规则 * fix: 优化设备管理-设备功能无物模型时兼容处理 * fix: 优化设备管理-设备功能无物模型时兼容处理 * fix: 修复设备无物模型时兼容问题 * fix: 修复第三方应用集成无法显示 * fix: bug#16997 * fix: bug#17001 * fix: bug#16990 * fix: 优化初始化菜单协议查询;修改产品保存接口请求类型为put * fix: bug#16969 * fix: 修复公网host多次校验提示 * fix: 修改bug * feat: 修改bug优化ui * fix: 修改bug * fix: 修改bug * fix: 修改bug * fix: 修改物模型导入bug * fix: 修改bug * fix: 修改bug * fix: 修改bug * fix: 设备功能 * fix: 设备功能删除console.log * fix: 修改onenet和ctwing * fix: 修改bug * fix: 修改首页视图 * fix: 修复产品跳转设备接口异常 * fix: 17008、16996 * fix: 修复Iframe页面无法访问 * fix: bug#17006 * fix: 修改bug * feat: 17019 * fix: 修改文档 * fix: ctwing右侧提示 * fix: bug#16975 * fix: 修改17021 * fix: bug#17018 * fix: 优化oauth页面跳转逻辑 * fix: 产品导入提示bug * fix: 产品导入bug * fix: bug#17035 * fix: bug#17041 * fix: bug#17041 * fix: bug#17041 * fix: bug#17051 * fix: 初始化页面添加菜单控制 * fix: 修复公网host多次校验提示
|
@ -25,7 +25,7 @@
|
||||||
"event-source-polyfill": "^1.0.31",
|
"event-source-polyfill": "^1.0.31",
|
||||||
"global": "^4.4.0",
|
"global": "^4.4.0",
|
||||||
"jetlinks-store": "^0.0.3",
|
"jetlinks-store": "^0.0.3",
|
||||||
"jetlinks-ui-components": "^1.0.27",
|
"jetlinks-ui-components": "^1.0.28",
|
||||||
"js-cookie": "^3.0.1",
|
"js-cookie": "^3.0.1",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"less": "^4.1.3",
|
"less": "^4.1.3",
|
||||||
|
|
After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 113 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 109 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 274 KiB After Width: | Height: | Size: 274 KiB |
After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 307 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 91 KiB |
After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 22 KiB |
|
@ -10,7 +10,7 @@ export const updateMeInfo_api = (data:object) => server.put(`/user/detail`,data)
|
||||||
// 修改登录用户密码
|
// 修改登录用户密码
|
||||||
export const updateMepsd_api = (data:object) => server.put(`/user/passwd`,data);
|
export const updateMepsd_api = (data:object) => server.put(`/user/passwd`,data);
|
||||||
// 第三方账号解绑
|
// 第三方账号解绑
|
||||||
export const unBind_api = (appId: string) => server.request.post(`/application/sso/${appId}/unbind/me`);
|
export const unBind_api = (appId: string) => server.post(`/application/sso/${appId}/unbind/me`);
|
||||||
/**
|
/**
|
||||||
* 校验字段合法性
|
* 校验字段合法性
|
||||||
* @param type 类型
|
* @param type 类型
|
||||||
|
|
|
@ -11,7 +11,7 @@ export const save = (data: any) => server.post(`/data-collect/channel`, data);
|
||||||
export const update = (id: string, data: any) =>
|
export const update = (id: string, data: any) =>
|
||||||
server.put(`/data-collect/channel/${id}`, data);
|
server.put(`/data-collect/channel/${id}`, data);
|
||||||
|
|
||||||
export const getProviders = () => server.get(`/gateway/device/providers`);
|
export const getProviders = () => server.get(`/data-collect/channel/providers`);
|
||||||
|
|
||||||
export const queryOptionsList = (type: string) =>
|
export const queryOptionsList = (type: string) =>
|
||||||
server.get(`/data-collect/opc/${type}`);
|
server.get(`/data-collect/opc/${type}`);
|
||||||
|
|
|
@ -61,3 +61,6 @@ export const scanOpcUAList = (data: any) =>
|
||||||
|
|
||||||
export const queryTypeList = () => server.get(`/data-collect/opc/data-types`);
|
export const queryTypeList = () => server.get(`/data-collect/opc/data-types`);
|
||||||
|
|
||||||
|
export const getProviders = () => server.get('/data-collect/channel/gateway/codec/providers')
|
||||||
|
|
||||||
|
export const getStates = () => server.get('/dictionary/running-state/items')
|
|
@ -106,7 +106,7 @@ export const addProduct = (data:any) => server.post('/device-product',data)
|
||||||
* @param data
|
* @param data
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const editProduct = (data: any) => server.patch('/device-product', data)
|
export const editProduct = (data: any) => server.put(`/device-product/${data.id}`, data)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除产品
|
* 删除产品
|
||||||
|
|
|
@ -67,4 +67,7 @@ export default {
|
||||||
|
|
||||||
// 播放云端回放
|
// 播放云端回放
|
||||||
playbackStart: (recordId: string) => server.get(`/media/record/${recordId}.mp4`),
|
playbackStart: (recordId: string) => server.get(`/media/record/${recordId}.mp4`),
|
||||||
|
|
||||||
|
// 设备预置位相关接口
|
||||||
|
opFunction: (deviceId: string, functionId: string, data?: any) => server.post(`/device/invoked/${deviceId}/function/${functionId}`, data)
|
||||||
}
|
}
|
|
@ -3,7 +3,9 @@
|
||||||
<j-input-search @search="search" allow-clear placeholder="搜索关键字" />
|
<j-input-search @search="search" allow-clear placeholder="搜索关键字" />
|
||||||
<div class="tree">
|
<div class="tree">
|
||||||
<j-tree @select="selectTree" :field-names="{ title: 'name', key: 'id', }" auto-expand-parent
|
<j-tree @select="selectTree" :field-names="{ title: 'name', key: 'id', }" auto-expand-parent
|
||||||
:tree-data="data">
|
:tree-data="data"
|
||||||
|
:showLine="{ showLeafIcon: false }"
|
||||||
|
:show-icon="true">
|
||||||
<template #title="node">
|
<template #title="node">
|
||||||
<div class="node">
|
<div class="node">
|
||||||
<div style="max-width: 180px"><Ellipsis>{{ node.name }}</Ellipsis></div>
|
<div style="max-width: 180px"><Ellipsis>{{ node.name }}</Ellipsis></div>
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
:field-names="{ title: 'name', key: 'id' }"
|
:field-names="{ title: 'name', key: 'id' }"
|
||||||
auto-expand-parent
|
auto-expand-parent
|
||||||
:tree-data="data"
|
:tree-data="data"
|
||||||
|
:showLine="{ showLeafIcon: false }"
|
||||||
|
:show-icon="true"
|
||||||
>
|
>
|
||||||
<template #title="node">
|
<template #title="node">
|
||||||
<div class="node">
|
<div class="node">
|
||||||
|
@ -86,6 +88,7 @@ import Markdown from 'vue3-markdown-it';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
id: String,
|
id: String,
|
||||||
|
propertiesOptions: Array
|
||||||
});
|
});
|
||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
|
@ -121,16 +124,14 @@ const lastClick = (node: OperatorItem) => {
|
||||||
emit('addOperatorValue', `$lastState("${node.id}")`);
|
emit('addOperatorValue', `$lastState("${node.id}")`);
|
||||||
};
|
};
|
||||||
const addClick = (node: OperatorItem) => {
|
const addClick = (node: OperatorItem) => {
|
||||||
console.log(node)
|
|
||||||
emit('addOperatorValue', node.code);
|
emit('addOperatorValue', node.code);
|
||||||
};
|
};
|
||||||
|
|
||||||
const productStore = useProductStore();
|
const productStore = useProductStore();
|
||||||
|
|
||||||
const getData = async (id?: string) => {
|
const getData = async (id?: string) => {
|
||||||
const metadata = productStore.current.metadata || '{}';
|
// const metadata = productStore.current.metadata || '{}';
|
||||||
const _properties =
|
const _properties = props.propertiesOptions as PropertyMetadata[]
|
||||||
JSON.parse(metadata).properties || ([] as PropertyMetadata[]);
|
|
||||||
const properties = {
|
const properties = {
|
||||||
id: 'property',
|
id: 'property',
|
||||||
name: '属性',
|
name: '属性',
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<div>
|
<div>
|
||||||
<Operator :id="id" @add-operator-value="addOperatorValue" />
|
<Operator :id="id" :propertiesOptions="propertiesOptions" @add-operator-value="addOperatorValue" />
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top: 10px;">
|
<div style="margin-top: 10px;">
|
||||||
<Editor
|
<Editor
|
||||||
|
@ -70,7 +70,8 @@ const props = defineProps({
|
||||||
value: String,
|
value: String,
|
||||||
id: String,
|
id: String,
|
||||||
virtualRule: Object,
|
virtualRule: Object,
|
||||||
aggList: Array
|
aggList: Array,
|
||||||
|
propertiesOptions: Array
|
||||||
});
|
});
|
||||||
|
|
||||||
const _value = ref<string | undefined>(props.value);
|
const _value = ref<string | undefined>(props.value);
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
<template>
|
||||||
|
<div class="view-content">
|
||||||
|
<div
|
||||||
|
class="select-item"
|
||||||
|
v-for="item in list"
|
||||||
|
:key="item.id"
|
||||||
|
@click="onChange(item.id)"
|
||||||
|
:class="{
|
||||||
|
active: currentView === item.id,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<img :src="getImage(`/home/home-view/${item.id}${currentView === item.id ? '-active' : ''}.png`)" alt="" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { getImage } from '@/utils/comm';
|
||||||
|
|
||||||
|
const list = [
|
||||||
|
{
|
||||||
|
id: 'device',
|
||||||
|
name: '设备接入视图',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'ops',
|
||||||
|
name: '运营管理视图',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'comprehensive',
|
||||||
|
name: '综合管理视图',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emits = defineEmits(['update:value', 'change'])
|
||||||
|
|
||||||
|
const currentView = ref<string>('');
|
||||||
|
|
||||||
|
const onChange = (id: string) => {
|
||||||
|
emits('change', id);
|
||||||
|
emits('update:value', id)
|
||||||
|
}
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
currentView.value = (props.value || '') as string
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.view-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
.select-item {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 30%;
|
||||||
|
border-radius: 14px;
|
||||||
|
color: #333333;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0px 3px 6px -4px rgba(0, 0, 0, 0.12),
|
||||||
|
0px 6px 16px 0px rgba(0, 0, 0, 0.08),
|
||||||
|
0px 9px 16px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -19,7 +19,7 @@
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
{
|
{
|
||||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
message: '标识只能由数字、字母、下划线、中划线组成',
|
||||||
},
|
},
|
||||||
]">
|
]">
|
||||||
<j-input v-model:value="_value[index].id" size="small" placeholder="请输入标识"></j-input>
|
<j-input v-model:value="_value[index].id" size="small" placeholder="请输入标识"></j-input>
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
{
|
{
|
||||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
message: '标识只能由数字、字母、下划线、中划线组成',
|
||||||
},
|
},
|
||||||
]">
|
]">
|
||||||
<j-input v-model:value="_value[index].id" size="small" placeholder="请输入标识"></j-input>
|
<j-input v-model:value="_value[index].id" size="small" placeholder="请输入标识"></j-input>
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<j-form-item name="type" label="读写类型" required>
|
<j-form-item name="type" label="读写类型" :rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择读写类型'
|
||||||
|
}
|
||||||
|
]">
|
||||||
<j-select
|
<j-select
|
||||||
v-model:value="myValue"
|
v-model:value="myValue"
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
</j-button>
|
</j-button>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<j-tooltip v-else title="暂无权限,请联系管理员">
|
<j-tooltip v-else title="暂无权限,请联系管理员" :placement="placement">
|
||||||
<slot v-if="noButton"></slot>
|
<slot v-if="noButton"></slot>
|
||||||
<j-button v-else v-bind="props" :disabled="_isPermission" :style="props.style">
|
<j-button v-else v-bind="props" :disabled="_isPermission" :style="props.style">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
@ -89,6 +89,10 @@ const props = defineProps({
|
||||||
style: {
|
style: {
|
||||||
type: Object as PropType<CSSProperties>
|
type: Object as PropType<CSSProperties>
|
||||||
},
|
},
|
||||||
|
placement:{
|
||||||
|
type: String,
|
||||||
|
default: 'top'
|
||||||
|
},
|
||||||
...omit(buttonProps(), 'icon')
|
...omit(buttonProps(), 'icon')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 45%;
|
width: 45%;
|
||||||
height: 45%;
|
height: 45%;
|
||||||
font-size: 30px;
|
// font-size: 30px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
transform: translate(-50%, -50%) rotateZ(-45deg);
|
transform: translate(-50%, -50%) rotateZ(-45deg);
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="direction-audio">
|
<div class="direction-audio">
|
||||||
<!-- <AIcon type="AudioOutlined" /> -->
|
<slot name="center"><!-- <AIcon type="AudioOutlined" /> --></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="zoom">
|
<div class="zoom">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
import menus, { AccountCenterBindPath, InitHomePath, InitLicense, LoginPath, OauthPath } from './menu'
|
import menus, { AccountCenterBindPath, InitHomePath, InitLicense, LoginPath, OauthPath, VideoSharePath } from './menu'
|
||||||
import { cleanToken, getToken } from '@/utils/comm'
|
import { cleanToken, getToken } from '@/utils/comm'
|
||||||
import { useUserInfo } from '@/store/userInfo'
|
import { useUserInfo } from '@/store/userInfo'
|
||||||
import { useSystem } from '@/store/system'
|
import { useSystem } from '@/store/system'
|
||||||
|
@ -15,7 +15,7 @@ const router = createRouter({
|
||||||
})
|
})
|
||||||
|
|
||||||
const filterPath = [ InitHomePath ]
|
const filterPath = [ InitHomePath ]
|
||||||
const noTokenPath = [ AccountCenterBindPath, OauthPath, InitLicense ]
|
const noTokenPath = [ AccountCenterBindPath, OauthPath, InitLicense, VideoSharePath ]
|
||||||
|
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
// TODO 切换路由取消请求
|
// TODO 切换路由取消请求
|
||||||
|
|
|
@ -5,6 +5,7 @@ export const InitLicense = '/init-license'
|
||||||
export const NotificationSubscriptionCode = 'message-subscribe'
|
export const NotificationSubscriptionCode = 'message-subscribe'
|
||||||
export const NotificationRecordCode = 'account/NotificationRecord'
|
export const NotificationRecordCode = 'account/NotificationRecord'
|
||||||
export const OauthPath = '/oauth'
|
export const OauthPath = '/oauth'
|
||||||
|
export const VideoSharePath = '/media/device/Share'
|
||||||
|
|
||||||
export const AccountMenu = {
|
export const AccountMenu = {
|
||||||
path: '/account',
|
path: '/account',
|
||||||
|
@ -86,5 +87,9 @@ export default [
|
||||||
},
|
},
|
||||||
component: () => import('@/views/oauth/WeChat.vue')
|
component: () => import('@/views/oauth/WeChat.vue')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: VideoSharePath,
|
||||||
|
component: () => import('@/views/media/Device/Channel/Share/index.vue')
|
||||||
|
},
|
||||||
AccountMenu
|
AccountMenu
|
||||||
]
|
]
|
|
@ -212,3 +212,7 @@ body {
|
||||||
margin: 16px 0;
|
margin: 16px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-notification {
|
||||||
|
z-index: 1100;
|
||||||
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ export const USER_CENTER_MENU_BUTTON_CODE = 'user-center-passwd-update'
|
||||||
|
|
||||||
/**协议列表 */
|
/**协议列表 */
|
||||||
export const protocolList = [
|
export const protocolList = [
|
||||||
{ label: 'OPC-UA', value: 'OPC_UA', alias: 'opc-ua' },
|
{ label: 'OPC_UA', value: 'OPC_UA', alias: 'opc-ua' },
|
||||||
{ label: 'Modbus/TCP', value: 'MODBUS_TCP', alias: 'modbus-tcp' },
|
{ label: 'MODBUS_TCP', value: 'MODBUS_TCP', alias: 'Modbus/TCP' },
|
||||||
|
{ label: 'COLLECTOR_GATEWAY', value: 'COLLECTOR_GATEWAY', alias: 'GATEWAY' },
|
||||||
]
|
]
|
||||||
|
|
|
@ -368,10 +368,10 @@ const findComponents = (code: string, level: number, isApp: boolean, components:
|
||||||
const myComponents = components[code]
|
const myComponents = components[code]
|
||||||
if (level === 1) { // BasicLayoutPage
|
if (level === 1) { // BasicLayoutPage
|
||||||
return myComponents ? () => myComponents() : BasicLayoutPage
|
return myComponents ? () => myComponents() : BasicLayoutPage
|
||||||
|
} else if (isApp){ // iframe
|
||||||
|
return Iframe
|
||||||
} else if (level === 2) { // BlankLayoutPage or components
|
} else if (level === 2) { // BlankLayoutPage or components
|
||||||
return myComponents ? () => myComponents() : BlankLayoutPage
|
return myComponents ? () => myComponents() : BlankLayoutPage
|
||||||
} else if (isApp){ // iframe
|
|
||||||
return () => Iframe
|
|
||||||
} else if(myComponents) { // components
|
} else if(myComponents) { // components
|
||||||
return () => myComponents()
|
return () => myComponents()
|
||||||
}
|
}
|
||||||
|
@ -417,6 +417,21 @@ const findDetailRouteItem = (item: any, components: any) => {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const findSaveRouteItem = (item: any, components: any) => {
|
||||||
|
const { code, url } = item
|
||||||
|
const Component = components[`${item.code}/Save`]
|
||||||
|
if (Component) {
|
||||||
|
return [{
|
||||||
|
url: `${url}/detail/:id`,
|
||||||
|
code: `${code}/Save`,
|
||||||
|
component: Component,
|
||||||
|
name: '详情信息',
|
||||||
|
isShow: false
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
export const handleMenus = (menuData: any[], components: any, level: number = 1) => {
|
export const handleMenus = (menuData: any[], components: any, level: number = 1) => {
|
||||||
if (menuData && menuData.length) {
|
if (menuData && menuData.length) {
|
||||||
return menuData.map(item => {
|
return menuData.map(item => {
|
||||||
|
@ -434,7 +449,6 @@ export const handleMenus = (menuData: any[], components: any, level: number = 1)
|
||||||
const extraRoute = hasExtraChildren(item, extraRouteObj)
|
const extraRoute = hasExtraChildren(item, extraRouteObj)
|
||||||
const detail_components = findDetailRouteItem(item, components)
|
const detail_components = findDetailRouteItem(item, components)
|
||||||
|
|
||||||
|
|
||||||
if (extraRoute && !isApp) { // 包含额外的子路由
|
if (extraRoute && !isApp) { // 包含额外的子路由
|
||||||
route.children = route.children ? [...route.children, ...extraRoute] : extraRoute
|
route.children = route.children ? [...route.children, ...extraRoute] : extraRoute
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,9 +43,7 @@ export const initWebSocket = () => {
|
||||||
const data = JSON.parse(msg.data)
|
const data = JSON.parse(msg.data)
|
||||||
|
|
||||||
if (data.type === 'error') {
|
if (data.type === 'error') {
|
||||||
notification.error({ key: 'wserr', message: data.message, style: {
|
notification.error({ key: 'error', message: data.message })
|
||||||
zIndex: 1040
|
|
||||||
} })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subs[data.requestId]) {
|
if (subs[data.requestId]) {
|
||||||
|
|
|
@ -0,0 +1,190 @@
|
||||||
|
<template>
|
||||||
|
<j-modal
|
||||||
|
:maskClosable="false"
|
||||||
|
:width='820'
|
||||||
|
title="选择网关设备"
|
||||||
|
visible
|
||||||
|
@cancel="cancel"
|
||||||
|
@ok="confirm"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<j-advanced-search
|
||||||
|
:columns="columns"
|
||||||
|
class="scene-search"
|
||||||
|
target="scene-triggrt-device-device"
|
||||||
|
type='simple'
|
||||||
|
@search="handleSearch"
|
||||||
|
/>
|
||||||
|
<j-divider style='margin: 0' />
|
||||||
|
<j-pro-table
|
||||||
|
ref='actionRef'
|
||||||
|
:bodyStyle='{
|
||||||
|
paddingRight: 0,
|
||||||
|
paddingLeft: 0
|
||||||
|
}'
|
||||||
|
:columns='columns'
|
||||||
|
:defaultParams="{
|
||||||
|
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||||
|
terms: [{value: 'gateway', termType: 'eq', column: 'deviceType'}]
|
||||||
|
}"
|
||||||
|
:gridColumn='2'
|
||||||
|
:params='params'
|
||||||
|
:request='query'
|
||||||
|
:scroll="{y: 350}"
|
||||||
|
model='CARD'
|
||||||
|
>
|
||||||
|
<template #card="slotProps">
|
||||||
|
<CardBox
|
||||||
|
:active="selectKey.id === slotProps.id"
|
||||||
|
:status="slotProps.state?.value"
|
||||||
|
:statusNames="{
|
||||||
|
online: 'processing',
|
||||||
|
offline: 'error',
|
||||||
|
notActive: 'warning',
|
||||||
|
}"
|
||||||
|
:statusText="slotProps.state?.text"
|
||||||
|
:value='slotProps'
|
||||||
|
@click="handleClick"
|
||||||
|
>
|
||||||
|
<template #img>
|
||||||
|
<slot name="img">
|
||||||
|
<img :src="slotProps.photoUrl || getImage('/device/instance/device-card.png')" height='80' width='80' />
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<Ellipsis style='width: calc(100% - 100px)'>
|
||||||
|
<span style="font-size: 16px;font-weight: 600" >
|
||||||
|
{{ slotProps.name }}
|
||||||
|
</span>
|
||||||
|
</Ellipsis>
|
||||||
|
<j-row>
|
||||||
|
<j-col :span="12">
|
||||||
|
<div class="card-item-content-text">
|
||||||
|
设备类型
|
||||||
|
</div>
|
||||||
|
<div>{{ slotProps.deviceType?.text }}</div>
|
||||||
|
</j-col>
|
||||||
|
<j-col :span="12">
|
||||||
|
<div class="card-item-content-text">
|
||||||
|
产品名称
|
||||||
|
</div>
|
||||||
|
<div>{{ slotProps.productName }}</div>
|
||||||
|
</j-col>
|
||||||
|
</j-row>
|
||||||
|
</template>
|
||||||
|
</CardBox>
|
||||||
|
</template>
|
||||||
|
</j-pro-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</j-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script name="GateWayDevice" setup>
|
||||||
|
import { getImage } from '@/utils/comm'
|
||||||
|
import {query} from "@/api/device/instance";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
default: undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['cancel', 'confirm'])
|
||||||
|
|
||||||
|
const selectKey = reactive({
|
||||||
|
id: props.value,
|
||||||
|
name: props.name
|
||||||
|
})
|
||||||
|
const params = ref()
|
||||||
|
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
width: 300,
|
||||||
|
ellipsis: true,
|
||||||
|
fixed: 'left',
|
||||||
|
search: {
|
||||||
|
type: 'string'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '设备名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
first: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
width: 200,
|
||||||
|
search: {
|
||||||
|
type: 'date'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'state',
|
||||||
|
width: 90,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: '禁用', value: 'notActive' },
|
||||||
|
{ label: '离线', value: 'offline' },
|
||||||
|
{ label: '在线', value: 'online' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// const deviceQuery = (p) => {
|
||||||
|
// const sorts = [];
|
||||||
|
//
|
||||||
|
// if (props.rowKeys) {
|
||||||
|
// props.rowKeys.forEach(rowKey => {
|
||||||
|
// sorts.push({
|
||||||
|
// name: 'id',
|
||||||
|
// value: rowKey,
|
||||||
|
// });
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// sorts.push({ name: 'createTime', order: 'desc' });
|
||||||
|
// const terms = [
|
||||||
|
// ...p.terms,
|
||||||
|
// { terms: [{ column: "productId", value: props.productId }]}
|
||||||
|
// ]
|
||||||
|
// return query({ ...p, terms, sorts })
|
||||||
|
// }
|
||||||
|
|
||||||
|
const handleClick = (detail) => {
|
||||||
|
console.log(detail)
|
||||||
|
selectKey.id = detail.id
|
||||||
|
selectKey.name = detail.name
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSearch = (p) => {
|
||||||
|
params.value = p
|
||||||
|
}
|
||||||
|
const cancel = () => {
|
||||||
|
emit('cancel')
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirm = () => {
|
||||||
|
emit('confirm', selectKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,71 @@
|
||||||
|
<template>
|
||||||
|
<j-button
|
||||||
|
v-if="!value"
|
||||||
|
style="width: 100%"
|
||||||
|
@click="showModal"
|
||||||
|
>
|
||||||
|
选择网关设备
|
||||||
|
</j-button>
|
||||||
|
<div v-else class="gateway-form-item">
|
||||||
|
<span>
|
||||||
|
<j-ellipsis>
|
||||||
|
{{ name }}
|
||||||
|
</j-ellipsis>
|
||||||
|
</span>
|
||||||
|
<j-button type="link" @click="showModal">
|
||||||
|
重新选择
|
||||||
|
</j-button>
|
||||||
|
</div>
|
||||||
|
<DeviceModal
|
||||||
|
v-if="visible"
|
||||||
|
:name="name"
|
||||||
|
:value="value"
|
||||||
|
@cancel="cancel"
|
||||||
|
@confirm="confirm"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script name="GateWayFormItem" setup>
|
||||||
|
import DeviceModal from './GateWayDeviceModal.vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
default: undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:value', 'update:name'])
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
|
||||||
|
const showModal = () =>{
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancel = () => {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirm = (select) => {
|
||||||
|
console.log(select)
|
||||||
|
emit('update:value', select.id)
|
||||||
|
emit('update:name', select.name)
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.gateway-form-item {
|
||||||
|
display: flex;
|
||||||
|
>span {
|
||||||
|
line-height: 32px;
|
||||||
|
max-width: calc(100% - 88px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -92,6 +92,17 @@
|
||||||
:filter-option="filterOption"
|
:filter-option="filterOption"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
|
<j-form-item
|
||||||
|
v-if="formData.provider === 'COLLECTOR_GATEWAY'"
|
||||||
|
:name="['configuration','deviceId']"
|
||||||
|
:rules="[{ required: true, message: '请选择网关设备'}]"
|
||||||
|
label="选择网关设备"
|
||||||
|
>
|
||||||
|
<GateWayFormItem
|
||||||
|
v-model:name="formData.configuration.deviceName"
|
||||||
|
v-model:value="formData.configuration.deviceId"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
v-if="formData.provider === 'OPC_UA'"
|
v-if="formData.provider === 'OPC_UA'"
|
||||||
label="安全模式"
|
label="安全模式"
|
||||||
|
@ -198,6 +209,7 @@ import type { FormInstance } from 'ant-design-vue';
|
||||||
import type { FormDataType } from '../type.d';
|
import type { FormDataType } from '../type.d';
|
||||||
import { cloneDeep, isArray } from 'lodash-es';
|
import { cloneDeep, isArray } from 'lodash-es';
|
||||||
import { protocolList } from '@/utils/consts';
|
import { protocolList } from '@/utils/consts';
|
||||||
|
import GateWayFormItem from "@/views/DataCollect/Channel/Save/GateWayFormItem.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
|
@ -222,7 +234,15 @@ const Options = ref({
|
||||||
const formData = ref<FormDataType>(cloneDeep(FormState));
|
const formData = ref<FormDataType>(cloneDeep(FormState));
|
||||||
|
|
||||||
const handleOk = async () => {
|
const handleOk = async () => {
|
||||||
const params = await formRef.value?.validate();
|
const params: any = await formRef.value?.validate();
|
||||||
|
if (params?.provider === 'COLLECTOR_GATEWAY') {
|
||||||
|
params.configuration.deviceName = formData.value.configuration.deviceName
|
||||||
|
}
|
||||||
|
|
||||||
|
params.circuitBreaker = {
|
||||||
|
type: 'Ignore'
|
||||||
|
}
|
||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const response = !id
|
const response = !id
|
||||||
? await save(params).catch(() => {})
|
? await save(params).catch(() => {})
|
||||||
|
@ -284,9 +304,9 @@ const getProvidersList = async () => {
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
const arr = resp.result
|
const arr = resp.result
|
||||||
.filter(
|
.filter(
|
||||||
(item: any) => item.id === 'modbus-tcp' || item.id === 'opc-ua',
|
(item: any) => ['GATEWAY', 'Modbus/TCP', 'opc-ua'].includes(item.name),
|
||||||
)
|
)
|
||||||
.map((it: any) => it.id);
|
.map((it: any) => it.name);
|
||||||
const providers: any = protocolList.filter((item: any) =>
|
const providers: any = protocolList.filter((item: any) =>
|
||||||
arr.includes(item.alias),
|
arr.includes(item.alias),
|
||||||
);
|
);
|
||||||
|
@ -302,7 +322,9 @@ getCertificateList();
|
||||||
watch(
|
watch(
|
||||||
() => props.data,
|
() => props.data,
|
||||||
(value) => {
|
(value) => {
|
||||||
if (value.id) formData.value = value as FormDataType;
|
if (value.id) {
|
||||||
|
formData.value = value as FormDataType;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true, deep: true },
|
{ immediate: true, deep: true },
|
||||||
);
|
);
|
||||||
|
|
|
@ -15,6 +15,8 @@ export const FormState: FormDataType = {
|
||||||
authType: 'anonymous',
|
authType: 'anonymous',
|
||||||
username: '',
|
username: '',
|
||||||
password: '',
|
password: '',
|
||||||
|
deviceId: undefined,
|
||||||
|
deviceName: undefined,
|
||||||
},
|
},
|
||||||
description: '',
|
description: '',
|
||||||
};
|
};
|
||||||
|
|
|
@ -143,11 +143,12 @@
|
||||||
<script lang="ts" setup name="DataCollectPage">
|
<script lang="ts" setup name="DataCollectPage">
|
||||||
import type { ActionsType } from '@/components/Table/index';
|
import type { ActionsType } from '@/components/Table/index';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
import { query, remove, update } from '@/api/data-collect/channel';
|
import { query, remove, update ,getProviders} from '@/api/data-collect/channel';
|
||||||
import { onlyMessage } from '@/utils/comm';
|
import { onlyMessage } from '@/utils/comm';
|
||||||
import { StatusColorEnum, updateStatus } from './data';
|
import { StatusColorEnum, updateStatus } from './data';
|
||||||
import { useMenuStore } from 'store/menu';
|
import { useMenuStore } from 'store/menu';
|
||||||
import Save from './Save/index.vue';
|
import Save from './Save/index.vue';
|
||||||
|
import { protocolList } from '@/utils/consts';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
const menuStory = useMenuStore();
|
const menuStory = useMenuStore();
|
||||||
|
@ -174,10 +175,20 @@ const columns = [
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
search: {
|
search: {
|
||||||
type: 'select',
|
type: 'select',
|
||||||
options: [
|
options: async() =>{
|
||||||
{ label: 'OPC_UA', value: 'OPC_UA' },
|
const resp: any = await getProviders();
|
||||||
{ label: 'MODBUS_TCP', value: 'MODBUS_TCP' },
|
if (resp.status === 200) {
|
||||||
],
|
const arr = resp.result
|
||||||
|
.filter(
|
||||||
|
(item: any) => ['GATEWAY', 'Modbus/TCP', 'opc-ua'].includes(item.name),
|
||||||
|
)
|
||||||
|
.map((it: any) => it.name);
|
||||||
|
const providers: any = protocolList.filter((item: any) =>
|
||||||
|
arr.includes(item.alias),
|
||||||
|
);
|
||||||
|
return providers
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -204,8 +215,7 @@ const columns = [
|
||||||
type: 'select',
|
type: 'select',
|
||||||
options: [
|
options: [
|
||||||
{ label: '运行中', value: 'running' },
|
{ label: '运行中', value: 'running' },
|
||||||
{ label: '部分错误', value: 'partialError' },
|
{ label: '已停止', value: 'stopped' },
|
||||||
{ label: '错误', value: 'failed' },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export interface ConfigurationType {
|
export interface ConfigurationType {
|
||||||
port: string | undefined;
|
port: string | undefined;
|
||||||
host: string | undefined;;
|
host: string | undefined;
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
|
@ -8,7 +8,8 @@ export interface ConfigurationType {
|
||||||
securityMode: string | undefined,
|
securityMode: string | undefined,
|
||||||
certId: string | undefined,
|
certId: string | undefined,
|
||||||
authType: string | undefined,
|
authType: string | undefined,
|
||||||
|
deviceId: string | undefined,
|
||||||
|
deviceName: string | undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormDataType {
|
export interface FormDataType {
|
||||||
|
|
|
@ -271,7 +271,7 @@ import {
|
||||||
import { ModBusRules, checkProviderData } from '../../data.ts';
|
import { ModBusRules, checkProviderData } from '../../data.ts';
|
||||||
import type { FormInstance } from 'ant-design-vue';
|
import type { FormInstance } from 'ant-design-vue';
|
||||||
import type { Rule } from 'ant-design-vue/lib/form';
|
import type { Rule } from 'ant-design-vue/lib/form';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import {cloneDeep, omit} from 'lodash-es';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
|
@ -347,6 +347,14 @@ const handleOk = async () => {
|
||||||
address: data?.pointKey,
|
address: data?.pointKey,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (props.data.provider === 'COLLECTOR_GATEWAY') {
|
||||||
|
const configuration = cloneDeep(params.configuration)
|
||||||
|
params.configuration = {
|
||||||
|
configuration: configuration,
|
||||||
|
interval: params.interval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const response = !id
|
const response = !id
|
||||||
? await savePointBatch(params).catch(() => {})
|
? await savePointBatch(params).catch(() => {})
|
||||||
|
@ -439,11 +447,20 @@ watch(
|
||||||
watch(
|
watch(
|
||||||
() => props.data,
|
() => props.data,
|
||||||
(value) => {
|
(value) => {
|
||||||
if (value.id && value.provider === 'MODBUS_TCP') {
|
if (value.id && ['MODBUS_TCP', 'COLLECTOR_GATEWAY'].includes(value.provider)) {
|
||||||
const _value: any = cloneDeep(value);
|
const _value: any = cloneDeep(value);
|
||||||
const { writeByteCount, byteCount } =
|
const { writeByteCount, byteCount } =
|
||||||
_value.configuration.parameter;
|
props.data.provider === 'COLLECTOR_GATEWAY' ? _value.configuration.configuration.parameter: _value.configuration.parameter;
|
||||||
|
|
||||||
|
if (props.data.provider === 'COLLECTOR_GATEWAY') {
|
||||||
|
formData.value = {
|
||||||
|
...omit(_value, ['configuration']),
|
||||||
|
..._value.configuration,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
formData.value = _value;
|
formData.value = _value;
|
||||||
|
}
|
||||||
|
|
||||||
if (!!_value.accessModes[0]?.value) {
|
if (!!_value.accessModes[0]?.value) {
|
||||||
formData.value.accessModes = value.accessModes.map(
|
formData.value.accessModes = value.accessModes.map(
|
||||||
(i: any) => i.value,
|
(i: any) => i.value,
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
checkable
|
checkable
|
||||||
@check="onCheck"
|
@check="onCheck"
|
||||||
:height="600"
|
:height="600"
|
||||||
|
:showLine="{ showLeafIcon: false }"
|
||||||
|
:show-icon="true"
|
||||||
>
|
>
|
||||||
<template #title="{ name, key }">
|
<template #title="{ name, key }">
|
||||||
<span
|
<span
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<j-space>
|
<j-space>
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
v-if="data?.provider == 'MODBUS_TCP'"
|
v-if="['MODBUS_TCP', 'COLLECTOR_GATEWAY'].includes(data?.provider)"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handlAdd"
|
@click="handlAdd"
|
||||||
hasPermission="DataCollect/Collector:add"
|
hasPermission="DataCollect/Collector:add"
|
||||||
|
@ -329,6 +329,8 @@ import {
|
||||||
batchDeletePoint,
|
batchDeletePoint,
|
||||||
removePoint,
|
removePoint,
|
||||||
readPoint,
|
readPoint,
|
||||||
|
getProviders,
|
||||||
|
getStates
|
||||||
} from '@/api/data-collect/collector';
|
} from '@/api/data-collect/collector';
|
||||||
import { onlyMessage } from '@/utils/comm';
|
import { onlyMessage } from '@/utils/comm';
|
||||||
import PointCardBox from './components/PointCardBox/index.vue';
|
import PointCardBox from './components/PointCardBox/index.vue';
|
||||||
|
@ -342,6 +344,7 @@ import { cloneDeep, isNumber, throttle } from 'lodash-es';
|
||||||
import { getWebSocket } from '@/utils/websocket';
|
import { getWebSocket } from '@/utils/websocket';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import { responsiveArray } from 'ant-design-vue/lib/_util/responsiveObserve';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
|
@ -409,16 +412,14 @@ const columns = [
|
||||||
key: 'provider',
|
key: 'provider',
|
||||||
search: {
|
search: {
|
||||||
type: 'select',
|
type: 'select',
|
||||||
options: [
|
options: async () =>{
|
||||||
{
|
const resp:any = await getProviders();
|
||||||
label: 'OPC_UA',
|
if(resp.success){
|
||||||
value: 'OPC_UA',
|
return resp.result.map((item: any) => ({ label: item.name, value: item.id }))
|
||||||
},
|
}else{
|
||||||
{
|
return []
|
||||||
label: 'MODBUS_TCP',
|
}
|
||||||
value: 'MODBUS_TCP',
|
}
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -436,16 +437,16 @@ const columns = [
|
||||||
key: 'runningState',
|
key: 'runningState',
|
||||||
search: {
|
search: {
|
||||||
type: 'select',
|
type: 'select',
|
||||||
options: [
|
options: async() =>{
|
||||||
{
|
const resq:any = await getStates();
|
||||||
label: '运行中',
|
if(resq.status === 200){
|
||||||
value: 'running',
|
return resq.result.map((item:any)=>(
|
||||||
},
|
{label:item.text,value:item.value}
|
||||||
{
|
))
|
||||||
label: '已停止',
|
}else{
|
||||||
value: 'stopped',
|
return []
|
||||||
},
|
}
|
||||||
],
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -542,7 +543,7 @@ const getQuantity = (item: Partial<Record<string, any>>) => {
|
||||||
};
|
};
|
||||||
const getAddress = (item: Partial<Record<string, any>>) => {
|
const getAddress = (item: Partial<Record<string, any>>) => {
|
||||||
const { address } = item.configuration?.parameter || '';
|
const { address } = item.configuration?.parameter || '';
|
||||||
return !!address ? address + '(地址)' : '';
|
return (!!address || address === 0) ? address + '(地址)' : '';
|
||||||
};
|
};
|
||||||
const getScaleFactor = (item: Partial<Record<string, any>>) => {
|
const getScaleFactor = (item: Partial<Record<string, any>>) => {
|
||||||
const { scaleFactor } = item.configuration?.codec?.configuration || '';
|
const { scaleFactor } = item.configuration?.codec?.configuration || '';
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
show-search
|
show-search
|
||||||
:filter-option="filterOption"
|
:filter-option="filterOption"
|
||||||
:disabled="!!id"
|
:disabled="!!id"
|
||||||
|
@select="channelSelect"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
|
@ -40,10 +41,27 @@
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
label="从机地址"
|
v-if="provider === 'COLLECTOR_GATEWAY'"
|
||||||
:name="['configuration', 'unitId']"
|
label="通讯协议"
|
||||||
|
:name="['collectorProvider']"
|
||||||
|
:rules="[{ required: true, message: '请选择通讯协议' }]"
|
||||||
|
>
|
||||||
|
<j-select
|
||||||
|
style="width: 100%"
|
||||||
|
v-model:value="formData.collectorProvider"
|
||||||
|
:options="providerListItems"
|
||||||
|
placeholder="请选择通讯协议"
|
||||||
|
allowClear
|
||||||
|
show-search
|
||||||
|
:filter-option="filterOption"
|
||||||
|
:disabled="!!id"
|
||||||
|
/>
|
||||||
|
</j-form-item>
|
||||||
|
<j-form-item
|
||||||
v-if="visibleUnitId"
|
v-if="visibleUnitId"
|
||||||
|
:name="['configuration', 'unitId']"
|
||||||
:rules="LeftTreeRules.unitId"
|
:rules="LeftTreeRules.unitId"
|
||||||
|
label="从机地址"
|
||||||
>
|
>
|
||||||
<j-input-number
|
<j-input-number
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
|
@ -54,6 +72,7 @@
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
|
v-if="provider !== 'COLLECTOR_GATEWAY'"
|
||||||
:name="['configuration', 'inheritBreakerSpec', 'type']"
|
:name="['configuration', 'inheritBreakerSpec', 'type']"
|
||||||
:rules="LeftTreeRules.type"
|
:rules="LeftTreeRules.type"
|
||||||
label="点位熔断处理"
|
label="点位熔断处理"
|
||||||
|
@ -69,14 +88,14 @@
|
||||||
@change="changeCardSelectType"
|
@change="changeCardSelectType"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<p style="color: #616161">
|
<p style="color: #616161" v-if="provider !== 'COLLECTOR_GATEWAY'">
|
||||||
{{ getTypeTooltip(formData.configuration.inheritBreakerSpec.type) }}
|
{{ getTypeTooltip(formData.configuration.inheritBreakerSpec.type) }}
|
||||||
</p>
|
</p>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
label="双字高低位切换"
|
|
||||||
:name="['configuration', 'endian']"
|
|
||||||
v-if="visibleEndian"
|
v-if="visibleEndian"
|
||||||
|
:name="['configuration', 'endian']"
|
||||||
:rules="LeftTreeRules.endian"
|
:rules="LeftTreeRules.endian"
|
||||||
|
label="双字高低位切换"
|
||||||
>
|
>
|
||||||
<j-card-select
|
<j-card-select
|
||||||
:showImage="false"
|
:showImage="false"
|
||||||
|
@ -90,10 +109,10 @@
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
label="单字高低位切换"
|
|
||||||
:name="['configuration', 'endianIn']"
|
|
||||||
v-if="visibleEndian"
|
v-if="visibleEndian"
|
||||||
|
:name="['configuration', 'endianIn']"
|
||||||
:rules="LeftTreeRules.endianIn"
|
:rules="LeftTreeRules.endianIn"
|
||||||
|
label="单字高低位切换"
|
||||||
>
|
>
|
||||||
<j-card-select
|
<j-card-select
|
||||||
:showImage="false"
|
:showImage="false"
|
||||||
|
@ -106,7 +125,7 @@
|
||||||
:column="2"
|
:column="2"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<div style="color: #616161" v-if="visibleEndian">
|
<div v-if="visibleEndian" style="color: #616161">
|
||||||
<p>当前内存布局: {{ endianData }}</p>
|
<p>当前内存布局: {{ endianData }}</p>
|
||||||
<p>
|
<p>
|
||||||
只有4字节数据类型(int32、ieee754 float)
|
只有4字节数据类型(int32、ieee754 float)
|
||||||
|
@ -114,19 +133,18 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
label="请求超时时间配置"
|
|
||||||
:name="['configuration', 'requsetTimeout']"
|
:name="['configuration', 'requsetTimeout']"
|
||||||
|
:rules="LeftTreeRules.requsetTimeout"
|
||||||
>
|
>
|
||||||
<j-input-number
|
<j-input-number
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
placeholder="请输入请求超时时间配置"
|
placeholder="请输入请求超时时间配置"
|
||||||
v-model:value="formData.configuration.requsetTimeout"
|
v-model:value="formData.configuration.requsetTimeout"
|
||||||
addon-after="ms"
|
addon-after="ms"
|
||||||
:max="2147483648"
|
:max="60000"
|
||||||
:min="1"
|
:min="2000"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
|
|
||||||
<j-form-item label="说明" name="description">
|
<j-form-item label="说明" name="description">
|
||||||
<j-textarea
|
<j-textarea
|
||||||
placeholder="请输入说明"
|
placeholder="请输入说明"
|
||||||
|
@ -154,11 +172,12 @@
|
||||||
</template>
|
</template>
|
||||||
</j-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" name="CollectorTreeSave" setup>
|
||||||
import { save, update } from '@/api/data-collect/collector';
|
import { save, update, getProviders } from '@/api/data-collect/collector';
|
||||||
import { LeftTreeRules } from '../../data';
|
import { LeftTreeRules } from '../../data';
|
||||||
import type { FormInstance } from 'ant-design-vue';
|
import type { FormInstance } from 'ant-design-vue';
|
||||||
import {cloneDeep} from "lodash-es";
|
import {cloneDeep, omit} from "lodash-es";
|
||||||
|
import {protocolList} from "@/utils/consts";
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const visibleEndian = ref(false);
|
const visibleEndian = ref(false);
|
||||||
|
@ -179,17 +198,40 @@ const emit = defineEmits(['change']);
|
||||||
|
|
||||||
const id = props.data.id;
|
const id = props.data.id;
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
|
const provider = ref()
|
||||||
|
const providerListItems = ref()
|
||||||
|
|
||||||
|
const geyProviderList = async () => {
|
||||||
|
const resp: any = await getProviders();
|
||||||
|
if (resp.success) {
|
||||||
|
providerListItems.value = resp.result.map((item: any) => ({ label: item.name, value: item.id }))
|
||||||
|
} else {
|
||||||
|
providerListItems.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const _channelListAll = computed(() => {
|
const _channelListAll = computed(() => {
|
||||||
return props.channelListAll || [];
|
return props.channelListAll || [];
|
||||||
})
|
})
|
||||||
|
|
||||||
const channelList = computed(() => {
|
const channelList = computed(() => {
|
||||||
return _channelListAll.value.map((item: any) => ({
|
const list:any = [];
|
||||||
|
_channelListAll.value.forEach((item: any) => {
|
||||||
|
if(item?.state?.value !== 'disabled'){
|
||||||
|
list.push({
|
||||||
|
provider: item.provider,
|
||||||
value: item.id,
|
value: item.id,
|
||||||
label: item.name,
|
label: item.name,
|
||||||
}));
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return list
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const channelSelect = (key: string, detail: any) => {
|
||||||
|
console.log(detail)
|
||||||
|
provider.value = detail.provider
|
||||||
|
}
|
||||||
|
|
||||||
const endianData = computed(() => {
|
const endianData = computed(() => {
|
||||||
const { endian, endianIn } = formData.value.configuration;
|
const { endian, endianIn } = formData.value.configuration;
|
||||||
|
@ -209,6 +251,7 @@ const endianData = computed(() => {
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
channelId: undefined,
|
channelId: undefined,
|
||||||
name: '',
|
name: '',
|
||||||
|
collectorProvider: undefined,
|
||||||
configuration: {
|
configuration: {
|
||||||
unitId: '',
|
unitId: '',
|
||||||
type: 'LowerFrequency',
|
type: 'LowerFrequency',
|
||||||
|
@ -227,25 +270,53 @@ const formData = ref({
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleOk = async () => {
|
const handleOk = async () => {
|
||||||
const data = await formRef.value?.validate();
|
const _data: any = await formRef.value?.validate();
|
||||||
|
|
||||||
const { provider, name } = _channelListAll.value.find(
|
if (_data) {
|
||||||
|
const { name } = _channelListAll.value.find(
|
||||||
(item: any) => item.id === formData.value.channelId,
|
(item: any) => item.id === formData.value.channelId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let _copyData = _data
|
||||||
|
|
||||||
|
if (['COLLECTOR_GATEWAY'].includes(provider.value)) {
|
||||||
|
const copyData = cloneDeep(_data)
|
||||||
|
_copyData = omit(copyData, ['configuration', 'collectorProvider'])
|
||||||
|
|
||||||
|
_copyData.configuration = {
|
||||||
|
configuration: {
|
||||||
|
..._data.configuration,
|
||||||
|
inheritBreakerSpec: {
|
||||||
|
type: 'Ignore'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
collectorProvider: _data.collectorProvider
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
...data,
|
..._copyData,
|
||||||
provider,
|
provider: provider.value,
|
||||||
channelName: name,
|
channelName: name,
|
||||||
|
circuitBreaker: {
|
||||||
|
type: 'Ignore'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
const response = !id
|
const response = !id
|
||||||
? await save(params).catch(() => { success: false })
|
? await save(params)
|
||||||
: await update(id, { ...props.data, ...params }).catch(() => { success: false });
|
: await update(id, { ...props.data, ...params })
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
emit('change', true);
|
emit('change', true);
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTypeTooltip = (value: string) => {
|
const getTypeTooltip = (value: string) => {
|
||||||
|
@ -253,7 +324,8 @@ const getTypeTooltip = (value: string) => {
|
||||||
case 'LowerFrequency': return '连续20次采集异常后,降低采集频率至设定频率的1/10,故障处理后,采集频率将恢复至设定频率。';
|
case 'LowerFrequency': return '连续20次采集异常后,降低采集频率至设定频率的1/10,故障处理后,采集频率将恢复至设定频率。';
|
||||||
// case 'Break': return '连续10分钟异常,停止采集数据进入断开状态,设备重新启用后恢复采集状态。'
|
// case 'Break': return '连续10分钟异常,停止采集数据进入断开状态,设备重新启用后恢复采集状态。'
|
||||||
case 'Break': return '连续20次采集异常后,降低采集频率至设定频率的1/10,10分钟内未排除故障,将停止采集。'
|
case 'Break': return '连续20次采集异常后,降低采集频率至设定频率的1/10,10分钟内未排除故障,将停止采集。'
|
||||||
default: return '忽略异常,保持设定采集频率。';
|
case 'Ignore': return '忽略异常,保持设定采集频率。';
|
||||||
|
default: return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,7 +352,7 @@ watch(
|
||||||
(value) => {
|
(value) => {
|
||||||
const dt = _channelListAll.value.find((item) => item.id === value);
|
const dt = _channelListAll.value.find((item) => item.id === value);
|
||||||
visibleUnitId.value = visibleEndian.value =
|
visibleUnitId.value = visibleEndian.value =
|
||||||
dt?.provider && dt?.provider === 'MODBUS_TCP';
|
dt?.provider && ['MODBUS_TCP', 'COLLECTOR_GATEWAY'].includes(dt?.provider);
|
||||||
},
|
},
|
||||||
{ deep: true },
|
{ deep: true },
|
||||||
);
|
);
|
||||||
|
@ -290,20 +362,37 @@ watch(
|
||||||
(value) => {
|
(value) => {
|
||||||
if (value.id) {
|
if (value.id) {
|
||||||
let copyValue = cloneDeep(value)
|
let copyValue = cloneDeep(value)
|
||||||
if (!copyValue?.configuration?.inheritBreakerSpec) {
|
if (!copyValue?.configuration?.inheritBreakerSpec && copyValue.provider !== 'COLLECTOR_GATEWAY') {
|
||||||
copyValue.configuration = {
|
copyValue.configuration = {
|
||||||
...copyValue.configuration,
|
...copyValue.configuration,
|
||||||
inheritBreakerSpec: {
|
inheritBreakerSpec: {
|
||||||
type: value.circuitBreaker.type
|
type: value.circuitBreaker?.type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
copyValue.circuitBreaker.type = 'Ignore'
|
copyValue.circuitBreaker.type = 'Ignore'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (copyValue.provider === 'COLLECTOR_GATEWAY') {
|
||||||
|
formData.value = {
|
||||||
|
...omit(copyValue, ['configuration']),
|
||||||
|
...copyValue.configuration,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
formData.value = copyValue
|
formData.value = copyValue
|
||||||
|
}
|
||||||
|
|
||||||
|
provider.value = copyValue?.provider
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{ immediate: true, deep: true },
|
{ immediate: true, deep: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (provider.value === 'COLLECTOR_GATEWAY') {
|
||||||
|
geyProviderList()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -31,30 +31,45 @@
|
||||||
:height="660"
|
:height="660"
|
||||||
@select='treeSelect'
|
@select='treeSelect'
|
||||||
defaultExpandAll
|
defaultExpandAll
|
||||||
|
:showLine="{ showLeafIcon: false }"
|
||||||
|
:show-icon="true"
|
||||||
>
|
>
|
||||||
<template #title="{ name, data }">
|
<template #title="{ name, data }">
|
||||||
<Ellipsis class="tree-left-title">
|
<Ellipsis class="tree-left-title">
|
||||||
{{ name }}
|
{{ name }}
|
||||||
</Ellipsis>
|
</Ellipsis>
|
||||||
|
<!-- <j-tag-->
|
||||||
|
<!-- class="tree-left-tag"-->
|
||||||
|
<!-- v-if="data.id !== '*'"-->
|
||||||
|
<!-- :color="-->
|
||||||
|
<!-- data?.uniformState?.value === 'normal' || data?.state?.value === 'disabled' ?-->
|
||||||
|
<!-- colorMap.get(data?.runningState?.value) :-->
|
||||||
|
<!-- colorMap.get(data?.uniformState?.value)-->
|
||||||
|
<!-- "-->
|
||||||
|
<!-- >{{-->
|
||||||
|
<!-- data?.uniformState?.value === 'normal' || data?.state?.value === 'disabled' ?-->
|
||||||
|
<!-- data?.runningState?.text :-->
|
||||||
|
<!-- data?.uniformState?.text-->
|
||||||
|
<!-- }}</j-tag-->
|
||||||
|
<!-- >-->
|
||||||
<j-tag
|
<j-tag
|
||||||
class="tree-left-tag"
|
class="tree-left-tag"
|
||||||
v-if="data.id !== '*'"
|
v-if="data.id !== '*'"
|
||||||
:color="
|
:color="colorMap.get(data?.uniformState?.value)"
|
||||||
data?.uniformState?.value === 'normal' || data?.state?.value === 'disabled' ?
|
>{{ data?.uniformState?.text }}</j-tag
|
||||||
colorMap.get(data?.runningState?.value) :
|
|
||||||
colorMap.get(data?.uniformState?.value)
|
|
||||||
"
|
|
||||||
>{{
|
|
||||||
data?.uniformState?.value === 'normal' || data?.state?.value === 'disabled' ?
|
|
||||||
data?.runningState?.text :
|
|
||||||
data?.uniformState?.text
|
|
||||||
}}</j-tag
|
|
||||||
>
|
>
|
||||||
<j-tag
|
<j-tag
|
||||||
class="tree-left-tag2"
|
class="tree-left-tag2"
|
||||||
v-if="data.id !== '*'"
|
v-if="data.id !== '*'"
|
||||||
:color="colorMap.get(data?.state?.value)"
|
:color="
|
||||||
>{{ data?.state?.text }}</j-tag
|
data?.state?.value === 'disabled' ? colorMap.get(data?.runningState?.value) :
|
||||||
|
colorMap.get(data?.state?.value)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
data?.state?.value === 'disabled' ? data?.state?.text : data?.runningState?.text
|
||||||
|
}}
|
||||||
|
</j-tag
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
v-if="data.id !== '*'"
|
v-if="data.id !== '*'"
|
||||||
|
@ -79,6 +94,7 @@
|
||||||
? '启用'
|
? '启用'
|
||||||
: '禁用',
|
: '禁用',
|
||||||
}"
|
}"
|
||||||
|
:disabled="data?.runningState?.value === 'stopped' && data?.state?.value!== 'disabled'"
|
||||||
hasPermission="DataCollect/Collector:action"
|
hasPermission="DataCollect/Collector:action"
|
||||||
:popConfirm="{
|
:popConfirm="{
|
||||||
title:
|
title:
|
||||||
|
|
|
@ -57,7 +57,7 @@ export const ModBusRules = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: regOnlyNumber,
|
pattern: regOnlyNumber,
|
||||||
message: '请输入0-999999999之间的正整数',
|
message: '请输入0-999999之间的正整数',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
quantity: [
|
quantity: [
|
||||||
|
@ -170,6 +170,9 @@ export const LeftTreeRules = {
|
||||||
endianIn: [
|
endianIn: [
|
||||||
{ required: true, message: '请选择单字高低位切换', trigger: 'blur' },
|
{ required: true, message: '请选择单字高低位切换', trigger: 'blur' },
|
||||||
],
|
],
|
||||||
|
requsetTimeout:[
|
||||||
|
{ pattern: /^\d+$/, message:'请输入2000-60000的正整数',trigger: 'change'}
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FormTableColumns = [
|
export const FormTableColumns = [
|
||||||
|
|
|
@ -83,7 +83,8 @@ const handleOptions = (x = [], y = []) => {
|
||||||
const chart: any = chartRef.value;
|
const chart: any = chartRef.value;
|
||||||
if (chart) {
|
if (chart) {
|
||||||
const myChart = echarts.init(chart);
|
const myChart = echarts.init(chart);
|
||||||
const maxY: number = y.sort((a,b)=>{
|
const _y = [...y];
|
||||||
|
const maxY: number = _y.sort((a,b)=>{
|
||||||
return b-a
|
return b-a
|
||||||
})?.[0]
|
})?.[0]
|
||||||
const options = {
|
const options = {
|
||||||
|
|
|
@ -12,54 +12,94 @@
|
||||||
</div>
|
</div>
|
||||||
<h1>1. 概述</h1>
|
<h1>1. 概述</h1>
|
||||||
<div>
|
<div>
|
||||||
DuerOS支持家居场景下的云端控制,该页面主要将平台的产品与DuerOS支持语音控制的产品进行映射,以到达小度平台控制本平台设备的目的。
|
DuerOS支持在家居场景下进行云端控制,该页面主要将平台的产品与DuerOS支持语音控制的产品进行映射,达到小度平台控制本平台设备的目的。
|
||||||
</div>
|
</div>
|
||||||
<h1>2. 操作步骤</h1>
|
<h1>2. 操作步骤</h1>
|
||||||
<div>
|
<div>
|
||||||
<h2>1、在百度小度技能平台创建技能,并授权。完成物联网平台与DuerOS的关联。</h2>
|
<div>1、在百度小度技能平台创建技能,并授权。</div>
|
||||||
<div class="image">
|
|
||||||
<j-image width="100%" :src="getImage('/cloud/dueros-doc.jpg')" />
|
|
||||||
</div>
|
|
||||||
<h1>授权地址</h1>
|
|
||||||
<div>物联网平台的登录地址。注意需要为https。</div>
|
|
||||||
<div>请复制并填写: https://{location.host}/#/user/login</div>
|
|
||||||
<h1>Client_Id</h1>
|
|
||||||
<div>请填写系统管理-应用管理中的clientId。</div>
|
|
||||||
<div class="image">
|
|
||||||
<j-image width="100%" :src="getImage('/cloud/dueros-doc1.png')" />
|
|
||||||
</div>
|
|
||||||
<h1>回调地址</h1>
|
|
||||||
<div>请复制DuerOS平台中的值,填写到系统管理-应用管理中-redirectUrl中。</div>
|
|
||||||
<div class="image">
|
|
||||||
<j-image width="100%" :src="getImage('/cloud/dueros-doc2.png')" />
|
|
||||||
</div>
|
|
||||||
<h1>Token地址</h1>
|
|
||||||
<div>请复制并填写:HTTPS://{location.host}/api/v1/token</div>
|
|
||||||
<h1>ClientSecret</h1>
|
|
||||||
<div>请复制系统管理-应用管理中的secureKey,填写到DuerOS平台。</div>
|
|
||||||
<div class="image">
|
|
||||||
<j-image width="100%" :src="getImage('/cloud/dueros-doc3.png')" />
|
|
||||||
</div>
|
|
||||||
<div></div>
|
|
||||||
<h1>WebService</h1>
|
|
||||||
<div>请复制并填写:/dueros/product/_query</div>
|
|
||||||
<h2>2、登录物联网平台,进行平台内产品与DuerOS产品的数据映射。</h2>
|
|
||||||
<h2>
|
|
||||||
3、智能家居用户通过物联网平台中的用户,登录小度APP,获取平台内当前用户的所属设备。获取后即可进行语音控制。
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<h1>3. 配置说明</h1>
|
|
||||||
<div>
|
<div>
|
||||||
<h2>
|
2、在物联网平台 系统管理--应用管理中配置应用完成与DuerOS的关联。
|
||||||
1、“设备类型”为DuerOS平台拟定的标准规范,设备类型将决定【动作映射】中“动作”的下拉选项,以及【属性映射】中“Dueros属性”的下拉选项
|
</div>
|
||||||
</h2>
|
<div class="image">
|
||||||
|
<j-image
|
||||||
|
width="100%"
|
||||||
|
:src="getImage('/cloud/dueros-doc.jpg')"
|
||||||
|
/>
|
||||||
|
<div class="desc">新建DuerOS</div>
|
||||||
|
</div>
|
||||||
|
<j-descriptions
|
||||||
|
bordered
|
||||||
|
size="small"
|
||||||
|
:column="1"
|
||||||
|
:labelStyle="{ width: '100px' }"
|
||||||
|
>
|
||||||
|
<j-descriptions-item label="参数">说明</j-descriptions-item>
|
||||||
|
<j-descriptions-item label="授权地址"
|
||||||
|
>物联网平台的登录地址http://host:port/JetLinks.cn</j-descriptions-item
|
||||||
|
>
|
||||||
|
<j-descriptions-item label="Client_Id">
|
||||||
|
请复制并填写物联网平台的appId
|
||||||
|
</j-descriptions-item>
|
||||||
|
<j-descriptions-item label="Scope">
|
||||||
|
以空格分割的权限列表,若不传递此参数,代表请求用户的默认权限
|
||||||
|
</j-descriptions-item>
|
||||||
|
<j-descriptions-item label="Token地址">
|
||||||
|
请复制并填写:HTTPS://host:port/api/v1/token
|
||||||
|
</j-descriptions-item>
|
||||||
|
<j-descriptions-item label="ClientSecret">
|
||||||
|
请复制并填写物联网平台的secureKey
|
||||||
|
</j-descriptions-item>
|
||||||
|
<j-descriptions-item label="WebService">
|
||||||
|
请复制并填写:/dueros/product/_query
|
||||||
|
</j-descriptions-item>
|
||||||
|
</j-descriptions>
|
||||||
|
<div class="image">
|
||||||
|
<j-image
|
||||||
|
width="100%"
|
||||||
|
:src="getImage('/cloud/dueros-doc1.png')"
|
||||||
|
/>
|
||||||
|
<div class="desc">新建应用</div>
|
||||||
|
</div>
|
||||||
|
<j-descriptions
|
||||||
|
bordered
|
||||||
|
size="small"
|
||||||
|
:column="1"
|
||||||
|
:labelStyle="{ width: '100px' }"
|
||||||
|
>
|
||||||
|
<j-descriptions-item label="参数">说明</j-descriptions-item>
|
||||||
|
<j-descriptions-item label="appId">
|
||||||
|
第三方应用唯一标识,物联网平台的自动生成
|
||||||
|
</j-descriptions-item>
|
||||||
|
<j-descriptions-item label="secureKey">
|
||||||
|
secureKey 第三方应用唯一标识匹配的秘钥,物联网平台的自动生成
|
||||||
|
</j-descriptions-item>
|
||||||
|
<j-descriptions-item label="角色">
|
||||||
|
为应用用户分配角色,根据绑定的角色,进行系统菜单赋权
|
||||||
|
</j-descriptions-item>
|
||||||
|
<j-descriptions-item label="组织">
|
||||||
|
为应用用户分配所属组织,根据绑定的组织,进行数据隔离
|
||||||
|
</j-descriptions-item>
|
||||||
|
<j-descriptions-item label="redirectUrl">
|
||||||
|
请复制并填写小度平台的回调地址
|
||||||
|
</j-descriptions-item>
|
||||||
|
<j-descriptions-item label="IP白名单">
|
||||||
|
允许指定IP地址访问
|
||||||
|
</j-descriptions-item>
|
||||||
|
</j-descriptions>
|
||||||
|
<div>3、登录物联网平台,进行平台内产品与DuerOS产品的数据映射。</div>
|
||||||
|
<div>
|
||||||
|
4、智能家居用户通过物联网平台中的用户,登录小度APP,获取平台内当前用户的所属设备。获取后即可进行语音控制。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h1>配置说明</h1>
|
||||||
|
<div>
|
||||||
|
“设备类型”为DuerOS平台拟定的标准规范,设备类型将决定【动作映射】中“动作”的下拉选项,以及【属性映射】中“Dueros属性”的下拉选项
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
@ -97,6 +137,13 @@ import { getImage } from '@/utils/comm';
|
||||||
|
|
||||||
.image {
|
.image {
|
||||||
margin: 16px 0;
|
margin: 16px 0;
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
color: rgb(138, 143, 141);
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,4 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
|
<j-scrollbar :height="`calc(100% - 51px)`">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<template v-if="bindList.length">
|
<template v-if="bindList.length">
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
<j-empty v-else style="margin: 200px 0" />
|
<j-empty v-else style="margin: 200px 0" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</j-scrollbar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|
|
@ -1,28 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="choose-view">
|
<div class="choose-view">
|
||||||
<div class="view-content">
|
<HomeView v-model:value="currentView" />
|
||||||
<div
|
|
||||||
:span="8"
|
|
||||||
class="select-item"
|
|
||||||
@click="currentView = 'device'"
|
|
||||||
>
|
|
||||||
<img :src="getImage(`/home/home-view/device${currentView === 'device' ? '-active' : ''}.png`)" alt="" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
:span="8"
|
|
||||||
class="select-item"
|
|
||||||
@click="currentView = 'ops'"
|
|
||||||
>
|
|
||||||
<img :src="getImage(`/home/home-view/ops${currentView === 'ops' ? '-active' : ''}.png`)" alt="" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
:span="8"
|
|
||||||
class="select-item"
|
|
||||||
@click="currentView = 'comprehensive'"
|
|
||||||
>
|
|
||||||
<img :src="getImage(`/home/home-view/comprehensive${currentView === 'comprehensive' ? '-active' : ''}.png`)" alt="" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="btn">
|
<div class="btn">
|
||||||
<j-button type="primary" @click="confirm">保存修改</j-button>
|
<j-button type="primary" @click="confirm">保存修改</j-button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,7 +9,8 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { getMe_api, getView_api, setView_api } from '@/api/home';
|
import { getMe_api, getView_api, setView_api } from '@/api/home';
|
||||||
import { getImage, onlyMessage } from '@/utils/comm';
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
import HomeView from '@/components/HomeView/index.vue';
|
||||||
|
|
||||||
const currentView = ref<string>('');
|
const currentView = ref<string>('');
|
||||||
const isApiUser = ref<boolean>();
|
const isApiUser = ref<boolean>();
|
||||||
|
@ -51,10 +30,13 @@ function getViews() {
|
||||||
})
|
})
|
||||||
.then((resp: any) => {
|
.then((resp: any) => {
|
||||||
if (resp?.status === 200) {
|
if (resp?.status === 200) {
|
||||||
if (resp.result) currentView.value = resp.result?.content;
|
if (resp.result) {
|
||||||
else if (resp.result?.username === 'admin') {
|
currentView.value = resp.result?.content;
|
||||||
|
} else if (resp.result?.username === 'admin') {
|
||||||
currentView.value = 'comprehensive';
|
currentView.value = 'comprehensive';
|
||||||
} else currentView.value = 'init';
|
} else {
|
||||||
|
currentView.value = 'device';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -76,19 +58,6 @@ onMounted(() => {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 4% 9% 0 9%;
|
padding: 4% 9% 0 9%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
.view-content {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
.select-item {
|
|
||||||
cursor: pointer;
|
|
||||||
width: 30%;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
:params="queryParams"
|
:params="queryParams"
|
||||||
:bodyStyle="{ padding: 0 }"
|
:bodyStyle="{ padding: 0 }"
|
||||||
:defaultParams="defaultParams"
|
:defaultParams="defaultParams"
|
||||||
|
:scroll="{y:420}"
|
||||||
>
|
>
|
||||||
<!-- <template #rightExtraRender>
|
<!-- <template #rightExtraRender>
|
||||||
<j-popconfirm title="确认全部已读?" @confirm="onAllRead">
|
<j-popconfirm title="确认全部已读?" @confirm="onAllRead">
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
|
<j-scrollbar :height="`calc(100% - 51px)`">
|
||||||
<j-spin :spinning="loading">
|
<j-spin :spinning="loading">
|
||||||
<div style="padding: 0 10px">
|
<div style="padding: 0 10px">
|
||||||
<div class="alert">
|
<div class="alert">
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</j-spin>
|
</j-spin>
|
||||||
|
</j-scrollbar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -53,9 +53,7 @@
|
||||||
:tab="item.title"
|
:tab="item.title"
|
||||||
/>
|
/>
|
||||||
</j-tabs>
|
</j-tabs>
|
||||||
<j-scrollbar :height="`calc(100% - 51px)`">
|
|
||||||
<component :is="tabs[user.tabKey]" />
|
<component :is="tabs[user.tabKey]" />
|
||||||
</j-scrollbar>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
<j-form-item label="名称" name="name">
|
<j-form-item label="名称" name="name">
|
||||||
<j-input
|
<j-input
|
||||||
v-model:value="formModel.name"
|
v-model:value="formModel.name"
|
||||||
:maxlength="64"
|
|
||||||
placeholder="请输入名称"
|
placeholder="请输入名称"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
|
@ -31,6 +30,7 @@
|
||||||
id="inputNumber"
|
id="inputNumber"
|
||||||
v-model:value="formModel.sortIndex"
|
v-model:value="formModel.sortIndex"
|
||||||
:min="1"
|
:min="1"
|
||||||
|
:max="9999"
|
||||||
placeholder="请输入排序"
|
placeholder="请输入排序"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
|
@ -102,7 +102,11 @@ const rules = ref({
|
||||||
message: '最多可输入64个字符',
|
message: '最多可输入64个字符',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
sortIndex: [{ required: true, message: '请输入排序', trigger: 'blur' }],
|
sortIndex: [{ required: true, message: '请输入排序', trigger: 'blur' },{
|
||||||
|
pattern:/^\d+$/,
|
||||||
|
message:'请输入正整数',
|
||||||
|
trigger:'change'
|
||||||
|
}],
|
||||||
});
|
});
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const { resetFields, validate, validateInfos } = useForm(
|
const { resetFields, validate, validateInfos } = useForm(
|
||||||
|
@ -174,7 +178,7 @@ const show = async (row: any) => {
|
||||||
childArr.value = row.children.sort(compare('sortIndex'));
|
childArr.value = row.children.sort(compare('sortIndex'));
|
||||||
formModel.value = {
|
formModel.value = {
|
||||||
name: '',
|
name: '',
|
||||||
sortIndex:
|
sortIndex:childArr.value[childArr.value.length - 1].sortIndex === 9999 ? childArr.value[childArr.value.length - 1].sortIndex :
|
||||||
childArr.value[childArr.value.length - 1].sortIndex + 1,
|
childArr.value[childArr.value.length - 1].sortIndex + 1,
|
||||||
description: '',
|
description: '',
|
||||||
};
|
};
|
||||||
|
@ -186,7 +190,7 @@ const show = async (row: any) => {
|
||||||
if (arr.value.length > 0) {
|
if (arr.value.length > 0) {
|
||||||
formModel.value = {
|
formModel.value = {
|
||||||
name: '',
|
name: '',
|
||||||
sortIndex: arr.value[arr.value.length - 1].sortIndex + 1,
|
sortIndex: arr.value[arr.value.length - 1].sortIndex === 9999 ? arr.value[arr.value.length - 1].sortIndex : arr.value[arr.value.length - 1].sortIndex + 1,
|
||||||
description: '',
|
description: '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,11 +107,15 @@ const query = reactive({
|
||||||
key: 'sortIndex',
|
key: 'sortIndex',
|
||||||
search: {
|
search: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
componentProps:{
|
||||||
|
precision:0,
|
||||||
|
min:1
|
||||||
|
}
|
||||||
},
|
},
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '描述',
|
title: '说明',
|
||||||
key: 'description',
|
key: 'description',
|
||||||
dataIndex: 'description',
|
dataIndex: 'description',
|
||||||
search: {
|
search: {
|
||||||
|
|
|
@ -150,7 +150,7 @@ const menuStore = useMenuStore();
|
||||||
* 获取产品数量
|
* 获取产品数量
|
||||||
*/
|
*/
|
||||||
const getProductData = () => {
|
const getProductData = () => {
|
||||||
if (menuStore.hasMenu('device/Product')) {
|
// if (menuStore.hasMenu('device/Product')) {
|
||||||
productCount().then((res) => {
|
productCount().then((res) => {
|
||||||
if (res.status == 200) {
|
if (res.status == 200) {
|
||||||
productTotal.value = res.result;
|
productTotal.value = res.result;
|
||||||
|
@ -180,14 +180,14 @@ const getProductData = () => {
|
||||||
productFooter.value[1].value = res.result;
|
productFooter.value[1].value = res.result;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
// }
|
||||||
};
|
};
|
||||||
getProductData();
|
getProductData();
|
||||||
/**
|
/**
|
||||||
* 获取设备数量
|
* 获取设备数量
|
||||||
*/
|
*/
|
||||||
const getDeviceData = () => {
|
const getDeviceData = () => {
|
||||||
if (menuStore.hasMenu('device/Instance')) {
|
// if (menuStore.hasMenu('device/Instance')) {
|
||||||
deviceCount().then((res) => {
|
deviceCount().then((res) => {
|
||||||
if (res.status == 200) {
|
if (res.status == 200) {
|
||||||
deviceTotal.value = res.result;
|
deviceTotal.value = res.result;
|
||||||
|
@ -206,7 +206,7 @@ const getDeviceData = () => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
// }
|
||||||
};
|
};
|
||||||
getDeviceData();
|
getDeviceData();
|
||||||
/**
|
/**
|
||||||
|
@ -214,7 +214,7 @@ getDeviceData();
|
||||||
*/
|
*/
|
||||||
const getOnline = () => {
|
const getOnline = () => {
|
||||||
const startTime = dayjs().subtract(0, 'days').startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
const startTime = dayjs().subtract(0, 'days').startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||||
const endTime = dayjs().subtract(0, 'days').endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
const endTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
|
||||||
dashboard([
|
dashboard([
|
||||||
{
|
{
|
||||||
|
@ -481,7 +481,7 @@ const setDevMesChartOption = (
|
||||||
//今日设备消息量
|
//今日设备消息量
|
||||||
const getDevice = () => {
|
const getDevice = () => {
|
||||||
const startTime = dayjs().subtract(0, 'days').startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
const startTime = dayjs().subtract(0, 'days').startOf('day').format('YYYY-MM-DD HH:mm:ss');
|
||||||
const endTime = dayjs().subtract(0, 'days').endOf('day').format('YYYY-MM-DD HH:mm:ss');
|
const endTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
|
||||||
dashboard([
|
dashboard([
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
:load-data="onLoadData"
|
:load-data="onLoadData"
|
||||||
@check="onCheck"
|
@check="onCheck"
|
||||||
v-model:expandedKeys="expandedKeys"
|
v-model:expandedKeys="expandedKeys"
|
||||||
|
:showLine="{ showLeafIcon: false }"
|
||||||
|
:show-icon="true"
|
||||||
/>
|
/>
|
||||||
</j-card>
|
</j-card>
|
||||||
<div style="width: 100px">
|
<div style="width: 100px">
|
||||||
|
|
|
@ -101,7 +101,9 @@
|
||||||
<j-col
|
<j-col
|
||||||
:span="24"
|
:span="24"
|
||||||
v-if="
|
v-if="
|
||||||
modelRef.type === 'INVOKE_FUNCTION' && modelRef.function && modelRef.inputs.length
|
modelRef.type === 'INVOKE_FUNCTION' &&
|
||||||
|
modelRef.function &&
|
||||||
|
modelRef.inputs.length
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<!-- <j-form-item
|
<!-- <j-form-item
|
||||||
|
@ -169,7 +171,7 @@ const funcChange = (val: string) => {
|
||||||
name: item.name,
|
name: item.name,
|
||||||
value: undefined,
|
value: undefined,
|
||||||
valueType: item?.valueType?.type,
|
valueType: item?.valueType?.type,
|
||||||
required: item?.expands?.required
|
required: item?.expands?.required,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
modelRef.inputs = list;
|
modelRef.inputs = list;
|
||||||
|
@ -177,22 +179,25 @@ const funcChange = (val: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveBtn = async () => {
|
const saveBtn = async () => {
|
||||||
const _inputs = await inputsRef.value.onSave();
|
const _data = await formRef.value?.validate();
|
||||||
console.log(_inputs)
|
if (!_data) return;
|
||||||
if(!_inputs){
|
|
||||||
return
|
|
||||||
}
|
|
||||||
formRef.value.validate().then(async () => {
|
|
||||||
const values = toRaw(modelRef);
|
const values = toRaw(modelRef);
|
||||||
let _inputs: any[] = [];
|
if (values.type === 'READ_PROPERTY') {
|
||||||
|
await readProperties(instanceStore.current?.id || '', [
|
||||||
|
values.properties,
|
||||||
|
]);
|
||||||
|
} else if (values.type === 'WRITE_PROPERTY') {
|
||||||
|
await settingProperties(instanceStore.current?.id || '', {
|
||||||
|
[values.properties || '']: values.propertyValue,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
if (modelRef.inputs.length) {
|
if (modelRef.inputs.length) {
|
||||||
_inputs = modelRef.inputs.filter((i: any) => !i.value && i?.required);
|
const _inputs = await inputsRef.value?.onSave();
|
||||||
if (_inputs.length) {
|
if (!_inputs) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.type === 'INVOKE_FUNCTION') {
|
|
||||||
const list = (modelRef?.inputs || [])?.filter((it: any) => !!it.value);
|
const list = (modelRef?.inputs || [])?.filter((it: any) => !!it.value);
|
||||||
const obj = {};
|
const obj = {};
|
||||||
list.map((it: any) => {
|
list.map((it: any) => {
|
||||||
|
@ -205,18 +210,7 @@ const saveBtn = async () => {
|
||||||
...obj,
|
...obj,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
if (values.type === 'READ_PROPERTY') {
|
|
||||||
await readProperties(instanceStore.current?.id || '', [
|
|
||||||
values.properties,
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
await settingProperties(instanceStore.current?.id || '', {
|
|
||||||
[values.properties || '']: values.propertyValue,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
defineExpose({ saveBtn });
|
defineExpose({ saveBtn });
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
:load-data="onLoadData"
|
:load-data="onLoadData"
|
||||||
@check="onCheck"
|
@check="onCheck"
|
||||||
v-model:expandedKeys="expandedKeys"
|
v-model:expandedKeys="expandedKeys"
|
||||||
|
:showLine="{ showLeafIcon: false }"
|
||||||
|
:show-icon="true"
|
||||||
/>
|
/>
|
||||||
</j-card>
|
</j-card>
|
||||||
<div style="width: 100px">
|
<div style="width: 100px">
|
||||||
|
|
|
@ -29,7 +29,7 @@ const menuStory = useMenuStore();
|
||||||
const instanceStore = useInstanceStore();
|
const instanceStore = useInstanceStore();
|
||||||
// const emits = defineEmits(['onJump']);
|
// const emits = defineEmits(['onJump']);
|
||||||
|
|
||||||
const metadata = computed(() => JSON.parse(instanceStore.detail.metadata));
|
const metadata = computed(() => JSON.parse(instanceStore.detail?.metadata || '{}'));
|
||||||
|
|
||||||
const activeKey = ref('Simple');
|
const activeKey = ref('Simple');
|
||||||
const tabs = {
|
const tabs = {
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<template>
|
||||||
|
<EditTable provider="collector-gateway" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import EditTable from '../components/EditTable/index.vue'
|
||||||
|
</script>
|
|
@ -19,6 +19,8 @@
|
||||||
:tree-data="dataSource"
|
:tree-data="dataSource"
|
||||||
:checkedKeys="checkedKeys"
|
:checkedKeys="checkedKeys"
|
||||||
@check="onCheck"
|
@check="onCheck"
|
||||||
|
:showLine="{ showLeafIcon: false }"
|
||||||
|
:show-icon="true"
|
||||||
/>
|
/>
|
||||||
</j-card>
|
</j-card>
|
||||||
<div style="width: 100px">
|
<div style="width: 100px">
|
||||||
|
@ -104,12 +106,12 @@ const handleData = (data: any[], type: string) => {
|
||||||
const handleSearch = async () => {
|
const handleSearch = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const resp = await treeMapping({
|
const resp = await treeMapping({
|
||||||
terms: [
|
// terms: [
|
||||||
{
|
// {
|
||||||
column: 'provider',
|
// column: 'provider',
|
||||||
value: _props.type,
|
// value: _props.type,
|
||||||
},
|
// },
|
||||||
],
|
// ],
|
||||||
});
|
});
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<j-spin :spinning="loading" v-if="metadata.properties.length">
|
<j-spin v-if="metadata.properties?.length" :spinning="loading">
|
||||||
<j-card :bordered="false" borderStyle="padding: 0">
|
<j-card :bordered="false" borderStyle="padding: 0">
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<j-space>
|
<j-space>
|
||||||
|
@ -28,13 +28,8 @@
|
||||||
placeholder="请选择"
|
placeholder="请选择"
|
||||||
allowClear
|
allowClear
|
||||||
:filter-option="filterOption"
|
:filter-option="filterOption"
|
||||||
>
|
:options="channelList"
|
||||||
<j-select-option
|
@select="(_, option) => { record.provider = option.provider }"
|
||||||
v-for="item in channelList"
|
|
||||||
:key="item.value"
|
|
||||||
:value="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
>{{ item.label }}</j-select-option
|
|
||||||
>
|
>
|
||||||
</j-select>
|
</j-select>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
|
@ -188,16 +183,16 @@ const visible = ref<boolean>(false);
|
||||||
const getChannel = async () => {
|
const getChannel = async () => {
|
||||||
const resp: any = await queryChannelNoPaging({
|
const resp: any = await queryChannelNoPaging({
|
||||||
paging: false,
|
paging: false,
|
||||||
terms: [
|
// terms: [
|
||||||
{
|
// {
|
||||||
terms: [
|
// terms: [
|
||||||
{
|
// {
|
||||||
column: 'provider',
|
// column: 'provider',
|
||||||
value: props.provider,
|
// value: props.provider,
|
||||||
},
|
// },
|
||||||
],
|
// ],
|
||||||
},
|
// },
|
||||||
],
|
// ],
|
||||||
});
|
});
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
channelList.value = resp.result?.map((item: any) => ({
|
channelList.value = resp.result?.map((item: any) => ({
|
||||||
|
@ -211,12 +206,12 @@ const getChannel = async () => {
|
||||||
const handleSearch = async () => {
|
const handleSearch = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
getChannel();
|
getChannel();
|
||||||
const _metadata = metadata.properties.map((item: any) => ({
|
const _metadata = metadata.properties?.map?.((item: any) => ({
|
||||||
metadataId: item.id,
|
metadataId: item.id,
|
||||||
metadataName: `${item.name}(${item.id})`,
|
metadataName: `${item.name}(${item.id})`,
|
||||||
metadataType: 'property',
|
metadataType: 'property',
|
||||||
name: item.name,
|
name: item.name,
|
||||||
}));
|
})) || [];
|
||||||
if (_metadata && _metadata.length) {
|
if (_metadata && _metadata.length) {
|
||||||
const resp: any = await queryMapping(
|
const resp: any = await queryMapping(
|
||||||
'device',
|
'device',
|
||||||
|
@ -269,6 +264,7 @@ const onSave = () => {
|
||||||
(i: any) => i.channelId,
|
(i: any) => i.channelId,
|
||||||
);
|
);
|
||||||
if (arr && arr.length !== 0) {
|
if (arr && arr.length !== 0) {
|
||||||
|
console.log(arr)
|
||||||
const resp = await saveMapping(
|
const resp = await saveMapping(
|
||||||
instanceStore.current.id,
|
instanceStore.current.id,
|
||||||
props.provider,
|
props.provider,
|
||||||
|
|
|
@ -122,6 +122,7 @@ import Modbus from './Modbus/index.vue';
|
||||||
import OPCUA from './OPCUA/index.vue';
|
import OPCUA from './OPCUA/index.vue';
|
||||||
import EdgeMap from './EdgeMap/index.vue';
|
import EdgeMap from './EdgeMap/index.vue';
|
||||||
import Parsing from './Parsing/index.vue';
|
import Parsing from './Parsing/index.vue';
|
||||||
|
import GateWay from './GateWay/index.vue'
|
||||||
import Log from './Log/index.vue';
|
import Log from './Log/index.vue';
|
||||||
import { _deploy, _disconnect } from '@/api/device/instance';
|
import { _deploy, _disconnect } from '@/api/device/instance';
|
||||||
import { getImage, onlyMessage } from '@/utils/comm';
|
import { getImage, onlyMessage } from '@/utils/comm';
|
||||||
|
@ -181,7 +182,8 @@ const tabs = {
|
||||||
EdgeMap,
|
EdgeMap,
|
||||||
Parsing,
|
Parsing,
|
||||||
Log,
|
Log,
|
||||||
MetadataMap
|
MetadataMap,
|
||||||
|
GateWay
|
||||||
};
|
};
|
||||||
|
|
||||||
const getStatus = (id: string) => {
|
const getStatus = (id: string) => {
|
||||||
|
@ -225,7 +227,7 @@ const getDetail = () => {
|
||||||
) {
|
) {
|
||||||
list.value.push({
|
list.value.push({
|
||||||
key: 'Modbus',
|
key: 'Modbus',
|
||||||
tab: 'Modbus TCP',
|
tab: '数采映射',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
|
@ -234,7 +236,16 @@ const getDetail = () => {
|
||||||
) {
|
) {
|
||||||
list.value.push({
|
list.value.push({
|
||||||
key: 'OPCUA',
|
key: 'OPCUA',
|
||||||
tab: 'OPC UA',
|
tab: '数采映射',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
instanceStore.current?.protocol === 'collector-gateway' &&
|
||||||
|
!keys.includes('GateWay')
|
||||||
|
) {
|
||||||
|
list.value.push({
|
||||||
|
key: 'GateWay',
|
||||||
|
tab: '数采映射',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -26,17 +26,21 @@
|
||||||
<j-descriptions-item label="设备类型">{{
|
<j-descriptions-item label="设备类型">{{
|
||||||
productStore.current.deviceType?.text
|
productStore.current.deviceType?.text
|
||||||
}}</j-descriptions-item>
|
}}</j-descriptions-item>
|
||||||
|
|
||||||
<j-descriptions-item label="接入方式">
|
<j-descriptions-item label="接入方式">
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
type="link"
|
type="link"
|
||||||
|
style="width:100%;padding:0"
|
||||||
@click="changeTables"
|
@click="changeTables"
|
||||||
hasPermission="device/Product:update"
|
hasPermission="device/Product:update"
|
||||||
>{{
|
>
|
||||||
|
<div style="white-space: normal">
|
||||||
|
<Ellipsis>{{
|
||||||
productStore.current.accessName
|
productStore.current.accessName
|
||||||
? productStore.current.accessName
|
? productStore.current.accessName
|
||||||
: '配置接入方式'
|
: '配置接入方式'
|
||||||
}}</PermissionButton
|
}}</Ellipsis>
|
||||||
|
</div>
|
||||||
|
</PermissionButton
|
||||||
>
|
>
|
||||||
</j-descriptions-item>
|
</j-descriptions-item>
|
||||||
<j-descriptions-item label="创建时间">{{
|
<j-descriptions-item label="创建时间">{{
|
||||||
|
|
|
@ -8,26 +8,28 @@
|
||||||
<template #title>
|
<template #title>
|
||||||
<div>
|
<div>
|
||||||
<div style="display: flex; align-items: center">
|
<div style="display: flex; align-items: center">
|
||||||
<a-tooltip>
|
<j-tooltip>
|
||||||
<template #title>{{
|
<template #title>{{
|
||||||
productStore.current.name
|
productStore.current.name
|
||||||
}}</template>
|
}}</template>
|
||||||
<div class="productDetailHead">
|
<div class="productDetailHead">
|
||||||
{{ productStore.current.name }}
|
{{ productStore.current.name }}
|
||||||
</div>
|
</div>
|
||||||
</a-tooltip>
|
</j-tooltip>
|
||||||
<div style="margin: -5px 0 0 20px">
|
<div style="margin: -5px 0 0 20px" v-if="permissionStore.hasPermission('device/Product:action')">
|
||||||
<j-popconfirm
|
<j-popconfirm
|
||||||
title="确认禁用"
|
title="确认禁用"
|
||||||
@confirm="handleUndeploy"
|
@confirm="handleUndeploy"
|
||||||
v-if="productStore.current.state === 1"
|
v-if="productStore.current.state === 1"
|
||||||
okText="确定"
|
okText="确定"
|
||||||
cancelText="取消"
|
cancelText="取消"
|
||||||
|
:disabled="!permissionStore.hasPermission('device/Product:action')"
|
||||||
>
|
>
|
||||||
<j-switch
|
<j-switch
|
||||||
:checked="productStore.current.state === 1"
|
:checked="productStore.current.state === 1"
|
||||||
checked-children="正常"
|
checked-children="正常"
|
||||||
un-checked-children="禁用"
|
un-checked-children="禁用"
|
||||||
|
:disabled="!permissionStore.hasPermission('device/Product:action')"
|
||||||
/>
|
/>
|
||||||
</j-popconfirm>
|
</j-popconfirm>
|
||||||
<j-popconfirm
|
<j-popconfirm
|
||||||
|
@ -36,6 +38,7 @@
|
||||||
v-if="productStore.current.state === 0"
|
v-if="productStore.current.state === 0"
|
||||||
okText="确定"
|
okText="确定"
|
||||||
cancelText="取消"
|
cancelText="取消"
|
||||||
|
:disabled="!permissionStore.hasPermission('device/Product:action')"
|
||||||
>
|
>
|
||||||
<j-switch
|
<j-switch
|
||||||
:unCheckedValue="
|
:unCheckedValue="
|
||||||
|
@ -43,9 +46,31 @@
|
||||||
"
|
"
|
||||||
checked-children="正常"
|
checked-children="正常"
|
||||||
un-checked-children="禁用"
|
un-checked-children="禁用"
|
||||||
|
:disabled="!permissionStore.hasPermission('device/Product:action')"
|
||||||
/>
|
/>
|
||||||
</j-popconfirm>
|
</j-popconfirm>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="margin: -5px 0 0 20px" v-else>
|
||||||
|
<j-tooltip>
|
||||||
|
<template #title>暂无权限,请联系管理员</template>
|
||||||
|
<j-switch
|
||||||
|
v-if="productStore.current.state === 1"
|
||||||
|
:checked="productStore.current.state === 1"
|
||||||
|
checked-children="正常"
|
||||||
|
un-checked-children="禁用"
|
||||||
|
:disabled="!permissionStore.hasPermission('device/Product:action')"
|
||||||
|
/>
|
||||||
|
<j-switch
|
||||||
|
v-if="productStore.current.state === 0"
|
||||||
|
:unCheckedValue="
|
||||||
|
productStore.current.state === 0
|
||||||
|
"
|
||||||
|
checked-children="正常"
|
||||||
|
un-checked-children="禁用"
|
||||||
|
:disabled="!permissionStore.hasPermission('device/Product:action')"
|
||||||
|
/>
|
||||||
|
</j-tooltip>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -87,6 +112,7 @@
|
||||||
: undefined
|
: undefined
|
||||||
"
|
"
|
||||||
hasPermission="device/Product:update"
|
hasPermission="device/Product:update"
|
||||||
|
placement="topRight"
|
||||||
>应用配置</PermissionButton
|
>应用配置</PermissionButton
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
|
@ -124,7 +150,9 @@ import { getImage, handleParamsToString, onlyMessage } from '@/utils/comm';
|
||||||
import { useMenuStore } from '@/store/menu';
|
import { useMenuStore } from '@/store/menu';
|
||||||
import { useRouterParams } from '@/utils/hooks/useParams';
|
import { useRouterParams } from '@/utils/hooks/useParams';
|
||||||
import {EventEmitter} from "@/utils/utils";
|
import {EventEmitter} from "@/utils/utils";
|
||||||
|
import { usePermissionStore } from '@/store/permission';
|
||||||
|
|
||||||
|
const permissionStore = usePermissionStore()
|
||||||
const menuStory = useMenuStore();
|
const menuStory = useMenuStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const checked = ref<boolean>(true);
|
const checked = ref<boolean>(true);
|
||||||
|
@ -169,7 +197,7 @@ const tabs = {
|
||||||
watch(
|
watch(
|
||||||
() => route.params.id,
|
() => route.params.id,
|
||||||
(newId) => {
|
(newId) => {
|
||||||
if (newId) {
|
if (newId && route.name === 'device/Product/Detail') {
|
||||||
productStore.reSet();
|
productStore.reSet();
|
||||||
productStore.tabActiveKey = 'Info';
|
productStore.tabActiveKey = 'Info';
|
||||||
productStore.refresh(newId as string);
|
productStore.refresh(newId as string);
|
||||||
|
|
|
@ -101,6 +101,10 @@ defineExpose({
|
||||||
}
|
}
|
||||||
.product-id {
|
.product-id {
|
||||||
margin: 10px 15px 10px 0px;
|
margin: 10px 15px 10px 0px;
|
||||||
|
max-width: 520px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow:ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.product-btn {
|
.product-btn {
|
||||||
margin: 10px 0px 10px 0;
|
margin: 10px 0px 10px 0;
|
||||||
|
|
|
@ -306,6 +306,7 @@ const getActions = (
|
||||||
|
|
||||||
icon: 'icon-xiazai',
|
icon: 'icon-xiazai',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
|
console.log(data);
|
||||||
const extra = omit(data, [
|
const extra = omit(data, [
|
||||||
'transportProtocol',
|
'transportProtocol',
|
||||||
'protocolName',
|
'protocolName',
|
||||||
|
@ -314,7 +315,7 @@ const getActions = (
|
||||||
'accessProvider',
|
'accessProvider',
|
||||||
'messageProtocol',
|
'messageProtocol',
|
||||||
]);
|
]);
|
||||||
downloadObject(extra, '产品');
|
downloadObject(extra, data.name+'产品');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -388,28 +389,37 @@ const beforeUpload = (file: any) => {
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
reader.onload = async (result) => {
|
reader.onload = async (result) => {
|
||||||
const text = result.target?.result;
|
const text = result.target?.result;
|
||||||
console.log('text: ', text);
|
// console.log('text: ', text);
|
||||||
|
// console.log(file);
|
||||||
if (!file.type.includes('json')) {
|
if (!file.type.includes('json')) {
|
||||||
onlyMessage('请上传json格式文件', 'error');
|
onlyMessage('请上传json格式文件', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if(!text){
|
||||||
|
onlyMessage('文件内容不能为空','error')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(text || '{}');
|
const data = JSON.parse(text);
|
||||||
// 设置导入的产品状态为未发布
|
// 设置导入的产品状态为未发布
|
||||||
data.state = 0;
|
data.state = 0;
|
||||||
|
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
onlyMessage('请上传json格式文件', 'error');
|
onlyMessage('请上传正确格式文件', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
delete data.state;
|
delete data.state;
|
||||||
|
if(!data?.name){
|
||||||
|
data.name = "产品" + Date.now();
|
||||||
|
}
|
||||||
const res = await updateDevice(data);
|
const res = await updateDevice(data);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
onlyMessage('操作成功');
|
onlyMessage('操作成功');
|
||||||
tableRef.value?.reload();
|
tableRef.value?.reload();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch(e) {
|
||||||
// onlyMessage('请上传json格式文件', 'error');
|
onlyMessage('请上传正确格式文件', 'error');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,8 +24,10 @@
|
||||||
title: hasOperate('add', type)
|
title: hasOperate('add', type)
|
||||||
? '当前的存储方式不支持新增'
|
? '当前的存储方式不支持新增'
|
||||||
: '新增',
|
: '新增',
|
||||||
|
getPopupContainer: getPopupContainer,
|
||||||
}"
|
}"
|
||||||
@click="handleAddClick()"
|
@click="handleAddClick()"
|
||||||
|
placement="topRight"
|
||||||
>
|
>
|
||||||
新增
|
新增
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
|
@ -42,8 +44,10 @@
|
||||||
? '当前的存储方式不支持新增'
|
? '当前的存储方式不支持新增'
|
||||||
: !editStatus ? '暂无改动数据': '保存',
|
: !editStatus ? '暂无改动数据': '保存',
|
||||||
placement: hasOperate('add', type) ? 'topRight' : 'top',
|
placement: hasOperate('add', type) ? 'topRight' : 'top',
|
||||||
|
getPopupContainer: getPopupContainer,
|
||||||
}"
|
}"
|
||||||
@click="handleSaveClick()"
|
@click="handleSaveClick()"
|
||||||
|
placement="topRight"
|
||||||
>
|
>
|
||||||
保存
|
保存
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
|
@ -92,11 +96,12 @@
|
||||||
v-else
|
v-else
|
||||||
v-model:value="data.record.expands"
|
v-model:value="data.record.expands"
|
||||||
:id="data.record.id"
|
:id="data.record.id"
|
||||||
:type="data.record.valueType.type"
|
|
||||||
:disabled="target === 'device' && productNoEdit.id?.includes?.(data.record._sortIndex)"
|
:disabled="target === 'device' && productNoEdit.id?.includes?.(data.record._sortIndex)"
|
||||||
|
:record="data.record"
|
||||||
:tooltip="target === 'device' && productNoEdit.id?.includes?.(data.record._sortIndex) ? {
|
:tooltip="target === 'device' && productNoEdit.id?.includes?.(data.record._sortIndex) ? {
|
||||||
title: '继承自产品物模型的数据不支持删除',
|
title: '继承自产品物模型的数据不支持删除',
|
||||||
} : undefined"
|
} : undefined"
|
||||||
|
:type="data.record.valueType.type"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #action="{data}">
|
<template #action="{data}">
|
||||||
|
@ -110,6 +115,7 @@
|
||||||
@click="copyItem(data.record, data.index)"
|
@click="copyItem(data.record, data.index)"
|
||||||
:tooltip="{
|
:tooltip="{
|
||||||
title: operateLimits('add', type) ? '当前的存储方式不支持复制' : '复制',
|
title: operateLimits('add', type) ? '当前的存储方式不支持复制' : '复制',
|
||||||
|
getPopupContainer: getPopupContainer,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<AIcon type="CopyOutlined" />
|
<AIcon type="CopyOutlined" />
|
||||||
|
@ -123,6 +129,7 @@
|
||||||
@click="handleAddClick(null, data.index)"
|
@click="handleAddClick(null, data.index)"
|
||||||
:tooltip="{
|
:tooltip="{
|
||||||
title: operateLimits('add', type) ? '当前的存储方式不支持新增' : '新增',
|
title: operateLimits('add', type) ? '当前的存储方式不支持新增' : '新增',
|
||||||
|
getPopupContainer: getPopupContainer,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<AIcon type="PlusSquareOutlined" />
|
<AIcon type="PlusSquareOutlined" />
|
||||||
|
@ -135,6 +142,7 @@
|
||||||
@click="showDetail(data.record)"
|
@click="showDetail(data.record)"
|
||||||
:tooltip="{
|
:tooltip="{
|
||||||
title: '详情',
|
title: '详情',
|
||||||
|
getPopupContainer: getPopupContainer,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<AIcon type="FileSearchOutlined" />
|
<AIcon type="FileSearchOutlined" />
|
||||||
|
@ -151,9 +159,11 @@
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
await removeItem(data.index);
|
await removeItem(data.index);
|
||||||
},
|
},
|
||||||
|
getPopupContainer: getPopupContainer
|
||||||
}"
|
}"
|
||||||
:tooltip="{
|
:tooltip="{
|
||||||
placement: 'topRight',
|
placement: 'topRight',
|
||||||
|
getPopupContainer: getPopupContainer,
|
||||||
title: target === 'device' && productNoEdit.id?.includes?.(data.record._sortIndex) ? '继承自产品物模型的数据不支持删除' :'删除',
|
title: target === 'device' && productNoEdit.id?.includes?.(data.record._sortIndex) ? '继承自产品物模型的数据不支持删除' :'删除',
|
||||||
}"
|
}"
|
||||||
:disabled="target === 'device' && productNoEdit.id?.includes?.(data.record._sortIndex)"
|
:disabled="target === 'device' && productNoEdit.id?.includes?.(data.record._sortIndex)"
|
||||||
|
@ -166,21 +176,25 @@
|
||||||
<PropertiesModal
|
<PropertiesModal
|
||||||
v-if="type === 'properties' && detailData.visible"
|
v-if="type === 'properties' && detailData.visible"
|
||||||
:data="detailData.data"
|
:data="detailData.data"
|
||||||
|
:getPopupContainer="getPopupContainer"
|
||||||
@cancel="cancelDetailModal"
|
@cancel="cancelDetailModal"
|
||||||
/>
|
/>
|
||||||
<FunctionModal
|
<FunctionModal
|
||||||
v-else-if="type === 'functions' && detailData.visible"
|
v-else-if="type === 'functions' && detailData.visible"
|
||||||
:data="detailData.data"
|
:data="detailData.data"
|
||||||
|
:getPopupContainer="getPopupContainer"
|
||||||
@cancel="cancelDetailModal"
|
@cancel="cancelDetailModal"
|
||||||
/>
|
/>
|
||||||
<EventModal
|
<EventModal
|
||||||
v-else-if="type === 'events' && detailData.visible"
|
v-else-if="type === 'events' && detailData.visible"
|
||||||
:data="detailData.data"
|
:data="detailData.data"
|
||||||
|
:getPopupContainer="getPopupContainer"
|
||||||
@cancel="cancelDetailModal"
|
@cancel="cancelDetailModal"
|
||||||
/>
|
/>
|
||||||
<TagsModal
|
<TagsModal
|
||||||
v-else-if="type === 'tags' && detailData.visible"
|
v-else-if="type === 'tags' && detailData.visible"
|
||||||
:data="detailData.data"
|
:data="detailData.data"
|
||||||
|
:getPopupContainer="getPopupContainer"
|
||||||
@cancel="cancelDetailModal"
|
@cancel="cancelDetailModal"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@ -209,10 +223,11 @@ import {omit} from "lodash-es";
|
||||||
import { PropertiesModal, FunctionModal, EventModal, TagsModal } from './DetailModal'
|
import { PropertiesModal, FunctionModal, EventModal, TagsModal } from './DetailModal'
|
||||||
import { Modal } from 'jetlinks-ui-components'
|
import { Modal } from 'jetlinks-ui-components'
|
||||||
import {EventEmitter} from "@/utils/utils";
|
import {EventEmitter} from "@/utils/utils";
|
||||||
import {watch} from "vue";
|
import {computed, watch} from "vue";
|
||||||
import {cloneDeep} from "lodash";
|
import {cloneDeep} from "lodash";
|
||||||
import {useSystem} from "store/system";
|
import {useSystem} from "store/system";
|
||||||
import {storeToRefs} from "pinia";
|
import {storeToRefs} from "pinia";
|
||||||
|
import { FULL_CODE } from 'jetlinks-ui-components/es/DataTable'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
target: {
|
target: {
|
||||||
|
@ -255,13 +270,23 @@ const detailData = reactive({
|
||||||
visible:false
|
visible:false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const showSave = ref(metadata.value.length !== 0)
|
const showSave = ref(metadata.value.length !== 0)
|
||||||
|
|
||||||
const showLastDelete = ref(false)
|
|
||||||
const dataSourceCache = ref<any[]>(metadata.value)
|
const dataSourceCache = ref<any[]>(metadata.value)
|
||||||
|
const fullRef = inject(FULL_CODE);
|
||||||
|
|
||||||
|
const getPopupContainer = (node: any) => {
|
||||||
|
const fullDom = tableRef.value?.fullRef?.()
|
||||||
|
return fullDom || node
|
||||||
|
}
|
||||||
|
|
||||||
|
const showLastDelete = computed(() => {
|
||||||
|
return dataSourceCache.value.length === 1
|
||||||
|
})
|
||||||
|
|
||||||
provide('_dataSource', dataSourceCache)
|
provide('_dataSource', dataSourceCache)
|
||||||
|
|
||||||
const showDetail = (data: any) => {
|
const showDetail = (data: any) => {
|
||||||
detailData.data = data
|
detailData.data = data
|
||||||
detailData.visible = true
|
detailData.visible = true
|
||||||
|
@ -344,15 +369,15 @@ const handleAddClick = async (_data?: any, index?: number) => {
|
||||||
const newObject = _data || getDataByType()
|
const newObject = _data || getDataByType()
|
||||||
|
|
||||||
const _addData = await tableRef.value.addItem(newObject, index)
|
const _addData = await tableRef.value.addItem(newObject, index)
|
||||||
if (_addData.length === 1) {
|
// if (_addData.length === 1) {
|
||||||
showLastDelete.value = true
|
// showLastDelete.value = true
|
||||||
}
|
// }
|
||||||
showSave.value = true
|
showSave.value = true
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyItem = (record: any, index: number) => {
|
const copyItem = (record: any, index: number) => {
|
||||||
const copyData = cloneDeep(omit(record, ['_uuid', '_sortIndex']))
|
const copyData = cloneDeep(omit(record, ['_uuid', '_sortIndex']))
|
||||||
copyData.id = `copy_${copyData.id}`.slice(0,64)
|
copyData.id = `copy_${copyData.id}`
|
||||||
handleAddClick(copyData, index)
|
handleAddClick(copyData, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,9 +386,9 @@ const removeItem = (index: number) => {
|
||||||
// data.splice(index, 1);
|
// data.splice(index, 1);
|
||||||
// dataSource.value = data
|
// dataSource.value = data
|
||||||
const _data = tableRef.value.removeItem(index)
|
const _data = tableRef.value.removeItem(index)
|
||||||
if (_data.length === 1) {
|
// if (_data.length === 1) {
|
||||||
showLastDelete.value = true
|
// showLastDelete.value = true
|
||||||
}
|
// }
|
||||||
if (_data.length === 0) {
|
if (_data.length === 0) {
|
||||||
showSave.value = false
|
showSave.value = false
|
||||||
|
|
||||||
|
@ -372,6 +397,7 @@ const removeItem = (index: number) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const editStatusChange = (status: boolean) => {
|
const editStatusChange = (status: boolean) => {
|
||||||
|
console.log('editStatusChange',status)
|
||||||
editStatus.value = status
|
editStatus.value = status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,6 +457,7 @@ const handleSaveClick = async (next?: Function) => {
|
||||||
if(result.success) {
|
if(result.success) {
|
||||||
dataSource.value = resp
|
dataSource.value = resp
|
||||||
tableRef.value.cleanEditStatus()
|
tableRef.value.cleanEditStatus()
|
||||||
|
editStatus.value = false
|
||||||
onlyMessage('操作成功!')
|
onlyMessage('操作成功!')
|
||||||
next?.()
|
next?.()
|
||||||
}
|
}
|
||||||
|
@ -476,7 +503,11 @@ watch(() => metadata.value, () => {
|
||||||
dataSource.value = metadata.value
|
dataSource.value = metadata.value
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
onBeforeRouteUpdate((to, from, next) => {
|
onBeforeRouteUpdate((to, from, next) => { // 设备管理内路由跳转
|
||||||
|
parentTabsChange(next as Function)
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeRouteLeave((to, from, next) => { // 设备管理外路由跳转
|
||||||
parentTabsChange(next as Function)
|
parentTabsChange(next as Function)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
title="事件详情"
|
title="事件详情"
|
||||||
width="650px"
|
width="650px"
|
||||||
|
:getContainer="getPopupContainer"
|
||||||
@cancel="cancel"
|
@cancel="cancel"
|
||||||
@ok="ok"
|
@ok="ok"
|
||||||
>
|
>
|
||||||
|
@ -36,6 +37,10 @@ const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
getPopupContainer: {
|
||||||
|
type: Function,
|
||||||
|
default: undefined
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
title="功能详情"
|
title="功能详情"
|
||||||
width="650px"
|
width="650px"
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
|
:getContainer="getPopupContainer"
|
||||||
@cancel="cancel"
|
@cancel="cancel"
|
||||||
@ok="ok"
|
@ok="ok"
|
||||||
>
|
>
|
||||||
|
@ -40,6 +41,10 @@ const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
getPopupContainer: {
|
||||||
|
type: Function,
|
||||||
|
default: undefined
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
visible
|
visible
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
title="属性详情"
|
title="属性详情"
|
||||||
|
:getContainer="getPopupContainer"
|
||||||
@cancel="cancel"
|
@cancel="cancel"
|
||||||
@ok="ok"
|
@ok="ok"
|
||||||
>
|
>
|
||||||
|
@ -67,6 +68,10 @@ const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
getPopupContainer: {
|
||||||
|
type: Function,
|
||||||
|
default: undefined
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
visible
|
visible
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
title="标签详情"
|
title="标签详情"
|
||||||
|
:getContainer="getPopupContainer"
|
||||||
@cancel="cancel"
|
@cancel="cancel"
|
||||||
@ok="ok"
|
@ok="ok"
|
||||||
>
|
>
|
||||||
|
@ -47,6 +48,10 @@ const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
getPopupContainer: {
|
||||||
|
type: Function,
|
||||||
|
default: undefined
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
{
|
{
|
||||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
message: '标识只能由数字、字母、下划线、中划线组成',
|
||||||
},
|
},
|
||||||
]">
|
]">
|
||||||
<j-input v-model:value="value.id" size="small" @change="asyncOtherConfig" :disabled="metadataStore.model.action === 'edit'" placeholder="请输入标识"></j-input>
|
<j-input v-model:value="value.id" size="small" @change="asyncOtherConfig" :disabled="metadataStore.model.action === 'edit'" placeholder="请输入标识"></j-input>
|
||||||
|
|
|
@ -38,6 +38,9 @@ const type = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const validatorConfig = (value: any, isObject: boolean = false) => {
|
export const validatorConfig = (value: any, isObject: boolean = false) => {
|
||||||
|
|
||||||
|
console.log(value)
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
@ -53,7 +56,7 @@ export const validatorConfig = (value: any, isObject: boolean = false) => {
|
||||||
return Promise.reject('请添加参数')
|
return Promise.reject('请添加参数')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.type === 'file' && !value.fileType) {
|
if (value.type === 'file' && (!value.fileType || !Object.keys(value.fileType).length)) {
|
||||||
return Promise.reject('请选择文件类型')
|
return Promise.reject('请选择文件类型')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +171,7 @@ export const useColumns = (type?: MetadataType, target?: 'device' | 'product', n
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
{
|
{
|
||||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
message: '标识只能由数字、字母、下划线、中划线组成',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -419,32 +422,28 @@ export const useColumns = (type?: MetadataType, target?: 'device' | 'product', n
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
doubleClick(record){
|
doubleClick(record){
|
||||||
if (target !== 'device') {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
if (record.expands.source === 'rule') {
|
if (record.expands.source === 'rule') {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return !isExtendsProdcut(record._sortIndex, productNoEdit?.value, 'expands')
|
return !isExtendsProdcut(record._sortIndex, productNoEdit?.value, 'expands')
|
||||||
}
|
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
required: true,
|
required: true,
|
||||||
rules: target !== 'device' ? [
|
rules: [
|
||||||
{
|
{
|
||||||
callback: async (rule: any, value: any, dataSource: any[]) => {
|
callback: async (rule: any, value: any, dataSource: any[]) => {
|
||||||
const field = rule.field.split('.')
|
const field = rule.field.split('.')
|
||||||
const fieldIndex = Number(field[1])
|
const fieldIndex = Number(field[1])
|
||||||
|
|
||||||
const values = dataSource.find((item, index) => index === fieldIndex)
|
const values = dataSource.find((item, index) => index === fieldIndex)
|
||||||
const virtualRule = values.elements?.virtualRule
|
const virtualRule = values.expands?.virtualRule
|
||||||
const source = value.source
|
const source = value.source
|
||||||
const ids = (noEdit?.value?.id || []) as any[]
|
const ids = (noEdit?.value?.id || []) as any[]
|
||||||
|
console.log(source, value)
|
||||||
if (source) {
|
if (source) {
|
||||||
if (source !== 'rule' && !value.type?.length) {
|
if (source === 'device' && !value.type?.length) {
|
||||||
return Promise.reject('请选择读写类型');
|
return Promise.reject('请选择读写类型');
|
||||||
} else if(!ids.includes(values.id) && virtualRule){
|
} else if( source === 'rule' && !virtualRule){
|
||||||
return Promise.reject('请配置规则');
|
return Promise.reject('请配置规则');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,7 +453,7 @@ export const useColumns = (type?: MetadataType, target?: 'device' | 'product', n
|
||||||
return Promise.reject('请选择属性来源');
|
return Promise.reject('请选择属性来源');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]: []
|
]
|
||||||
},
|
},
|
||||||
control(newValue, oldValue) {
|
control(newValue, oldValue) {
|
||||||
if (newValue && !oldValue) {
|
if (newValue && !oldValue) {
|
||||||
|
@ -542,12 +541,12 @@ export const useColumns = (type?: MetadataType, target?: 'device' | 'product', n
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
required: true,
|
required: true,
|
||||||
rules: [{
|
rules: [
|
||||||
|
{
|
||||||
callback(rule:any,value: any, dataSource: any[]) {
|
callback(rule:any,value: any, dataSource: any[]) {
|
||||||
const field = rule.field.split('.')
|
const field = rule.field.split('.')
|
||||||
const fieldIndex = Number(field[1])
|
const fieldIndex = Number(field[1])
|
||||||
const values = dataSource.find((item, index) => index === fieldIndex)
|
const values = dataSource.find((item, index) => index === fieldIndex)
|
||||||
|
|
||||||
if (!values?.expands?.type?.length) {
|
if (!values?.expands?.type?.length) {
|
||||||
return Promise.reject('请选择读写类型')
|
return Promise.reject('请选择读写类型')
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,6 +150,7 @@ const columns = [
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
width: 100,
|
width: 100,
|
||||||
|
placement: 'Left',
|
||||||
form: {
|
form: {
|
||||||
required: true,
|
required: true,
|
||||||
rules: [
|
rules: [
|
||||||
|
@ -170,7 +171,7 @@ const columns = [
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
{
|
{
|
||||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
message: '标识只能由数字、字母、下划线、中划线组成',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<j-select-boolean
|
<j-select
|
||||||
v-model:value="myValue"
|
v-model:value="myValue"
|
||||||
tureTitle="必填"
|
|
||||||
falseTitle='不必填'
|
|
||||||
style="width: 100%;"
|
style="width: 100%;"
|
||||||
|
:options="[
|
||||||
|
{ label: '不必填', value: 'false'},
|
||||||
|
{ label: '必填', value: 'true'},
|
||||||
|
]"
|
||||||
@change="change"
|
@change="change"
|
||||||
>
|
>
|
||||||
|
|
||||||
</j-select-boolean>
|
</j-select>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="ConstraintSelect">
|
<script setup name="ConstraintSelect">
|
||||||
|
@ -32,14 +34,14 @@ const myValue = ref()
|
||||||
|
|
||||||
const change = (e) => {
|
const change = (e) => {
|
||||||
const newData = { ...props.value }
|
const newData = { ...props.value }
|
||||||
set(newData, props.name, myValue.value)
|
set(newData, props.name, myValue.value === 'true')
|
||||||
console.log(newData, e);
|
console.log(newData, e);
|
||||||
emit('update:value', newData)
|
emit('update:value', newData)
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => JSON.stringify(props.data), () => {
|
watch(() => JSON.stringify(props.data), () => {
|
||||||
console.log(props.value, props.name);
|
console.log(props.value, props.name);
|
||||||
myValue.value = get(props.value, props.name) || false
|
myValue.value = get(props.value, props.name) === true ? 'true' : 'false'
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -86,6 +86,7 @@ const columns = [{
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
width: 100,
|
width: 100,
|
||||||
|
placement: 'Left',
|
||||||
form: {
|
form: {
|
||||||
required: true,
|
required: true,
|
||||||
rules: [{
|
rules: [{
|
||||||
|
@ -105,7 +106,7 @@ const columns = [{
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
{
|
{
|
||||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
message: '标识只能由数字、字母、下划线、中划线组成',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -191,6 +192,7 @@ watch(
|
||||||
() => {
|
() => {
|
||||||
type.value = props.value?.valueType?.type;
|
type.value = props.value?.valueType?.type;
|
||||||
_valueType.value = props.value?.valueType
|
_valueType.value = props.value?.valueType
|
||||||
|
console.log(props.value)
|
||||||
// elements.value = props.value?.valueType.elements;
|
// elements.value = props.value?.valueType.elements;
|
||||||
// if (['float', 'double', 'int', 'long'].includes(type.value)) {
|
// if (['float', 'double', 'int', 'long'].includes(type.value)) {
|
||||||
// const res = getUnit().then((res) => {
|
// const res = getUnit().then((res) => {
|
||||||
|
|
|
@ -27,6 +27,7 @@ const columns = [
|
||||||
title: '参数标识',
|
title: '参数标识',
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
placement: 'Left',
|
||||||
form: {
|
form: {
|
||||||
required: true,
|
required: true,
|
||||||
rules: [{
|
rules: [{
|
||||||
|
@ -46,7 +47,7 @@ const columns = [
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
{
|
{
|
||||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
message: '标识只能由数字、字母、下划线、中划线组成',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<span>{{ TypeStringMap[data.record.valueType?.type] }}</span>
|
<span>{{ TypeStringMap[data.record.valueType?.type] }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #required="{ data }">
|
<template #required="{ data }">
|
||||||
<span>{{ data.record.expands?.required ? "是": '否' }}</span>
|
<span>{{ data.record.expands?.required ? "必填": '不必填' }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #config="{ data }">
|
<template #config="{ data }">
|
||||||
<ConfigModal v-model:value="data.record.valueType" :showOther="false" />
|
<ConfigModal v-model:value="data.record.valueType" :showOther="false" />
|
||||||
|
@ -64,6 +64,7 @@ const columns = ref([
|
||||||
title: '参数标识',
|
title: '参数标识',
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
placement: 'Left',
|
||||||
form: {
|
form: {
|
||||||
required: true,
|
required: true,
|
||||||
rules: [
|
rules: [
|
||||||
|
@ -84,7 +85,7 @@ const columns = ref([
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
{
|
{
|
||||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
message: '标识只能由数字、字母、下划线、中划线组成',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="metadata-type">
|
<div class="metadata-type">
|
||||||
<div class="metadata-type-select">
|
<div class="metadata-type-select">
|
||||||
<DataTableTypeSelect v-model:value="type" @change="typeChange" />
|
<DataTableTypeSelect v-model:value="type" :allowClear="true" @change="typeChange" />
|
||||||
</div>
|
</div>
|
||||||
<DataTableArray
|
<DataTableArray
|
||||||
v-if="type === 'array'"
|
v-if="type === 'array'"
|
||||||
|
@ -101,6 +101,7 @@ const columns = [
|
||||||
title: '参数标识',
|
title: '参数标识',
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
placement: 'Left',
|
||||||
form: {
|
form: {
|
||||||
required: true,
|
required: true,
|
||||||
rules: [{
|
rules: [{
|
||||||
|
@ -120,7 +121,7 @@ const columns = [
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
{
|
{
|
||||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
message: '标识只能由数字、字母、下划线、中划线组成',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,9 @@
|
||||||
{{ data.record.range === true ? '范围值' : '固定值'}}
|
{{ data.record.range === true ? '范围值' : '固定值'}}
|
||||||
</template>
|
</template>
|
||||||
<template #value="{data}">
|
<template #value="{data}">
|
||||||
{{ data.record.range === true ? data.record.value?.join('-') : data.record.value }}
|
<j-ellipsis>
|
||||||
|
{{ data.record.range === true ? data.record.value?.join('-') : showText(data.record.value) }}
|
||||||
|
</j-ellipsis>
|
||||||
</template>
|
</template>
|
||||||
<template #action="{data}">
|
<template #action="{data}">
|
||||||
<j-button
|
<j-button
|
||||||
|
@ -43,6 +45,10 @@ const props = defineProps({
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
default: undefined
|
default: undefined
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -53,12 +59,31 @@ const tableRef = ref()
|
||||||
|
|
||||||
provide('metricsType', props.type)
|
provide('metricsType', props.type)
|
||||||
|
|
||||||
|
const showText = (value: any) => {
|
||||||
|
switch (props.type) {
|
||||||
|
case 'date':
|
||||||
|
return value;
|
||||||
|
case 'boolean':
|
||||||
|
const item = props.options.find(item => item.value === value)
|
||||||
|
if (item) {
|
||||||
|
return item.label
|
||||||
|
}else if (value) {
|
||||||
|
return value === 'true' ? '是' : '否'
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const columns: any = [
|
const columns: any = [
|
||||||
{
|
{
|
||||||
title: '指标标识',
|
title: '指标标识',
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
width: 120,
|
width: 120,
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
placement: 'Left',
|
||||||
form: {
|
form: {
|
||||||
required: true,
|
required: true,
|
||||||
rules: [{
|
rules: [{
|
||||||
|
@ -78,7 +103,7 @@ const columns: any = [
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
{
|
{
|
||||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
message: '标识只能由数字、字母、下划线、中划线组成',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -109,14 +134,17 @@ const columns: any = [
|
||||||
width: 100,
|
width: 100,
|
||||||
type: 'components',
|
type: 'components',
|
||||||
components: {
|
components: {
|
||||||
name: MetricValueItem
|
name: MetricValueItem,
|
||||||
|
props: {
|
||||||
|
options: props.options
|
||||||
|
}
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
required: true,
|
required: true,
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
callback(rule:any,value: any) {
|
callback(rule:any,value: any) {
|
||||||
if (!value) {
|
if (!value || (Array.isArray(value) && value.some(item => !item))) {
|
||||||
return Promise.reject('请配置指标')
|
return Promise.reject('请配置指标')
|
||||||
}
|
}
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="metrics-item-value">
|
<div class="metrics-item-value">
|
||||||
<div class="metrics-item-text">
|
<div class="metrics-item-text">
|
||||||
|
<j-ellipsis>
|
||||||
{{ showText }}
|
{{ showText }}
|
||||||
|
</j-ellipsis>
|
||||||
</div>
|
</div>
|
||||||
<j-popconfirm-modal
|
<j-popconfirm-modal
|
||||||
:show-cancel="false"
|
:show-cancel="false"
|
||||||
|
@ -11,8 +13,8 @@
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<j-form ref="formRef" :model="formData">
|
<j-form ref="formRef" :model="formData">
|
||||||
<j-form-item v-if="value.range === false" :rules="[{ required: true, message: '请输入指标值'}]" name="value">
|
<j-form-item v-if="value.range === false" :rules="[{ validator: typeValidator}]" name="value">
|
||||||
<Item v-model:value="formData.value" />
|
<Item v-model:value="formData.value" :options="options" />
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<div v-else class="data-table-boolean-item">
|
<div v-else class="data-table-boolean-item">
|
||||||
<div class="data-table-boolean-item--value">
|
<div class="data-table-boolean-item--value">
|
||||||
|
@ -41,6 +43,7 @@ import Item from './item.vue'
|
||||||
import {Form} from "jetlinks-ui-components";
|
import {Form} from "jetlinks-ui-components";
|
||||||
import {cloneDeep} from "lodash";
|
import {cloneDeep} from "lodash";
|
||||||
import { FULL_CODE } from 'jetlinks-ui-components/es/DataTable'
|
import { FULL_CODE } from 'jetlinks-ui-components/es/DataTable'
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
type ValueType = number | Array<number | undefined> | undefined;
|
type ValueType = number | Array<number | undefined> | undefined;
|
||||||
|
|
||||||
|
@ -53,12 +56,16 @@ const props = defineProps({
|
||||||
type: Object as PropType<any>,
|
type: Object as PropType<any>,
|
||||||
default: undefined,
|
default: undefined,
|
||||||
},
|
},
|
||||||
|
options: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<Emit>();
|
const emit = defineEmits<Emit>();
|
||||||
const formItemContext = Form.useInjectFormItemContext();
|
const formItemContext = Form.useInjectFormItemContext();
|
||||||
const fullRef = inject(FULL_CODE);
|
const fullRef = inject(FULL_CODE);
|
||||||
|
const type = inject<string>('metricsType')
|
||||||
|
|
||||||
const formData = reactive<{
|
const formData = reactive<{
|
||||||
value: ValueType;
|
value: ValueType;
|
||||||
|
@ -72,26 +79,81 @@ const formData = reactive<{
|
||||||
|
|
||||||
const formRef = ref()
|
const formRef = ref()
|
||||||
|
|
||||||
|
|
||||||
const showText = computed(() => {
|
const showText = computed(() => {
|
||||||
if (props.value.range === false) {
|
if (props.value.range === false) {
|
||||||
return props.value?.value || ''
|
switch (type) {
|
||||||
|
case 'date':
|
||||||
|
return props.value?.value;
|
||||||
|
case 'boolean':
|
||||||
|
const _value = props.value?.value
|
||||||
|
const item = props.options.find(item => item.value === props.value?.value)
|
||||||
|
if (item) {
|
||||||
|
return item.label
|
||||||
|
}else if (_value) {
|
||||||
|
return _value === 'true' ? '是' : '否'
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return props.value?.value
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return props.value?.value?.[0] ? props.value.value.join('-') : ''
|
return props.value?.value?.[0] ? props.value.value.join('-') : ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const validatorTip = () =>{
|
||||||
|
let tip = '请输入'
|
||||||
|
if (['boolean', 'date'].includes(type!)) {
|
||||||
|
tip = '请选择'
|
||||||
|
}
|
||||||
|
return `${tip}指标值`
|
||||||
|
}
|
||||||
|
|
||||||
const validator = (_: any, value: any) => {
|
const validator = (_: any, value: any) => {
|
||||||
|
|
||||||
if (props.value.range && formData.rangeValue![0] >= formData.rangeValue![1]) {
|
if (props.value.range && formData.rangeValue![0] >= formData.rangeValue![1]) {
|
||||||
return Promise.reject('需大于左侧数值')
|
return Promise.reject('需大于左侧数值')
|
||||||
}
|
}
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const typeValidator = (_: any, value: any) => {
|
||||||
|
if (value === undefined) {
|
||||||
|
return Promise.reject(validatorTip())
|
||||||
|
}
|
||||||
|
if (type === 'string' && value?.length > 64) {
|
||||||
|
return Promise.reject('最多可输入64个字符')
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleValueByType = (value: any, isRange: boolean = false) => {
|
||||||
|
if (isRange) {
|
||||||
|
return (value as number[]).map(item => {
|
||||||
|
const itemStr = String(item)
|
||||||
|
const index = String(item).indexOf('.')
|
||||||
|
|
||||||
|
return index === -1 ? item : Number(itemStr.substring(0, index))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const itemStr = String(value)
|
||||||
|
const index = String(value).indexOf('.')
|
||||||
|
return index === -1 ? value : Number(itemStr.substring(0, index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const confirm = () => {
|
const confirm = () => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
formRef.value.validate().then(() => {
|
formRef.value.validate().then(() => {
|
||||||
const value = props.value.range === true ? formData.rangeValue : formData.value
|
let value = props.value.range === true ? formData.rangeValue : formData.value
|
||||||
console.log('confirm',value, props.value)
|
|
||||||
|
if (['int', 'long'].includes(type)) {
|
||||||
|
value = handleValueByType(value, props.value.range)
|
||||||
|
console.log('confirm',value, type)
|
||||||
|
}
|
||||||
|
|
||||||
emit('update:value', {
|
emit('update:value', {
|
||||||
...props.value,
|
...props.value,
|
||||||
value: value
|
value: value
|
||||||
|
@ -121,6 +183,7 @@ watch(() => props.value.range,(value, oldValue) => {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
.metrics-item-text {
|
.metrics-item-text {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
<j-input
|
<j-input
|
||||||
v-if="type === 'string'"
|
v-if="type === 'string'"
|
||||||
v-model:value="myValue"
|
v-model:value="myValue"
|
||||||
:maxLength="64"
|
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
@change="change"
|
@change="change"
|
||||||
/>
|
/>
|
||||||
|
@ -11,7 +10,7 @@
|
||||||
v-model:value="myValue"
|
v-model:value="myValue"
|
||||||
:precision="0"
|
:precision="0"
|
||||||
:max="2147483647"
|
:max="2147483647"
|
||||||
:min="-2147483647"
|
:min="-2147483648"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
@change="change"
|
@change="change"
|
||||||
|
@ -19,8 +18,8 @@
|
||||||
<j-input-number
|
<j-input-number
|
||||||
v-else-if="type === 'long'"
|
v-else-if="type === 'long'"
|
||||||
v-model:value="myValue"
|
v-model:value="myValue"
|
||||||
:max="9223372036854775807"
|
:max="999999999999999"
|
||||||
:min="-9223372036854775808"
|
:min="-999999999999999"
|
||||||
:precision="0"
|
:precision="0"
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
|
@ -29,8 +28,8 @@
|
||||||
<j-input-number
|
<j-input-number
|
||||||
v-else-if="['float', 'double'].includes(type)"
|
v-else-if="['float', 'double'].includes(type)"
|
||||||
v-model:value="myValue"
|
v-model:value="myValue"
|
||||||
:max="9999999999999999"
|
:max="999999999999999"
|
||||||
:min="-9999999999999999"
|
:min="-999999999999999"
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
@change="change"
|
@change="change"
|
||||||
|
@ -38,18 +37,21 @@
|
||||||
<j-select
|
<j-select
|
||||||
v-else-if="type === 'boolean'"
|
v-else-if="type === 'boolean'"
|
||||||
placeholder="请选择"
|
placeholder="请选择"
|
||||||
:options="[
|
v-model:value="myValue"
|
||||||
{ label: '否', value: 'false'},
|
style="width: 100%"
|
||||||
{ label: '是', value: 'true'},
|
:options="options"
|
||||||
]"
|
|
||||||
:get-popup-container="(node) => fullRef || node"
|
:get-popup-container="(node) => fullRef || node"
|
||||||
|
@change="change"
|
||||||
/>
|
/>
|
||||||
<j-date-picker
|
<j-date-picker
|
||||||
v-else-if="type === 'date' "
|
v-else-if="type === 'date' "
|
||||||
v-model:value="myValue"
|
v-model:value="myValue"
|
||||||
show-time
|
show-time
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
style="width: 100%"
|
||||||
placeholder="请选择"
|
placeholder="请选择"
|
||||||
:get-popup-container="(node) => fullRef || node"
|
:get-popup-container="(node) => fullRef || node"
|
||||||
|
valueFormat="YYYY-MM-DD HH:mm:ss"
|
||||||
@change="change"
|
@change="change"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@ -61,6 +63,10 @@ const props = defineProps({
|
||||||
value: {
|
value: {
|
||||||
type: [String, Number, Array],
|
type: [String, Number, Array],
|
||||||
default: undefined
|
default: undefined
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -72,8 +78,8 @@ const myValue = ref(props.value)
|
||||||
const fullRef = inject(FULL_CODE);
|
const fullRef = inject(FULL_CODE);
|
||||||
|
|
||||||
const change = () => {
|
const change = () => {
|
||||||
// formItemContext.onFieldChange()
|
|
||||||
emit('update:value', myValue.value)
|
emit('update:value', myValue.value)
|
||||||
|
formItemContext.onFieldChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.value, () => {
|
watch(() => props.value, () => {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
@cancel="cancel"
|
@cancel="cancel"
|
||||||
@visibleChange="visibleChange"
|
@visibleChange="visibleChange"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template v-if="visible" #content>
|
||||||
<j-scrollbar height="350" v-if="showMetrics || config.length > 0">
|
<j-scrollbar height="350" v-if="showMetrics || config.length > 0">
|
||||||
<j-collapse v-model:activeKey="activeKey">
|
<j-collapse v-model:activeKey="activeKey">
|
||||||
<j-collapse-panel v-for="(item, index) in config" :key="'store_'+index" :header="item.name">
|
<j-collapse-panel v-for="(item, index) in config" :key="'store_'+index" :header="item.name">
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
<AIcon type="ExclamationCircleOutlined" style="padding-left: 12px;padding-top: 4px;" />
|
<AIcon type="ExclamationCircleOutlined" style="padding-left: 12px;padding-top: 4px;" />
|
||||||
</j-tooltip>
|
</j-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<Metrics ref="metricsRef" :value="myValue.metrics" :type="props.type"/>
|
<Metrics ref="metricsRef" :options="booleanOptions" :type="props.type" :value="myValue.metrics"/>
|
||||||
</j-collapse-panel>
|
</j-collapse-panel>
|
||||||
</j-collapse>
|
</j-collapse>
|
||||||
|
|
||||||
|
@ -84,6 +84,10 @@ const props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
default: undefined
|
default: undefined
|
||||||
},
|
},
|
||||||
|
record: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const fullRef = inject(FULL_CODE);
|
const fullRef = inject(FULL_CODE);
|
||||||
|
@ -109,6 +113,11 @@ const showMetrics = computed(() => {
|
||||||
return ['int', 'long', 'float', 'double', 'string', 'boolean', 'date'].includes(props.type as any)
|
return ['int', 'long', 'float', 'double', 'string', 'boolean', 'date'].includes(props.type as any)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const booleanOptions = ref([
|
||||||
|
{ label: '否', value: 'false'},
|
||||||
|
{ label: '是', value: 'true'},
|
||||||
|
])
|
||||||
|
|
||||||
const columns = ref([
|
const columns = ref([
|
||||||
{
|
{
|
||||||
title: '参数名称',
|
title: '参数名称',
|
||||||
|
@ -129,9 +138,16 @@ const columns = ref([
|
||||||
|
|
||||||
const getConfig = async () => {
|
const getConfig = async () => {
|
||||||
const id = type === 'product' ? productStore.current?.id : deviceStore.current.id
|
const id = type === 'product' ? productStore.current?.id : deviceStore.current.id
|
||||||
console.log(props.id, id, props.type)
|
console.log(props.id, id, props)
|
||||||
|
|
||||||
if(!props.id || !id || !props.type) return
|
if(!props.id || !id || !props.type) return
|
||||||
|
|
||||||
|
if (props.type === 'boolean') {
|
||||||
|
const booleanValue = props.record.valueType
|
||||||
|
booleanOptions.value[0] = { label: booleanValue.falseText || '否', value: booleanValue.falseValue || 'false'}
|
||||||
|
booleanOptions.value[1] = { label: booleanValue.trueText || '是', value: booleanValue.trueValue || 'true'}
|
||||||
|
}
|
||||||
|
|
||||||
const params: any = {
|
const params: any = {
|
||||||
deviceId: id,
|
deviceId: id,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -189,6 +205,7 @@ const confirm = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const visibleChange = (e: boolean) => {
|
const visibleChange = (e: boolean) => {
|
||||||
|
visible.value = e
|
||||||
if (e) {
|
if (e) {
|
||||||
configValue.value = omit(props.value, ['source', 'type', 'metrics', 'required'])
|
configValue.value = omit(props.value, ['source', 'type', 'metrics', 'required'])
|
||||||
getConfig()
|
getConfig()
|
||||||
|
@ -200,6 +217,7 @@ const cancel = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.value, () => {
|
watch(() => props.value, () => {
|
||||||
|
console.log(props.value)
|
||||||
myValue.value = cloneDeep(props.value)
|
myValue.value = cloneDeep(props.value)
|
||||||
}, {immediate: true, deep: true})
|
}, {immediate: true, deep: true})
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,13 @@
|
||||||
:get-popup-container="(node) => fullRef || node"
|
:get-popup-container="(node) => fullRef || node"
|
||||||
placement="topLeft"
|
placement="topLeft"
|
||||||
@confirm="confirm"
|
@confirm="confirm"
|
||||||
|
@visibleChange="visibleChange"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<j-scrollbar v-if="myValue">
|
<j-scrollbar v-if="myValue">
|
||||||
<div style="padding: 0 10px">
|
<div style="padding: 0 10px">
|
||||||
<VirtualRule
|
<VirtualRule
|
||||||
|
v-if="visible"
|
||||||
:value="value"
|
:value="value"
|
||||||
:source="myValue"
|
:source="myValue"
|
||||||
:dataSource="dataSource"
|
:dataSource="dataSource"
|
||||||
|
@ -103,11 +105,16 @@ const formItemContext = Form.useInjectFormItemContext();
|
||||||
const myValue = ref<SourceType>('');
|
const myValue = ref<SourceType>('');
|
||||||
const type = ref<string>('');
|
const type = ref<string>('');
|
||||||
const virtualRuleRef = ref<any>(null);
|
const virtualRuleRef = ref<any>(null);
|
||||||
|
const visible = ref(false)
|
||||||
|
|
||||||
|
const visibleChange = (e: boolean) => {
|
||||||
|
visible.value = e
|
||||||
|
}
|
||||||
|
|
||||||
const disabled = computed(() => {
|
const disabled = computed(() => {
|
||||||
if (props.target === 'device') {
|
// if (props.target === 'device') {
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
return props.noEdit?.length
|
return props.noEdit?.length
|
||||||
? props.noEdit.includes(props.value._sortIndex)
|
? props.noEdit.includes(props.value._sortIndex)
|
||||||
: false;
|
: false;
|
||||||
|
@ -149,6 +156,15 @@ const confirm = async () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const cancel = () => {
|
||||||
|
if (props.value.id && !props.value?.expands?.source) {
|
||||||
|
myValue.value = 'device';
|
||||||
|
} else {
|
||||||
|
myValue.value = props.value?.expands?.source || '';
|
||||||
|
}
|
||||||
|
type.value = props.value?.expands?.type || [];
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.value,
|
() => props.value,
|
||||||
() => {
|
() => {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<j-button @click="visible = true" style="width: 100%" type="dashed">
|
<j-button @click="visible = true" style="width: 100%" type="dashed">
|
||||||
编辑规则
|
编辑规则
|
||||||
</j-button>
|
</j-button>
|
||||||
<FRuleEditor :aggList="aggList" @close="onClose" v-if="visible" :value="value" @save="onChange" :id="id" :virtualRule="virtualRule" />
|
<FRuleEditor v-if="visible" :id="id" :aggList="aggList" :propertiesOptions="propertiesOptions" :value="value" :virtualRule="virtualRule" @close="onClose" @save="onChange" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="Rule">
|
<script setup lang="ts" name="Rule">
|
||||||
|
@ -21,7 +21,8 @@ const props = defineProps({
|
||||||
value: String,
|
value: String,
|
||||||
id: String,
|
id: String,
|
||||||
virtualRule: Object,
|
virtualRule: Object,
|
||||||
aggList: Array
|
aggList: Array,
|
||||||
|
propertiesOptions: Array
|
||||||
});
|
});
|
||||||
|
|
||||||
const visible = ref<boolean>(false);
|
const visible = ref<boolean>(false);
|
||||||
|
|
|
@ -6,7 +6,10 @@
|
||||||
:options="typeOptions"
|
:options="typeOptions"
|
||||||
/>
|
/>
|
||||||
<template v-if="source === 'rule'">
|
<template v-if="source === 'rule'">
|
||||||
<j-form-item :name="['virtualRule', 'triggerProperties']" required>
|
<j-form-item :name="['virtualRule', 'triggerProperties']" :rules="[{
|
||||||
|
required: true,
|
||||||
|
message: '请选择触发属性'
|
||||||
|
}]">
|
||||||
<template #label>
|
<template #label>
|
||||||
触发属性
|
触发属性
|
||||||
<j-tooltip>
|
<j-tooltip>
|
||||||
|
@ -23,7 +26,7 @@
|
||||||
<j-select
|
<j-select
|
||||||
v-model:value="formData.virtualRule.triggerProperties"
|
v-model:value="formData.virtualRule.triggerProperties"
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
placeholder="请选择属性"
|
placeholder="请选择触发属性"
|
||||||
show-search
|
show-search
|
||||||
max-tag-count="responsive"
|
max-tag-count="responsive"
|
||||||
>
|
>
|
||||||
|
@ -50,21 +53,25 @@
|
||||||
</j-select>
|
</j-select>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
:name="['virtualRule', 'rule', 'script']"
|
:name="['virtualRule', 'script']"
|
||||||
label="计算规则"
|
label="计算规则"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<Rule
|
<Rule
|
||||||
v-model:value="formData.virtualRule.script"
|
v-model:value="formData.virtualRule.script"
|
||||||
:virtualRule="_virtualRule.virtualRule"
|
:virtualRule="_virtualRule.virtualRule"
|
||||||
|
:propertiesOptions="options"
|
||||||
:id="value.id"
|
:id="value.id"
|
||||||
:aggList="aggList"
|
:aggList="aggList"
|
||||||
/>
|
/>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item
|
<j-form-item
|
||||||
label="窗口"
|
label="窗口"
|
||||||
:name="['virtualRule', 'rule', 'windowType']"
|
:name="['virtualRule', 'windowType']"
|
||||||
required
|
:rules="[{
|
||||||
|
required: true,
|
||||||
|
message: '请选择窗口类型'
|
||||||
|
}]"
|
||||||
>
|
>
|
||||||
<j-select
|
<j-select
|
||||||
v-model:value="formData.virtualRule.windowType"
|
v-model:value="formData.virtualRule.windowType"
|
||||||
|
@ -84,7 +91,10 @@
|
||||||
<j-form-item
|
<j-form-item
|
||||||
label="聚合函数"
|
label="聚合函数"
|
||||||
:name="['virtualRule', 'aggType']"
|
:name="['virtualRule', 'aggType']"
|
||||||
required
|
:rules="[{
|
||||||
|
required: true,
|
||||||
|
message: '请选择聚合函数'
|
||||||
|
}]"
|
||||||
>
|
>
|
||||||
<j-select
|
<j-select
|
||||||
v-model:value="formData.virtualRule.aggType"
|
v-model:value="formData.virtualRule.aggType"
|
||||||
|
@ -107,7 +117,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: /^\d+$/,
|
pattern: /^\d+$/,
|
||||||
message: '请输入0-999999之间的正整数',
|
message: '请输入1-999999之间的正整数',
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
|
@ -134,7 +144,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: /^\d+$/,
|
pattern: /^\d+$/,
|
||||||
message: '请输入0-999999之间的正整数',
|
message: '请输入1-999999之间的正整数',
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
|
@ -249,11 +259,10 @@ const options = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const setInitVirtualRule = () => {
|
const setInitVirtualRule = () => {
|
||||||
console.log(props.value?.expands?.virtualRule);
|
|
||||||
formData.virtualRule = {
|
formData.virtualRule = {
|
||||||
...initData,
|
...initData,
|
||||||
...(props.value?.expands?.virtualRule || {}),
|
...(props.value?.expands?.virtualRule || {}),
|
||||||
triggerProperties: props.value?.expands?.virtualRule?.triggerProperties || ['*'],
|
triggerProperties: props.value?.expands?.virtualRule?.triggerProperties?.length ? props.value?.expands?.virtualRule?.triggerProperties : ['*']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,8 +282,9 @@ const handleSearch = async () => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (resp && resp.status === 200 && resp.result) {
|
if (resp && resp.status === 200 && resp.result) {
|
||||||
|
const _triggerProperties = props.value?.expands?.virtualRule?.triggerProperties?.length ? props.value?.expands?.virtualRule?.triggerProperties : resp.result.triggerProperties
|
||||||
formData.virtualRule = {
|
formData.virtualRule = {
|
||||||
triggerProperties: resp.result.triggerProperties,
|
triggerProperties: _triggerProperties?.length ? _triggerProperties : ['*'],
|
||||||
...resp.result.rule,
|
...resp.result.rule,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -317,6 +327,7 @@ watch(
|
||||||
formData.virtualRule = initData;
|
formData.virtualRule = initData;
|
||||||
|
|
||||||
handleSearch();
|
handleSearch();
|
||||||
|
setInitVirtualRule()
|
||||||
} else {
|
} else {
|
||||||
formData.virtualRule = undefined;
|
formData.virtualRule = undefined;
|
||||||
}
|
}
|
||||||
|
@ -332,8 +343,8 @@ const _virtualRule = computed(() => {
|
||||||
return {
|
return {
|
||||||
type: formData?.type,
|
type: formData?.type,
|
||||||
virtualRule: {
|
virtualRule: {
|
||||||
type: flag ? 'window' : 'script',
|
|
||||||
...formData?.virtualRule,
|
...formData?.virtualRule,
|
||||||
|
type: flag ? 'window' : 'script',
|
||||||
isVirtualRule: flag,
|
isVirtualRule: flag,
|
||||||
triggerProperties: formData?.virtualRule?.triggerProperties.includes('*')
|
triggerProperties: formData?.virtualRule?.triggerProperties.includes('*')
|
||||||
? []
|
? []
|
||||||
|
|
|
@ -97,6 +97,7 @@ const handleConvertMetadata = (key: Key) => {
|
||||||
convertMetadata('to', 'alink', JSON.parse(metadata.value)).then(res => {
|
convertMetadata('to', 'alink', JSON.parse(metadata.value)).then(res => {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
value.value = JSON.stringify(res.result)
|
value.value = JSON.stringify(res.result)
|
||||||
|
monacoValue.value = JSON.stringify(res.result)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<j-modal :mask-closable="false" title="导入物模型" destroy-on-close v-model:visible="_visible" @cancel="close"
|
<j-modal
|
||||||
@ok="handleImport" :confirm-loading="loading">
|
:mask-closable="false"
|
||||||
|
title="导入物模型"
|
||||||
|
destroy-on-close
|
||||||
|
v-model:visible="_visible"
|
||||||
|
@cancel="close"
|
||||||
|
@ok="handleImport"
|
||||||
|
:confirm-loading="loading"
|
||||||
|
>
|
||||||
<div class="import-content">
|
<div class="import-content">
|
||||||
<p class="import-tip">
|
<p class="import-tip">
|
||||||
<AIcon type="ExclamationCircleOutlined" style="margin-right: 5px" />
|
<AIcon
|
||||||
|
type="ExclamationCircleOutlined"
|
||||||
|
style="margin-right: 5px"
|
||||||
|
/>
|
||||||
<template v-if="type === 'product'">
|
<template v-if="type === 'product'">
|
||||||
导入的物模型会覆盖原来的属性、功能、事件、标签,请谨慎操作。
|
导入的物模型会覆盖原来的属性、功能、事件、标签,请谨慎操作。
|
||||||
</template>
|
</template>
|
||||||
|
@ -12,83 +22,197 @@
|
||||||
</template>
|
</template>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<j-form layout="vertical" v-model="formModel">
|
<j-form layout="vertical" ref="formRef" :model="formModel">
|
||||||
<j-form-item v-if="type === 'product'" label="导入方式" v-bind="validateInfos.type">
|
<j-form-item
|
||||||
|
v-if="type === 'product'"
|
||||||
|
label="导入方式"
|
||||||
|
name="type"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择导入方式',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
<j-select v-model:value="formModel.type">
|
<j-select v-model:value="formModel.type">
|
||||||
<j-select-option value="copy">拷贝产品</j-select-option>
|
<j-select-option value="copy">拷贝产品</j-select-option>
|
||||||
<j-select-option value="import">导入物模型</j-select-option>
|
<j-select-option value="import">导入物模型</j-select-option>
|
||||||
</j-select>
|
</j-select>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item label="选择产品" v-bind="validateInfos.copy" v-if="formModel.type === 'copy'">
|
<j-form-item
|
||||||
<j-select :options="productList" v-model:value="formModel.copy" option-filter-prop="label" showSearch></j-select>
|
label="选择产品"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择产品',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
name="copy"
|
||||||
|
v-if="formModel.type === 'copy'"
|
||||||
|
>
|
||||||
|
<j-select
|
||||||
|
:options="productList"
|
||||||
|
v-model:value="formModel.copy"
|
||||||
|
option-filter-prop="label"
|
||||||
|
placeholder="请选择产品"
|
||||||
|
showSearch
|
||||||
|
></j-select>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item label="物模型类型" v-bind="validateInfos.metadata" v-if="type === 'device' || formModel.type === 'import'">
|
<j-form-item
|
||||||
|
label="物模型类型"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择物模型类型',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
name="metadata"
|
||||||
|
v-if="type === 'device' || formModel.type === 'import'"
|
||||||
|
>
|
||||||
<j-select v-model:value="formModel.metadata">
|
<j-select v-model:value="formModel.metadata">
|
||||||
<j-select-option value="jetlinks">Jetlinks物模型</j-select-option>
|
<j-select-option value="jetlinks"
|
||||||
<j-select-option value="alink">阿里云物模型TSL</j-select-option>
|
>Jetlinks物模型</j-select-option
|
||||||
|
>
|
||||||
|
<j-select-option value="alink"
|
||||||
|
>阿里云物模型TSL</j-select-option
|
||||||
|
>
|
||||||
</j-select>
|
</j-select>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item label="导入类型" v-bind="validateInfos.metadataType"
|
<j-form-item
|
||||||
v-if="type === 'device' || formModel.type === 'import'">
|
label="导入类型"
|
||||||
<j-select v-model:value="formModel.metadataType">
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择导入类型',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
name="metadataType"
|
||||||
|
v-if="type === 'device' || formModel.type === 'import'"
|
||||||
|
>
|
||||||
|
<j-select v-model:value="formModel.metadataType" @change="formModel.import = undefined">
|
||||||
<j-select-option value="file">文件上传</j-select-option>
|
<j-select-option value="file">文件上传</j-select-option>
|
||||||
<j-select-option value="script">脚本</j-select-option>
|
<j-select-option value="script">脚本</j-select-option>
|
||||||
</j-select>
|
</j-select>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item v-if="formModel.type === 'import' && formModel.metadataType === 'file'" label="文件上传" v-bind="validateInfos.upload">
|
<j-form-item
|
||||||
<j-input v-model:value="formModel.upload">
|
v-if="
|
||||||
|
formModel.type === 'import' &&
|
||||||
|
formModel.metadataType === 'file'
|
||||||
|
"
|
||||||
|
label="文件上传"
|
||||||
|
name="import"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请上传文件',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<!-- <j-input v-model:value="formModel.upload">
|
||||||
<template #addonAfter>
|
<template #addonAfter>
|
||||||
<j-upload v-model:file-list="fileList" :before-upload="beforeUpload" accept=".json" :show-upload-list="false"
|
<j-upload
|
||||||
:action="FILE_UPLOAD" @change="fileChange" :headers="{ 'X-Access-Token': getToken() }">
|
v-model:file-list="fileList"
|
||||||
<AIcon type="UploadOutlined" class="upload-button" />
|
:before-upload="beforeUpload"
|
||||||
|
accept=".json"
|
||||||
|
:show-upload-list="false"
|
||||||
|
:action="FILE_UPLOAD"
|
||||||
|
@change="fileChange"
|
||||||
|
:headers="{ 'X-Access-Token': getToken() }"
|
||||||
|
>
|
||||||
|
<AIcon
|
||||||
|
type="UploadOutlined"
|
||||||
|
class="upload-button"
|
||||||
|
/>
|
||||||
</j-upload>
|
</j-upload>
|
||||||
</template>
|
</template>
|
||||||
</j-input>
|
</j-input> -->
|
||||||
|
<j-upload
|
||||||
|
v-model:file-list="fileList"
|
||||||
|
:before-upload="beforeUpload"
|
||||||
|
accept=".json"
|
||||||
|
:show-upload-list="false"
|
||||||
|
:action="FILE_UPLOAD"
|
||||||
|
@change="fileChange"
|
||||||
|
:headers="{ 'X-Access-Token': getToken() }"
|
||||||
|
>
|
||||||
|
<j-button>
|
||||||
|
<template #icon><AIcon type="UploadOutlined" /></template>
|
||||||
|
上传文件
|
||||||
|
</j-button>
|
||||||
|
</j-upload>
|
||||||
|
<div style="margin-left: 10px; color: rgba(0, 0, 0, .6);">支持扩展名:.json</div>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
<j-form-item v-bind="validateInfos.import" v-if="(type === 'device' || formModel.type === 'import') && formModel.metadataType === 'script'">
|
<j-form-item
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入物模型',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
name="import"
|
||||||
|
v-if="
|
||||||
|
(type === 'device' || formModel.type === 'import') &&
|
||||||
|
formModel.metadataType === 'script'
|
||||||
|
"
|
||||||
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<j-space>
|
<j-space>
|
||||||
物模型
|
物模型
|
||||||
<j-tooltip title="在线编辑器中编写物模型脚本">
|
<j-tooltip title="在线编辑器中编写物模型脚本">
|
||||||
<AIcon type="QuestionCircleOutlined" style="color: rgb(136, 136, 136);"/>
|
<AIcon
|
||||||
|
type="QuestionCircleOutlined"
|
||||||
|
style="color: rgb(136, 136, 136)"
|
||||||
|
/>
|
||||||
</j-tooltip>
|
</j-tooltip>
|
||||||
</j-space>
|
</j-space>
|
||||||
</template>
|
</template>
|
||||||
<JMonacoEditor v-model="formModel.import" theme="vs" style="height: 300px" lang="json"></JMonacoEditor>
|
<JMonacoEditor
|
||||||
|
v-model:modelValue="formModel.import"
|
||||||
|
theme="vs"
|
||||||
|
style="height: 300px"
|
||||||
|
lang="json"
|
||||||
|
></JMonacoEditor>
|
||||||
</j-form-item>
|
</j-form-item>
|
||||||
</j-form>
|
</j-form>
|
||||||
</j-modal>
|
</j-modal>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts" name="Import">
|
<script setup lang="ts" name="Import">
|
||||||
import { useForm } from 'ant-design-vue/es/form';
|
import { saveMetadata } from '@/api/device/instance';
|
||||||
import { saveMetadata } from '@/api/device/instance'
|
import {
|
||||||
import { queryNoPagingPost, convertMetadata, modify } from '@/api/device/product'
|
queryNoPagingPost,
|
||||||
|
convertMetadata,
|
||||||
|
modify,
|
||||||
|
} from '@/api/device/product';
|
||||||
import type { DefaultOptionType } from 'ant-design-vue/es/select';
|
import type { DefaultOptionType } from 'ant-design-vue/es/select';
|
||||||
import type { UploadProps, UploadFile, UploadChangeParam } from 'ant-design-vue/es';
|
import type {
|
||||||
import type { DeviceMetadata } from '@/views/device/Product/typings'
|
UploadProps,
|
||||||
import { useInstanceStore } from '@/store/instance'
|
UploadFile,
|
||||||
|
UploadChangeParam,
|
||||||
|
} from 'ant-design-vue/es';
|
||||||
|
import type { DeviceMetadata } from '@/views/device/Product/typings';
|
||||||
|
import { useInstanceStore } from '@/store/instance';
|
||||||
import { useProductStore } from '@/store/product';
|
import { useProductStore } from '@/store/product';
|
||||||
import { FILE_UPLOAD } from '@/api/comm';
|
import { FILE_UPLOAD } from '@/api/comm';
|
||||||
import { getToken, onlyMessage } from '@/utils/comm';
|
import { getToken, onlyMessage } from '@/utils/comm';
|
||||||
import { useMetadataStore } from '@/store/metadata';
|
import { useMetadataStore } from '@/store/metadata';
|
||||||
import {omit} from "lodash-es";
|
import { omit } from 'lodash-es';
|
||||||
import { Modal } from 'jetlinks-ui-components'
|
import { Modal } from 'jetlinks-ui-components';
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute();
|
||||||
const instanceStore = useInstanceStore()
|
const instanceStore = useInstanceStore();
|
||||||
const productStore = useProductStore()
|
const productStore = useProductStore();
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
visible: boolean,
|
visible: boolean;
|
||||||
type: 'device' | 'product',
|
type: 'device' | 'product';
|
||||||
}
|
}
|
||||||
interface Emits {
|
interface Emits {
|
||||||
(e: 'update:visible', data: boolean): void;
|
(e: 'update:visible', data: boolean): void;
|
||||||
(e: 'submit', data: any): void;
|
(e: 'submit', data: any): void;
|
||||||
}
|
}
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>();
|
||||||
const emits = defineEmits<Emits>()
|
const emits = defineEmits<Emits>();
|
||||||
const loading = ref(false)
|
const loading = ref(false);
|
||||||
|
|
||||||
const _visible = computed({
|
const _visible = computed({
|
||||||
get: () => {
|
get: () => {
|
||||||
|
@ -97,206 +221,224 @@ const _visible = computed({
|
||||||
set: (val: any) => {
|
set: (val: any) => {
|
||||||
emits('update:visible', val);
|
emits('update:visible', val);
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
emits('update:visible', false);
|
emits('update:visible', false);
|
||||||
}
|
};
|
||||||
|
|
||||||
/** form表单 */
|
/** form表单 */
|
||||||
const formModel = reactive<Record<string, any>>({
|
const formModel = reactive<Record<string, any>>({
|
||||||
type: 'import',
|
type: 'import',
|
||||||
metadata: 'jetlinks',
|
metadata: 'jetlinks',
|
||||||
metadataType: 'script',
|
metadataType: 'script',
|
||||||
})
|
});
|
||||||
const rules = reactive({
|
// const { validate, validateInfos } = useForm(formModel, rules);
|
||||||
type: [
|
const fileList = ref<UploadFile[]>([]);
|
||||||
{
|
const hasVirtualRule = ref(false);
|
||||||
required: true,
|
const formRef = ref();
|
||||||
message: '请选择导入方式',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
copy: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请选择产品',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
metadata: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请选择物模型类型',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
metadataType: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请选择导入类型',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
upload: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请上传文件',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
import: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: '请输入物模型',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
const { validate, validateInfos } = useForm(formModel, rules);
|
|
||||||
const fileList = ref<UploadFile[]>([])
|
|
||||||
const hasVirtualRule = ref(false)
|
|
||||||
|
|
||||||
const productList = ref<DefaultOptionType[]>([])
|
const productList = ref<DefaultOptionType[]>([]);
|
||||||
|
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
const { id } = route.params || {}
|
const { id } = route.params || {};
|
||||||
const product = await queryNoPagingPost({
|
const product = (await queryNoPagingPost({
|
||||||
paging: false,
|
paging: false,
|
||||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||||
terms: [{ column: 'id$not', value: id }],
|
terms: [{ column: 'id$not', value: id }],
|
||||||
}) as any
|
})) as any;
|
||||||
productList.value = product.result.filter((i: any) => i?.metadata).map((item: any) => ({
|
productList.value = product.result
|
||||||
|
.filter((i: any) => i?.metadata)
|
||||||
|
.map((item: any) => ({
|
||||||
label: item.name,
|
label: item.name,
|
||||||
value: item.metadata,
|
value: item.metadata,
|
||||||
key: item.id
|
key: item.id,
|
||||||
})) as DefaultOptionType[]
|
})) as DefaultOptionType[];
|
||||||
}
|
};
|
||||||
loadData()
|
loadData();
|
||||||
|
|
||||||
const beforeUpload: UploadProps['beforeUpload'] = file => {
|
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
||||||
|
if(file.type === 'application/json') {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
reader.onload = (json) => {
|
reader.onload = (json) => {
|
||||||
|
if(json.target?.result){
|
||||||
|
onlyMessage('操作成功!')
|
||||||
formModel.import = json.target?.result;
|
formModel.import = json.target?.result;
|
||||||
};
|
} else {
|
||||||
|
onlyMessage('文件内容不能为空', 'error')
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
onlyMessage('请上传json格式的文件', 'error')
|
||||||
|
}
|
||||||
|
};
|
||||||
const fileChange = (info: UploadChangeParam) => {
|
const fileChange = (info: UploadChangeParam) => {
|
||||||
if (info.file.status === 'done') {
|
if (info.file.status === 'done') {
|
||||||
const { response } = info.file
|
const { response } = info.file;
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
formModel.upload = response.result
|
formModel.upload = response.result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const uniqArray = (arr: any[]) => {
|
||||||
|
const _map = new Map();
|
||||||
|
for(let item of arr) {
|
||||||
|
_map.set(item.id, item)
|
||||||
|
}
|
||||||
|
return [..._map.values()]
|
||||||
}
|
}
|
||||||
|
|
||||||
const operateLimits = (mdata: DeviceMetadata) => {
|
const operateLimits = (mdata: DeviceMetadata) => {
|
||||||
hasVirtualRule.value = false
|
hasVirtualRule.value = false;
|
||||||
const obj: DeviceMetadata = { ...mdata };
|
const obj: DeviceMetadata = { ...mdata };
|
||||||
const old = JSON.parse(instanceStore.detail?.metadata || '{}');
|
const old = JSON.parse((props.type === 'device' ? instanceStore.detail?.metadata : productStore.detail?.metadata) || '{}');
|
||||||
const fid = instanceStore.detail?.features?.map(item => item.id);
|
const fid = instanceStore.detail?.features?.map((item) => item.id);
|
||||||
|
const _data: DeviceMetadata = {
|
||||||
|
properties: [],
|
||||||
|
events: [],
|
||||||
|
functions: [],
|
||||||
|
tags: []
|
||||||
|
}
|
||||||
|
_data.properties = uniqArray([...(old?.properties || []), ...uniqArray(obj?.properties || [])])
|
||||||
|
_data.events = uniqArray([...(old?.events || []), ...uniqArray(obj?.events || [])])
|
||||||
|
_data.functions = uniqArray([...(old?.functions || []), ...uniqArray(obj?.functions || [])])
|
||||||
|
_data.tags = uniqArray([...(old?.tags || []), ...uniqArray(obj?.tags || [])])
|
||||||
|
|
||||||
if (fid?.includes('eventNotModifiable')) {
|
if (fid?.includes('eventNotModifiable')) {
|
||||||
obj.events = old?.events || [];
|
_data.events = old?.events || [];
|
||||||
}
|
}
|
||||||
if (fid?.includes('propertyNotModifiable')) {
|
if (fid?.includes('propertyNotModifiable')) {
|
||||||
obj.properties = old?.properties || [];
|
_data.properties = old?.properties || [];
|
||||||
}
|
}
|
||||||
(obj?.events || []).map((item, index) => {
|
|
||||||
return { ...item, sortsIndex: index };
|
(_data?.properties || []).map((item) => {
|
||||||
});
|
|
||||||
(obj?.properties || []).map((item, index) => {
|
|
||||||
if (item.expands?.source === 'rule') {
|
if (item.expands?.source === 'rule') {
|
||||||
hasVirtualRule.value = true
|
hasVirtualRule.value = true;
|
||||||
item.expands = omit(item.expands, ['virtualRule'])
|
item.expands = omit(item.expands, ['virtualRule']);
|
||||||
}
|
}
|
||||||
return { ...item, sortsIndex: index };
|
return item
|
||||||
});
|
});
|
||||||
(obj?.functions || []).map((item, index) => {
|
return _data;
|
||||||
return { ...item, sortsIndex: index };
|
|
||||||
});
|
|
||||||
(obj?.tags || []).map((item, index) => {
|
|
||||||
return { ...item, sortsIndex: index };
|
|
||||||
});
|
|
||||||
return obj;
|
|
||||||
};
|
};
|
||||||
const metadataStore = useMetadataStore()
|
const metadataStore = useMetadataStore();
|
||||||
|
|
||||||
const handleImport = async () => {
|
const handleImport = async () => {
|
||||||
validate().then(async (data) => {
|
formRef.value.validate().then(async (data: any) => {
|
||||||
loading.value = true
|
const { id } = route.params || {};
|
||||||
const { id } = route.params || {}
|
|
||||||
if (data.metadata === 'alink') {
|
if (data.metadata === 'alink') {
|
||||||
const res = await convertMetadata('from', 'alink', JSON.parse(data.import)).catch(err => err)
|
try {
|
||||||
|
const _import = JSON.parse(data.import);
|
||||||
|
loading.value = true;
|
||||||
|
const res = await convertMetadata(
|
||||||
|
'from',
|
||||||
|
'alink',
|
||||||
|
_import,
|
||||||
|
).catch((err) => err);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
const metadata = operateLimits(res.result)
|
const metadata = operateLimits(res.result);
|
||||||
let result;
|
let result;
|
||||||
if (props?.type === 'device') {
|
if (props?.type === 'device') {
|
||||||
result = await saveMetadata(id as string, metadata).catch(err => err)
|
result = await saveMetadata(
|
||||||
|
id as string,
|
||||||
|
metadata,
|
||||||
|
).catch((err) => err);
|
||||||
} else {
|
} else {
|
||||||
result = await modify(id as string, { id, metadata: JSON.stringify(metadata) }).catch(err => err)
|
result = await modify(id as string, {
|
||||||
|
id,
|
||||||
|
metadata: JSON.stringify(metadata),
|
||||||
|
}).catch((err) => err);
|
||||||
}
|
}
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
onlyMessage('导入成功')
|
onlyMessage('导入成功');
|
||||||
}
|
}
|
||||||
loading.value = false
|
loading.value = false;
|
||||||
} else {
|
} else {
|
||||||
loading.value = false
|
|
||||||
// onlyMessage('物模型数据不正确!', 'error')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (props?.type === 'device') {
|
|
||||||
await instanceStore.refresh(id as string)
|
|
||||||
} else {
|
|
||||||
await productStore.getDetail(id as string)
|
|
||||||
}
|
|
||||||
metadataStore.set('importMetadata', true)
|
|
||||||
// Store.set(SystemConst.GET_METADATA, true)
|
|
||||||
// Store.set(SystemConst.REFRESH_METADATA_TABLE, true)
|
|
||||||
close()
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
const _object = JSON.parse(data[props?.type === 'device' ? 'import' : data?.type] || '{}')
|
|
||||||
if (
|
|
||||||
!(!!_object?.properties || !!_object?.events || !!_object?.functions || !!_object?.tags)
|
|
||||||
) {
|
|
||||||
onlyMessage('物模型数据不正确', 'error')
|
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { id } = route.params || {}
|
if (props?.type === 'device') {
|
||||||
const copyOperateLimits = operateLimits(_object as DeviceMetadata)
|
await instanceStore.refresh(id as string);
|
||||||
|
} else {
|
||||||
|
await productStore.getDetail(id as string);
|
||||||
|
}
|
||||||
|
metadataStore.set('importMetadata', true);
|
||||||
|
close();
|
||||||
|
} catch (e) {
|
||||||
|
onlyMessage(
|
||||||
|
e === 'error'
|
||||||
|
? '物模型数据不正确'
|
||||||
|
: '上传json格式的物模型文件',
|
||||||
|
'error',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const _object = JSON.parse(
|
||||||
|
data[data?.type === 'copy' ? 'copy' : 'import'] ||
|
||||||
|
'{}',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
!!_object?.properties ||
|
||||||
|
!!_object?.events ||
|
||||||
|
!!_object?.functions ||
|
||||||
|
!!_object?.tags
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
onlyMessage('物模型数据不正确', 'error');
|
||||||
|
loading.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { id } = route.params || {};
|
||||||
|
const copyOperateLimits = operateLimits(
|
||||||
|
_object as DeviceMetadata,
|
||||||
|
);
|
||||||
const params = {
|
const params = {
|
||||||
id,
|
id,
|
||||||
metadata: JSON.stringify(copyOperateLimits),
|
metadata: JSON.stringify(copyOperateLimits),
|
||||||
};
|
};
|
||||||
const paramsDevice = copyOperateLimits
|
const paramsDevice = copyOperateLimits;
|
||||||
|
let resp = undefined;
|
||||||
let resp = undefined
|
loading.value = true;
|
||||||
if (props?.type === 'device') {
|
if (props?.type === 'device') {
|
||||||
resp = await saveMetadata(id as string, paramsDevice)
|
resp = await saveMetadata(id as string, paramsDevice);
|
||||||
} else {
|
} else {
|
||||||
resp = await modify(id as string, params)
|
resp = await modify(id as string, params);
|
||||||
}
|
}
|
||||||
loading.value = false
|
loading.value = false;
|
||||||
if (resp.success) {
|
if (resp.success) {
|
||||||
onlyMessage('导入成功')
|
onlyMessage('导入成功');
|
||||||
if (hasVirtualRule.value) {
|
if (hasVirtualRule.value) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
Modal.info({
|
Modal.info({
|
||||||
title: '导入数据存在虚拟属性,请及时添加虚拟属性计算规则。',
|
title: '导入数据存在虚拟属性,请及时添加虚拟属性计算规则。',
|
||||||
okText: '确认'
|
okText: '确认',
|
||||||
})
|
});
|
||||||
}, 300)
|
}, 300);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (props?.type === 'device') {
|
if (props?.type === 'device') {
|
||||||
await instanceStore.refresh(id as string)
|
await instanceStore.refresh(id as string);
|
||||||
} else {
|
} else {
|
||||||
await productStore.getDetail(id as string)
|
await productStore.getDetail(id as string);
|
||||||
}
|
}
|
||||||
metadataStore.set('importMetadata', true)
|
metadataStore.set('importMetadata', true);
|
||||||
close();
|
close();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loading.value = false
|
loading.value = false;
|
||||||
onlyMessage(e === 'error' ? '物模型数据不正确' : '上传json格式的物模型文件', 'error')
|
onlyMessage(
|
||||||
|
e === 'error'
|
||||||
|
? '物模型数据不正确'
|
||||||
|
: '上传json格式的物模型文件',
|
||||||
|
'error',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
// const showProduct = computed(() => formModel.type === 'copy')
|
// const showProduct = computed(() => formModel.type === 'copy')
|
||||||
</script>
|
</script>
|
||||||
|
|