Merge branch 'dev' into dev-hub

This commit is contained in:
jackhoo_98 2023-03-16 18:19:47 +08:00
commit b11d8eaa0d
69 changed files with 1663 additions and 1784 deletions

View File

@ -13,8 +13,10 @@ export default {
del: (id: string) => server.remove(`/media/device/${id}`), del: (id: string) => server.remove(`/media/device/${id}`),
// 更新通道 // 更新通道
updateChannels: (id: string) => server.post(`/media/device/${id}/channels/_sync`), updateChannels: (id: string) => server.post(`/media/device/${id}/channels/_sync`),
// 查询产品列表 // post查询产品列表
queryProductList: (data: any) => server.post<ProductType[]>(`/device/product/_query/no-paging`, data), queryProductList: (data: any) => server.post<ProductType[]>(`/device/product/_query/no-paging`, data),
// get获取产品
getProductList: (data: any) => server.get(`/device/product/_query/no-paging?paging=false`, data),
// 快速添加产品 // 快速添加产品
saveProduct: (data: any) => server.post<any>(`/device/product`, data), saveProduct: (data: any) => server.post<any>(`/device/product`, data),
// 产品发布 // 产品发布

View File

@ -14,7 +14,7 @@
> >
{{ slotProps.route.breadcrumbName }} {{ slotProps.route.breadcrumbName }}
</a> </a>
<span v-else >{{ slotProps.route.breadcrumbName }}</span> <span v-else style='cursor: pointer' >{{ slotProps.route.breadcrumbName }}</span>
</template> </template>
<template #rightContentRender> <template #rightContentRender>
<div class="right-content"> <div class="right-content">

View File

@ -4,6 +4,7 @@
:type='type' :type='type'
:request='saveSearchHistory' :request='saveSearchHistory'
:historyRequest='getSearchHistory' :historyRequest='getSearchHistory'
:deleteRequest='deleteSearchHistory'
:columns='columns' :columns='columns'
:class='props.class' :class='props.class'
@search='searchSubmit' @search='searchSubmit'
@ -13,7 +14,7 @@
<script setup lang='ts' name='ProSearch'> <script setup lang='ts' name='ProSearch'>
import { PropType } from 'vue' import { PropType } from 'vue'
import { JColumnsProps } from 'components/Table/types' import { JColumnsProps } from 'components/Table/types'
import { saveSearchHistory, getSearchHistory } from '@/api/comm' import { saveSearchHistory, getSearchHistory, deleteSearchHistory } from '@/api/comm'
interface Emit { interface Emit {
(e: 'search', data: any): void (e: 'search', data: any): void

View File

@ -89,6 +89,10 @@ const props: JUploadProps = defineProps({
type: String, type: String,
default: '', default: '',
}, },
accept:{
type:Array,
default:()=>[],
}
}); });
const loading = ref<boolean>(false); const loading = ref<boolean>(false);

View File

@ -86,8 +86,8 @@
@cancel="modalVis = false" @cancel="modalVis = false"
@ok="handleItemModalSubmit" @ok="handleItemModalSubmit"
> >
<div style="width: 100%; height: 400px"> <div style="width: 100%; height: 300px">
<MonacoEditor v-model:modelValue="objectValue" /> <JMonacoEditor v-model:modelValue="objectValue" />
</div> </div>
</j-modal> </j-modal>
</div> </div>
@ -97,7 +97,6 @@
import { PropType } from 'vue'; import { PropType } from 'vue';
import { UploadChangeParam, UploadFile } from 'ant-design-vue'; import { UploadChangeParam, UploadFile } from 'ant-design-vue';
import { DefaultOptionType } from 'ant-design-vue/lib/select'; import { DefaultOptionType } from 'ant-design-vue/lib/select';
import MonacoEditor from '@/components/MonacoEditor/index.vue';
import GeoComponent from '@/components/GeoComponent/index.vue'; import GeoComponent from '@/components/GeoComponent/index.vue';
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable'; import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
import { LocalStore } from '@/utils/comm'; import { LocalStore } from '@/utils/comm';
@ -196,7 +195,7 @@ const timeChange = (e: any) => {
} }
const inputChange = (e: any) => { const inputChange = (e: any) => {
emit('change', e.target.value) emit('change', e.target ? e.target.value : e)
} }
const dateChange = (e: any) => { const dateChange = (e: any) => {

View File

@ -1,5 +1,5 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import type { BranchesType, FormModelType, SceneItem } from '@/views/rule-engine/Scene/typings' import type { FormModelType, SceneItem } from '@/views/rule-engine/Scene/typings'
import { detail } from '@/api/rule-engine/scene' import { detail } from '@/api/rule-engine/scene'
import { cloneDeep, isArray } from 'lodash-es' import { cloneDeep, isArray } from 'lodash-es'
import { randomString } from '@/utils/utils' import { randomString } from '@/utils/utils'

View File

@ -206,6 +206,17 @@ const findDetailRoutes = (routes: any[]): any[] => {
export const findCodeRoute = (asyncRouterMap: any[]) => { export const findCodeRoute = (asyncRouterMap: any[]) => {
const routeMeta = {} const routeMeta = {}
function getDetail( code: string, url: string) {
const detail = findDetailRouteItem(code, url)
if (!detail) return
routeMeta[(detail as MenuItem).code] = {
path: detail.url,
title: detail.name,
parentName: code,
buttons: detail.buttons?.map((b: any) => b.id) || []
}
}
function findChildren (data: any[], code: string = '') { function findChildren (data: any[], code: string = '') {
data.forEach(route => { data.forEach(route => {
routeMeta[route.code] = { routeMeta[route.code] = {
@ -214,29 +225,24 @@ export const findCodeRoute = (asyncRouterMap: any[]) => {
parentName: code, parentName: code,
buttons: route.buttons?.map((b: any) => b.id) || [] buttons: route.buttons?.map((b: any) => b.id) || []
} }
const detail = findDetailRouteItem(route.code, route.url)
if (detail) {
routeMeta[(detail as MenuItem).code] = {
path: detail.url,
title: detail.name,
parentName: route.code,
buttons: detail.buttons?.map((b: any) => b.id) || []
}
}
const otherRoutes = extraRouteObj[route.code] const otherRoutes = extraRouteObj[route.code]
if (otherRoutes) { if (otherRoutes) {
otherRoutes.children.map((item: any) => { otherRoutes.children.map((item: any) => {
const _code = `${route.code}/${item.code}` const _code = `${route.code}/${item.code}`
const url = `${route.url}/${item.code}`
routeMeta[_code] = { routeMeta[_code] = {
path: `${route.url}/${item.code}`, path: `${route.url}/${item.code}`,
title: item.name, title: item.name,
parentName: route.code, parentName: route.code,
buttons: item.buttons?.map((b: any) => b.id) || [] buttons: item.buttons?.map((b: any) => b.id) || []
} }
getDetail(_code, url)
}) })
} }
getDetail(route.code, route.url)
if (route.children) { if (route.children) {
findChildren(route.children, route.code) findChildren(route.children, route.code)
} }

View File

@ -25,13 +25,13 @@
label="密码" label="密码"
name="newPassword" name="newPassword"
:rules="[ :rules="[
{ required: true,message:'请输入密码' }, { required: true, message: '请输入密码' },
{ validator: checkMothods.new, trigger: 'blur' }, { validator: checkMothods.new, trigger: 'blur' },
]" ]"
> >
<j-input-password <j-input-password
v-model:value="form.newPassword" v-model:value="form.newPassword"
placeholder="请输入姓名" placeholder="请输入密码"
/> />
</j-form-item> </j-form-item>
<j-form-item <j-form-item
@ -44,7 +44,7 @@
> >
<j-input-password <j-input-password
v-model:value="form.confirmPassword" v-model:value="form.confirmPassword"
placeholder="请输入姓名" placeholder="请再次输入密码"
/> />
</j-form-item> </j-form-item>
</j-form> </j-form>
@ -64,7 +64,7 @@ const emits = defineEmits(['ok', 'update:visible']);
const props = defineProps<{ const props = defineProps<{
visible: boolean; visible: boolean;
}>(); }>();
const loading = ref(false) const loading = ref(false);
const formRef = ref<FormInstance>(); const formRef = ref<FormInstance>();
const form = ref<formType>({ const form = ref<formType>({
oldPassword: '', oldPassword: '',
@ -102,7 +102,9 @@ const checkMothods = {
}, },
confirm: async (_rule: Rule, value: string) => { confirm: async (_rule: Rule, value: string) => {
if (!value) return Promise.reject(); if (!value) return Promise.reject();
else if (form.value.newPassword && value !== form.value.newPassword) {
formRef.value?.validate('newPassword');
}
try { try {
const resp: any = await validateField_api('password', value); const resp: any = await validateField_api('password', value);
if (resp.status === 200 && !resp.result.passed) if (resp.status === 200 && !resp.result.passed)
@ -116,18 +118,20 @@ const checkMothods = {
const handleOk = () => { const handleOk = () => {
formRef.value?.validate().then(() => { formRef.value?.validate().then(() => {
loading.value = true loading.value = true;
const params = { const params = {
oldPassword: form.value.oldPassword, oldPassword: form.value.oldPassword,
newPassword: form.value.newPassword, newPassword: form.value.newPassword,
}; };
updateMepsd_api(params).then((resp) => { updateMepsd_api(params)
if (resp.status === 200) { .then((resp) => {
message.success('保存成功'); if (resp.status === 200) {
emits('ok'); message.success('保存成功');
emits('update:visible', false); emits('ok');
} emits('update:visible', false);
}).finally(()=>loading.value = false) }
})
.finally(() => (loading.value = false));
}); });
}; };
console.clear(); console.clear();

View File

@ -114,10 +114,7 @@
<h3>修改密码</h3> <h3>修改密码</h3>
<div class="content"> <div class="content">
<div class="content" style="align-items: flex-end"> <div class="content" style="align-items: flex-end">
<lock-outlined <AIcon type="LockOutlined" style="color: #1d39c4; font-size: 70px" />
style="color: #1d39c4; font-size: 70px"
/>
<!-- <AIcon type="LockOutlined" /> -->
<span <span
style="margin-left: 5px; color: rgba(0, 0, 0, 0.55)" style="margin-left: 5px; color: rgba(0, 0, 0, 0.55)"
>安全性高的密码可以使帐号更安全建议您定期更换密码,设置一个包含字母,符号或数字中至少两项且长度超过8位的密码</span >安全性高的密码可以使帐号更安全建议您定期更换密码,设置一个包含字母,符号或数字中至少两项且长度超过8位的密码</span
@ -232,7 +229,6 @@
import PermissionButton from '@/components/PermissionButton/index.vue'; import PermissionButton from '@/components/PermissionButton/index.vue';
import EditInfoDialog from './components/EditInfoDialog.vue'; import EditInfoDialog from './components/EditInfoDialog.vue';
import EditPasswordDialog from './components/EditPasswordDialog.vue'; import EditPasswordDialog from './components/EditPasswordDialog.vue';
import { LockOutlined } from '@ant-design/icons-vue';
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable'; import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
import { LocalStore, getImage } from '@/utils/comm'; import { LocalStore, getImage } from '@/utils/comm';
import { message, UploadChangeParam, UploadFile } from 'ant-design-vue'; import { message, UploadChangeParam, UploadFile } from 'ant-design-vue';
@ -247,7 +243,7 @@ import { isNoCommunity } from '@/utils/utils';
import { userInfoType } from './typing'; import { userInfoType } from './typing';
const permission = 'system/User'; const permission = 'system/User';
const userInfo = ref<userInfoType>({}); const userInfo = ref<userInfoType>({} as any);
// //
const bindList = ref<any[]>([]); const bindList = ref<any[]>([]);
const bindIcon = { const bindIcon = {

View File

@ -1,10 +1,11 @@
<!-- 新增编辑弹窗 --> <!-- 新增编辑弹窗 -->
<template> <template>
<j-modal <j-modal
v-if="visible"
:title="props.title" :title="props.title"
:maskClosable="false" :maskClosable="false"
destroy-on-close destroy-on-close
v-model:visible="visible" visible
@ok="submitData" @ok="submitData"
@cancel="close" @cancel="close"
okText="确定" okText="确定"
@ -93,7 +94,13 @@ const formModel = ref<formState>({
description: '', description: '',
}); });
const rules = ref({ const rules = ref({
name: [{ required: true, message: '请输入名称', trigger: 'blur' }], name: [
{ required: true, message: '请输入名称', trigger: 'blur' },
{
max: 64,
message: '最多可输入64个字符',
},
],
sortIndex: [{ required: true, message: '请输入排序', trigger: 'blur' }], sortIndex: [{ required: true, message: '请输入排序', trigger: 'blur' }],
}); });
const visible = ref(false); const visible = ref(false);
@ -111,20 +118,20 @@ const submitData = async () => {
if (props.isChild === 1) { if (props.isChild === 1) {
addParams.value = { addParams.value = {
...formModel.value, ...formModel.value,
// sortIndex: sortIndex:
// childArr.value[childArr.value.length - 1].sortIndex + 1, childArr.value[childArr.value.length - 1].sortIndex + 1,
parentId: addObj.value.id, parentId: addObj.value.id,
}; };
} else if (props.isChild === 2) { } else if (props.isChild === 2) {
addParams.value = { addParams.value = {
parentId: addObj.value.id, parentId: addObj.value.id,
...formModel.value, ...formModel.value,
// sortIndex: 1, sortIndex: 1,
}; };
} else if (props.isChild === 3) { } else if (props.isChild === 3) {
addParams.value = { addParams.value = {
...formModel.value, ...formModel.value,
// sortIndex: arr.value[arr.value.length - 1].sortIndex + 1, sortIndex: arr.value[arr.value.length - 1].sortIndex + 1,
}; };
} }
const res = await saveTree(addParams.value); const res = await saveTree(addParams.value);
@ -157,7 +164,7 @@ const submitData = async () => {
/** /**
* 显示弹窗 * 显示弹窗
*/ */
const show = (row: any) => { const show = async (row: any) => {
// //
if (props.isAdd === 0) { if (props.isAdd === 0) {
if (props.isChild === 1) { if (props.isChild === 1) {
@ -173,6 +180,7 @@ const show = (row: any) => {
visible.value = true; visible.value = true;
} }
} else if (props.isChild === 3) { } else if (props.isChild === 3) {
const res = await getTableData();
arr.value = listData.value.sort(compare('sortIndex')); arr.value = listData.value.sort(compare('sortIndex'));
if (arr.value.length > 0) { if (arr.value.length > 0) {
formModel.value = { formModel.value = {
@ -244,7 +252,6 @@ const close = () => {
visible.value = false; visible.value = false;
resetFields(); resetFields();
}; };
getTableData();
//ID //ID
watch([() => props.isAdd], () => {}, { immediate: false, deep: true }); watch([() => props.isAdd], () => {}, { immediate: false, deep: true });
defineExpose({ defineExpose({

View File

@ -9,8 +9,10 @@
<JProTable <JProTable
ref="tableRef" ref="tableRef"
:columns="table.columns" :columns="table.columns"
:dataSource="dataSource" :request="queryTree"
model="table" model="TABLE"
type="TREE"
:scroll="{ y: 550 }"
:defaultParams="{ :defaultParams="{
paging: false, paging: false,
sorts: [ sorts: [
@ -21,7 +23,7 @@
}, },
], ],
}" }"
:params="query.params" :params="params"
:loading="tableLoading" :loading="tableLoading"
> >
<template #headerTitle> <template #headerTitle>
@ -35,7 +37,7 @@
</PermissionButton> </PermissionButton>
</template> </template>
<template #action="slotProps"> <template #action="slotProps">
<j-space :size="16"> <j-space>
<template <template
v-for="i in getActions(slotProps, 'table')" v-for="i in getActions(slotProps, 'table')"
:key="i.key" :key="i.key"
@ -118,38 +120,13 @@ const query = reactive({
scopedSlots: true, scopedSlots: true,
}, },
], ],
params: {
paging: false,
sorts: [
{ name: 'sortIndex', order: 'asc' },
{
name: 'createTime',
order: 'desc',
},
],
},
}); });
/** let params = ref();
* 查询树形列表
*/
const getTableData = async () => {
tableLoading.value = true;
const res = await queryTree(query.params);
if (res.status === 200) {
dataSource.value = res.result;
}
tableLoading.value = false;
};
getTableData();
/** /**
* 搜索 * 搜索
*/ */
const handleSearch = (e: any) => { const handleSearch = (e: any) => {
query.params = { params.value = e
...query.params,
...e,
};
getTableData();
}; };
/** /**
* 操作栏按钮 * 操作栏按钮
@ -200,6 +177,9 @@ const getActions = (
{ {
key: 'delete', key: 'delete',
text: '删除', text: '删除',
tooltip: {
title: '删除',
},
popConfirm: { popConfirm: {
title: '确认删除?', title: '确认删除?',
okText: ' 确定', okText: ' 确定',
@ -208,7 +188,7 @@ const getActions = (
const resp = await deleteTree(data.id); const resp = await deleteTree(data.id);
if (resp.status === 200) { if (resp.status === 200) {
message.success('操作成功!'); message.success('操作成功!');
getTableData(); tableRef.value.reload();
} else { } else {
message.error('操作失败!'); message.error('操作失败!');
} }
@ -222,23 +202,31 @@ const getActions = (
const table = reactive({ const table = reactive({
columns: [ columns: [
{ title: '名称', dataIndex: 'name', key: 'name' }, {
title: '名称',
dataIndex: 'name',
key: 'name',
ellipsis: true,
width: 600,
},
{ {
title: '排序', title: '排序',
dataIndex: 'sortIndex', dataIndex: 'sortIndex',
key: 'sortIndex', key: 'sortIndex',
scopedSlots: true, scopedSlots: true,
width: 200,
}, },
{ {
title: '说明', title: '说明',
dataIndex: 'description', dataIndex: 'description',
key: 'description', key: 'description',
width: 700,
}, },
{ {
title: '操作', title: '操作',
key: 'action', key: 'action',
fixed: 'right', fixed: 'right',
width: 250, ellipsis: true,
scopedSlots: true, scopedSlots: true,
}, },
], ],
@ -257,7 +245,7 @@ const table = reactive({
* 刷新表格数据 * 刷新表格数据
*/ */
refresh: () => { refresh: () => {
getTableData(); tableRef.value.reload();
}, },
}); });
const { add, columns, refresh } = toRefs(table); const { add, columns, refresh } = toRefs(table);

View File

@ -228,7 +228,6 @@ const getOnline = () => {
onlineYdata.reverse(); onlineYdata.reverse();
setOnlineChartOpition(x, onlineYdata); setOnlineChartOpition(x, onlineYdata);
onlineFooter.value[0].value = y?.[1]; onlineFooter.value[0].value = y?.[1];
console.log(res.result);
} }
}); });
}; };

View File

@ -7,7 +7,7 @@
:pagination="false" :pagination="false"
> >
<template #bodyCell="{ column, text, record }"> <template #bodyCell="{ column, text, record }">
<div style="width: 280px"> <div>
<template v-if="['valueType', 'name'].includes(column.dataIndex)"> <template v-if="['valueType', 'name'].includes(column.dataIndex)">
<span>{{ text }}</span> <span>{{ text }}</span>
</template> </template>

View File

@ -214,7 +214,7 @@ defineExpose({ saveBtn });
<style lang="less" scoped> <style lang="less" scoped>
.function { .function {
padding: 15px; padding: 24px 15px 0 15px;
background-color: #e7eaec; background-color: #e7eaec;
} }
</style> </style>

View File

@ -30,7 +30,7 @@
<j-col :span="8"> <j-col :span="8">
<div class="right-log"> <div class="right-log">
<TitleComponent data="日志" /> <TitleComponent data="日志" />
<div :style="{ marginTop: '10px' }"> <div class="right-log-box">
<template v-if="logList.length"> <template v-if="logList.length">
<Log <Log
v-for="item in logList" v-for="item in logList"
@ -38,7 +38,9 @@
:key="item.key" :key="item.key"
/> />
</template> </template>
<j-empty v-else /> <div v-else class="right-log-box-empty">
<j-empty />
</div>
</div> </div>
</div> </div>
</j-col> </j-col>
@ -187,8 +189,21 @@ onUnmounted(() => {
padding-left: 20px; padding-left: 20px;
border-left: 1px solid rgba(0, 0, 0, 0.09); border-left: 1px solid rgba(0, 0, 0, 0.09);
overflow: hidden; overflow: hidden;
max-height: 600px; height: 100%;
overflow-y: auto; overflow-y: auto;
min-height: 400px; min-height: 400px;
.right-log-box {
padding-top: 10px;
height: calc(100% - 40px);
width: 100%;
.right-log-box-empty {
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
}
} }
</style> </style>

View File

@ -1,14 +1,14 @@
<template> <template>
<a-select allowClear v-model:value="_value" @change="onChange" placeholder="请选择" style="width: 100%"> <j-select allowClear v-model:value="_value" @change="onChange" placeholder="请选择" style="width: 100%">
<a-select-option <j-select-option
v-for="item in list" v-for="item in list"
:key="item.id" :key="item.id"
:value="item.id" :value="item.id"
:label="item.name" :label="item.name"
:filter-option="filterOption" :filter-option="filterOption"
>{{ item.name }}</a-select-option >{{ item.name }}</j-select-option
> >
</a-select> </j-select>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template> <template>
<a-modal <j-modal
width="900px" width="900px"
title="批量映射" title="批量映射"
visible visible
@ -10,49 +10,49 @@
<div class="map-tree-top"> <div class="map-tree-top">
采集器的点位名称与属性名称一致时将自动映射绑定有多个采集器点位名称与属性名称一致时以第1个采集器的点位数据进行绑定 采集器的点位名称与属性名称一致时将自动映射绑定有多个采集器点位名称与属性名称一致时以第1个采集器的点位数据进行绑定
</div> </div>
<a-spin :spinning="loading"> <j-spin :spinning="loading">
<div class="map-tree-content"> <div class="map-tree-content">
<a-card class="map-tree-content-card" title="源数据"> <j-card class="map-tree-content-card" title="源数据">
<a-tree <j-tree
checkable checkable
:height="300" :height="300"
:tree-data="dataSource" :tree-data="dataSource"
:checkedKeys="checkedKeys" :checkedKeys="checkedKeys"
@check="onCheck" @check="onCheck"
/> />
</a-card> </j-card>
<div style="width: 100px"> <div style="width: 100px">
<a-button <j-button
:disabled="rightList.length >= leftList.length" :disabled="rightList.length >= leftList.length"
@click="onRight" @click="onRight"
>加入右侧</a-button >加入右侧</j-button
> >
</div> </div>
<a-card class="map-tree-content-card" title="采集器"> <j-card class="map-tree-content-card" title="采集器">
<a-list <j-list
size="small" size="small"
:data-source="rightList" :data-source="rightList"
class="map-tree-content-card-list" class="map-tree-content-card-list"
> >
<template #renderItem="{ item }"> <template #renderItem="{ item }">
<a-list-item> <j-list-item>
{{ item.title }} {{ item.title }}
<template #actions> <template #actions>
<a-popconfirm <j-popconfirm
title="确定删除?" title="确定删除?"
@confirm="_delete(item.key)" @confirm="_delete(item.key)"
> >
<AIcon type="DeleteOutlined" /> <AIcon type="DeleteOutlined" />
</a-popconfirm> </j-popconfirm>
</template> </template>
</a-list-item> </j-list-item>
</template> </template>
</a-list> </j-list>
</a-card> </j-card>
</div> </div>
</a-spin> </j-spin>
</div> </div>
</a-modal> </j-modal>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

@ -1,46 +1,46 @@
<template> <template>
<a-spin :spinning="loading" v-if="metadata.properties.length"> <j-spin :spinning="loading" v-if="metadata.properties.length">
<a-card> <j-card>
<template #extra> <template #extra>
<a-space> <j-space>
<a-button @click="visible = true">批量映射</a-button> <j-button @click="visible = true">批量映射</j-button>
<a-button type="primary" @click="onSave">保存</a-button> <j-button type="primary" @click="onSave">保存</j-button>
</a-space> </j-space>
</template> </template>
<a-form ref="formRef" :model="modelRef"> <j-form ref="formRef" :model="modelRef">
<a-table :dataSource="modelRef.dataSource" :columns="columns"> <j-table :dataSource="modelRef.dataSource" :columns="columns">
<template #headerCell="{ column }"> <template #headerCell="{ column }">
<template v-if="column.key === 'collectorId'"> <template v-if="column.key === 'collectorId'">
采集器 采集器
<a-tooltip title="边缘网关代理的真实物理设备"> <j-tooltip title="边缘网关代理的真实物理设备">
<AIcon type="QuestionCircleOutlined" /> <AIcon type="QuestionCircleOutlined" />
</a-tooltip> </j-tooltip>
</template> </template>
</template> </template>
<template #bodyCell="{ column, record, index }"> <template #bodyCell="{ column, record, index }">
<template v-if="column.dataIndex === 'channelId'"> <template v-if="column.dataIndex === 'channelId'">
<a-form-item <j-form-item
:name="['dataSource', index, 'channelId']" :name="['dataSource', index, 'channelId']"
> >
<a-select <j-select
style="width: 100%" style="width: 100%"
v-model:value="record[column.dataIndex]" v-model:value="record[column.dataIndex]"
placeholder="请选择" placeholder="请选择"
allowClear allowClear
:filter-option="filterOption" :filter-option="filterOption"
> >
<a-select-option <j-select-option
v-for="item in channelList" v-for="item in channelList"
:key="item.value" :key="item.value"
:value="item.value" :value="item.value"
:label="item.label" :label="item.label"
>{{ item.label }}</a-select-option >{{ item.label }}</j-select-option
> >
</a-select> </j-select>
</a-form-item> </j-form-item>
</template> </template>
<template v-if="column.dataIndex === 'collectorId'"> <template v-if="column.dataIndex === 'collectorId'">
<a-form-item <j-form-item
:name="['dataSource', index, 'collectorId']" :name="['dataSource', index, 'collectorId']"
:rules="[ :rules="[
{ {
@ -55,10 +55,10 @@
type="COLLECTOR" type="COLLECTOR"
:edgeId="instanceStore.current.parentId" :edgeId="instanceStore.current.parentId"
/> />
</a-form-item> </j-form-item>
</template> </template>
<template v-if="column.dataIndex === 'pointId'"> <template v-if="column.dataIndex === 'pointId'">
<a-form-item <j-form-item
:name="['dataSource', index, 'pointId']" :name="['dataSource', index, 'pointId']"
:rules="[ :rules="[
{ {
@ -73,33 +73,33 @@
type="POINT" type="POINT"
:edgeId="instanceStore.current.parentId" :edgeId="instanceStore.current.parentId"
/> />
</a-form-item> </j-form-item>
</template> </template>
<template v-if="column.dataIndex === 'id'"> <template v-if="column.dataIndex === 'id'">
<a-badge <j-badge
v-if="record[column.dataIndex]" v-if="record[column.dataIndex]"
status="success" status="success"
text="已绑定" text="已绑定"
/> />
<a-badge v-else status="error" text="未绑定" /> <j-badge v-else status="error" text="未绑定" />
</template> </template>
<template v-if="column.key === 'action'"> <template v-if="column.key === 'action'">
<a-tooltip title="解绑"> <j-tooltip title="解绑">
<a-popconfirm <j-popconfirm
title="确认解绑" title="确认解绑"
:disabled="!record.id" :disabled="!record.id"
@confirm="unbind(record.id)" @confirm="unbind(record.id)"
> >
<a-button type="link" :disabled="!record.id" <j-button type="link" :disabled="!record.id"
><AIcon type="icon-jiebang" ><AIcon type="icon-jiebang"
/></a-button> /></j-button>
</a-popconfirm> </j-popconfirm>
</a-tooltip> </j-tooltip>
</template> </template>
</template> </template>
</a-table> </j-table>
</a-form> </j-form>
</a-card> </j-card>
<PatchMapping <PatchMapping
:deviceId="instanceStore.current.id" :deviceId="instanceStore.current.id"
v-if="visible" v-if="visible"
@ -108,10 +108,10 @@
:metaData="modelRef.dataSource" :metaData="modelRef.dataSource"
:edgeId="instanceStore.current.parentId" :edgeId="instanceStore.current.parentId"
/> />
</a-spin> </j-spin>
<a-card v-else> <j-card v-else>
<JEmpty description='暂无数据,请配置物模型' style="margin: 10% 0" /> <JEmpty description='暂无数据,请配置物模型' style="margin: 10% 0" />
</a-card> </j-card>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

@ -8,7 +8,7 @@
> >
<j-row :gutter="30"> <j-row :gutter="30">
<j-col :span="15"> <j-col :span="15">
<MonacoEditor <JMonacoEditor
:ref="`monacoEditor${func.id}`" :ref="`monacoEditor${func.id}`"
v-model="func.json" v-model="func.json"
theme="vs-dark" theme="vs-dark"

View File

@ -25,7 +25,7 @@
</div> </div>
<div> <div>
脚本语言: 脚本语言:
<j-select :defaultValue="'JavaScript'" style="width: 200;margin-left: 5px;"> <j-select :defaultValue="'JavaScript'" style="width: 200px;margin-left: 5px;">
<j-select-option value="JavaScript">JavaScript(ECMAScript 5)</j-select-option> <j-select-option value="JavaScript">JavaScript(ECMAScript 5)</j-select-option>
</j-select> </j-select>
<AIcon type="ExpandOutlined" style="margin-left: 20px;" @click="toggle" /> <AIcon type="ExpandOutlined" style="margin-left: 20px;" @click="toggle" />
@ -52,8 +52,7 @@
<template v-if="instanceStore.current.transport === 'MQTT'"> <template v-if="instanceStore.current.transport === 'MQTT'">
<div style="margin-right: 5px;">Topic:</div> <div style="margin-right: 5px;">Topic:</div>
<j-auto-complete placeholder="请输入Topic" style="width: 300px" :options="topicList" <j-auto-complete placeholder="请输入Topic" style="width: 300px" :options="topicList"
:allowClear="true" :filterOption="(inputValue: any, option: any) => :allowClear="true" :filterOption="filterOption" v-model:value="topic" />
option!.value.indexOf(inputValue) !== -1" v-model:value="topic" />
</template> </template>
<template v-else> <template v-else>
<div style="margin-right: 5px;">URL:</div> <div style="margin-right: 5px;">URL:</div>
@ -134,6 +133,7 @@ const resStyle = computed(() => (isBoolean(resultValue.value.success) ? {
} : { } : {
'margin-top': '10px', 'margin-top': '10px',
})) }))
const filterOption = (inputValue: any, option: any) => option!.value.indexOf(inputValue) !== -1
const isDisabled = computed(() => simulation.value === '') const isDisabled = computed(() => simulation.value === '')

View File

@ -41,19 +41,21 @@
</j-descriptions> </j-descriptions>
</j-card> </j-card>
<!-- 编辑 --> <!-- 编辑 -->
<Save ref="saveRef" :isAdd="isAdd" :title="title" /> <Save ref="saveRef" :isAdd="isAdd" :title="title" @success="refresh"/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useProductStore } from '@/store/product'; import { useProductStore } from '@/store/product';
import Save from '../../Save/index.vue'; import Save from '../../Save/index.vue';
import moment from 'moment'; import moment from 'moment';
import { useRoute } from 'vue-router';
import { import {
EditOutlined, EditOutlined,
DeleteOutlined, DeleteOutlined,
PlusOutlined, PlusOutlined,
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
const productStore = useProductStore(); const productStore = useProductStore();
const route = useRoute();
const saveRef = ref(); const saveRef = ref();
const isAdd = ref(2); const isAdd = ref(2);
const title = ref('编辑'); const title = ref('编辑');
@ -69,4 +71,10 @@ const editConfig = () => {
const changeTables = () => { const changeTables = () => {
productStore.tabActiveKey = 'Device'; productStore.tabActiveKey = 'Device';
}; };
/**
* 修改成功刷新
*/
const refresh = () =>{
productStore.refresh(route.params.id as string);
}
</script> </script>

View File

@ -269,7 +269,8 @@
<!-- 选择设备 --> <!-- 选择设备 -->
<j-modal <j-modal
title="设备接入配置" title="设备接入配置"
:visible="visible" v-if="visible"
visible
width="1200px" width="1200px"
okText="确定" okText="确定"
cancelText="取消" cancelText="取消"
@ -280,6 +281,7 @@
:columns="query.columns" :columns="query.columns"
target="deviceModal" target="deviceModal"
@search="search" @search="search"
type='simple'
/> />
<JProTable <JProTable
:columns="query.columns" :columns="query.columns"
@ -442,6 +444,7 @@ const showModal = () => {
* 关闭弹窗 * 关闭弹窗
*/ */
const cancel = () => { const cancel = () => {
queryParams.value = {};
visible.value = false; visible.value = false;
}; };
/** /**
@ -968,6 +971,7 @@ const submitData = async () => {
message.success('操作成功!'); message.success('操作成功!');
} }
visible.value = false; visible.value = false;
queryParams.value = {};
}); });
} }
} else { } else {

View File

@ -151,7 +151,6 @@ watch(
() => route.params.id, () => route.params.id,
(newId) => { (newId) => {
if (newId) { if (newId) {
console.log(newId);
productStore.tabActiveKey = 'Info'; productStore.tabActiveKey = 'Info';
productStore.refresh(newId as string); productStore.refresh(newId as string);
} }
@ -230,7 +229,7 @@ const getProtocol = async () => {
* 详情页跳转到设备页 * 详情页跳转到设备页
*/ */
const jumpDevice = () => { const jumpDevice = () => {
console.log(productStore.current?.id); // console.log(productStore.current?.id);
const searchParams = { const searchParams = {
column: 'productId', column: 'productId',
termType: 'eq', termType: 'eq',

View File

@ -22,8 +22,8 @@
<div class="product-title">产品创建成功</div> <div class="product-title">产品创建成功</div>
</div> </div>
<div style="display: flex"> <div style="display: flex">
<div class="product-id">产品ID: {{ idValue.value }}</div> <div class="product-id">产品ID: {{ idValue }}</div>
<div class="product-btn" @click="showDetail">查看详情</div> <div class="product-btn" @click="showDetail" style="cursor: pointer;">查看详情</div>
</div> </div>
<div>接下来推荐操作:</div> <div>接下来推荐操作:</div>
<div class="product-main">1配置产品接入方式</div> <div class="product-main">1配置产品接入方式</div>
@ -49,10 +49,12 @@
import { getImage } from '@/utils/comm.ts'; import { getImage } from '@/utils/comm.ts';
import { useProductStore } from '@/store/product'; import { useProductStore } from '@/store/product';
import { CheckCircleOutlined } from '@ant-design/icons-vue'; import { CheckCircleOutlined } from '@ant-design/icons-vue';
import { useMenuStore } from '@/store/menu';
const visible = ref<boolean>(false); const visible = ref<boolean>(false);
const productStore = useProductStore(); const productStore = useProductStore();
const router = useRouter(); const router = useRouter();
const idValue = ref({}); const idValue = ref({});
const menuStore = useMenuStore();
/** /**
* 弹窗关闭 * 弹窗关闭
*/ */
@ -70,17 +72,12 @@ const show = (id: string) => {
* 查看详情 * 查看详情
*/ */
const showDetail = () => { const showDetail = () => {
jump(idValue.value); menuStore.jumpPage('device/Product/Detail',{id:idValue.value})
};
/**
* 跳转页面
*/
const jump = (id: string) => {
router.push('/iot/device/product/detail/' + id);
}; };
defineExpose({ defineExpose({
show: show, show: show,
}); });
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.product-tips { .product-tips {

View File

@ -23,73 +23,15 @@
<j-row type="flex"> <j-row type="flex">
<j-col flex="180px"> <j-col flex="180px">
<j-form-item name="photoUrl"> <j-form-item name="photoUrl">
<Upload v-model="form.photoUrl" /> <j-pro-upload
v-model="form.photoUrl"
:accept="
imageTypes && imageTypes.length
? imageTypes.toString()
: ''
"
/>
</j-form-item> </j-form-item>
<!-- <j-form-item>
<div class="upload-image-warp-logo">
<div class="upload-image-border-logo">
<a-upload
name="file"
:action="FILE_UPLOAD"
:headers="headers"
:showUploadList="false"
:beforeUpload="beforeUpload"
@change="handleChange"
:accept="
imageTypes && imageTypes.length
? imageTypes.toString()
: ''
"
>
<div class="upload-image-content-logo">
<div
class="loading-logo"
v-if="logoLoading"
>
<LoadingOutlined
style="font-size: 28px"
/>
</div>
<div
class="upload-image"
v-if="photoValue"
:style="
photoValue
? `background-image: url(${photoValue});`
: ''
"
></div>
<div
v-if="photoValue"
class="upload-image-mask"
>
点击修改
</div>
<div v-else>
<div v-if="logoLoading">
<LoadingOutlined
style="font-size: 28px"
/>
</div>
<div v-else>
<PlusOutlined
style="font-size: 28px"
/>
</div>
</div>
</div>
</a-upload>
<div v-if="logoLoading">
<div class="upload-loading-mask">
<LoadingOutlined
v-if="logoLoading"
style="font-size: 28px"
/>
</div>
</div>
</div>
</div>
</j-form-item> -->
</j-col> </j-col>
<j-col flex="auto"> <j-col flex="auto">
<j-form-item name="id"> <j-form-item name="id">
@ -125,6 +67,7 @@
placeholder="请选择产品分类" placeholder="请选择产品分类"
:tree-data="treeList" :tree-data="treeList"
@change="valueChange" @change="valueChange"
allow-clear
:fieldNames="{ label: 'name', value: 'id' }" :fieldNames="{ label: 'name', value: 'id' }"
:filterTreeNode=" :filterTreeNode="
(v, option) => filterSelectNode(v, option) (v, option) => filterSelectNode(v, option)
@ -185,8 +128,11 @@
</j-row> </j-row>
</j-radio-group> </j-radio-group>
</j-form-item> </j-form-item>
<j-form-item label="说明" name="describe"> <j-form-item label="说明" name="description">
<j-textarea <j-textarea
:maxlength="200"
showCount
:auto-size="{ minRows: 4, maxRows: 5 }"
v-model:value="form.describe" v-model:value="form.describe"
placeholder="请输入说明" placeholder="请输入说明"
/> />
@ -318,6 +264,9 @@ const rules = reactive({
trigger: 'blur', trigger: 'blur',
}, },
], ],
description: [
{ max: 200, message: '最多可输入200位字符', trigger: 'blur' },
],
}); });
const valueChange = (value: string, label: string) => { const valueChange = (value: string, label: string) => {
@ -395,7 +344,7 @@ const submitData = () => {
message.success('保存成功!'); message.success('保存成功!');
visible.value = false; visible.value = false;
emit('success'); emit('success');
dialogRef.value.show(form.id); dialogRef.value.show(res.result.id);
} else { } else {
message.error('操作失败'); message.error('操作失败');
} }

View File

@ -123,9 +123,6 @@
:status="statusMap.get(slotProps.state)" :status="statusMap.get(slotProps.state)"
/> />
</template> </template>
<template #id="slotProps">
<a>{{ slotProps.id }}</a>
</template>
<template #action="slotProps"> <template #action="slotProps">
<j-space :size="16"> <j-space :size="16">
<template <template
@ -202,7 +199,7 @@ const columns = [
scopedSlots: true, scopedSlots: true,
}, },
{ {
title: '名称', title: '产品名称',
dataIndex: 'name', dataIndex: 'name',
key: 'name', key: 'name',
}, },

View File

@ -34,8 +34,11 @@ const props = defineProps({
const { cardData, cardTitle } = toRefs(props); const { cardData, cardTitle } = toRefs(props);
const jumpPage = (item: bootConfig) => { const jumpPage = (item: bootConfig) => {
if (item.auth === undefined || item.auth) _jumpPage(item.link, item.params); if (item.auth === undefined || item.auth) {
else message.warning('暂无权限,请联系管理员'); _jumpPage(item.link, item.params);
} else {
message.warning('暂无权限,请联系管理员');
}
}; };
</script> </script>

View File

@ -37,8 +37,11 @@ const { cardData, cardTitle } = toRefs(props);
const { jumpPage: _jumpPage } = useMenuStore(); const { jumpPage: _jumpPage } = useMenuStore();
const jumpPage = (item: bootConfig) => { const jumpPage = (item: bootConfig) => {
if (item.auth === undefined || item.auth) _jumpPage(item.link, item.params); if (item.auth === undefined || item.auth) {
else message.warning('暂无权限,请联系管理员'); _jumpPage(item.link, item.params);
} else {
message.warning('暂无权限,请联系管理员');
}
}; };
</script> </script>

View File

@ -13,7 +13,7 @@
<j-row :gutter="24"> <j-row :gutter="24">
<j-col :span="12"><DeviceCountCard /></j-col> <j-col :span="12"><DeviceCountCard /></j-col>
<j-col :span="12"><BasicCountCard /></j-col> <j-col :span="12"><BasicCountCard /></j-col>
<j-col :span="24" style="margin-top: 24px;"> <j-col :span="24" style="margin-top: 24px">
<PlatformPicCard image="/images/home/content1.svg" /> <PlatformPicCard image="/images/home/content1.svg" />
</j-col> </j-col>
</j-row> </j-row>
@ -157,7 +157,7 @@ const deviceStepDetails: recommendList[] = [
linkUrl: 'device/Instance', linkUrl: 'device/Instance',
auth: devicePermission('import'), auth: devicePermission('import'),
params: { params: {
type: 'import' type: 'import',
}, },
}, },
]; ];
@ -227,17 +227,4 @@ const opsStepDetails: recommendList[] = [
const productDialogVisible = ref(false); const productDialogVisible = ref(false);
const deviceDialogVisible = ref(false); const deviceDialogVisible = ref(false);
// ---- {save:true}
// ----- {id: 'xxxx', tab:'xxx'}
// ---- {save: true}
// ----
// ----
// -----
// ----
</script> </script>

View File

@ -46,9 +46,6 @@ const opsBootConfig: bootConfig[] = [
english: 'STEP3', english: 'STEP3',
label: '实时监控', label: '实时监控',
link: 'link/DashBoard', link: 'link/DashBoard',
params: {
type: 'add',
},
}, },
]; ];
const opsStepDetails: recommendList[] = [ const opsStepDetails: recommendList[] = [

View File

@ -38,7 +38,9 @@ const props = defineProps({
// //
const jumpPage = (row: recommendList) => { const jumpPage = (row: recommendList) => {
if (row.auth === false) return message.warning('暂无权限,请联系管理员'); if (row.auth === false) {
return message.warning('暂无权限,请联系管理员');
}
row.onClick ? row.onClick(row) : _jumpPage(row.linkUrl, row.params); row.onClick ? row.onClick(row) : _jumpPage(row.linkUrl, row.params);
}; };
</script> </script>

View File

@ -52,7 +52,9 @@ const props = defineProps<{
// //
const confirm = () => { const confirm = () => {
if (selectedKeys.value.length < 1) return message.warn('请选择设备'); if (selectedKeys.value.length < 1) {
return message.warn('请选择设备');
}
emits('confirm', selectedKeys.value[0]); emits('confirm', selectedKeys.value[0]);
emits('update:visible', false); emits('update:visible', false);
}; };

View File

@ -46,8 +46,8 @@ const form = ref({
const productList = ref<[productItem] | []>([]); const productList = ref<[productItem] | []>([]);
const getOptions = () => { const getOptions = () => {
getProductList_api().then((resp: any) => { getProductList_api().then(({ result }: any) => {
productList.value = resp.result productList.value = result
.filter((i: any) => !i?.accessId) .filter((i: any) => !i?.accessId)
.map((item: { name: any; id: any }) => ({ .map((item: { name: any; id: any }) => ({
label: item.name, label: item.name,

View File

@ -1,6 +1,6 @@
<template> <template>
<page-container> <page-container>
<div class="iot-home-container" v-loading="loading"> <div class="iot-home-container">
<InitHome v-if="currentView === 'init'" @refresh="setCurrentView" /> <InitHome v-if="currentView === 'init'" @refresh="setCurrentView" />
<DeviceHome v-else-if="currentView === 'device'" /> <DeviceHome v-else-if="currentView === 'device'" />
<DevOpsHome v-else-if="currentView === 'ops'" /> <DevOpsHome v-else-if="currentView === 'ops'" />
@ -54,25 +54,26 @@ import { getMe_api, getView_api } from '@/api/home';
import { getAppInfo_api } from '@/api/system/apply'; import { getAppInfo_api } from '@/api/system/apply';
const currentView = ref<string>(''); const currentView = ref<string>('');
const loading = ref<boolean>(true);
const clientId = useUserInfo().$state.userInfos.id; const clientId = useUserInfo().$state.userInfos.id;
const secureKey = ref<string>(''); const secureKey = ref<string>('');
const showKey = ref(false); const showKey = ref(false);
// //
const setCurrentView = () => { const setCurrentView = () => {
getView_api().then((resp: any) => { getView_api().then(({ status, result }: any) => {
if (resp.status === 200) { if (status === 200) {
if (resp.result) { currentView.value = 'init';
if (resp.result.username === 'admin') if (result) {
currentView.value = 'comprehensive'; currentView.value =
else currentView.value = resp.result?.content; result.username === 'admin'
} else currentView.value = 'init'; ? 'comprehensive'
: result?.content;
}
} }
}); });
}; };
if (isNoCommunity) { if (isNoCommunity) {
// api // api
getMe_api().then((resp: any) => { getMe_api().then((resp: any) => {
if (resp && resp.status === 200) { if (resp && resp.status === 200) {
const isApiUser = resp.result.dimensions.find( const isApiUser = resp.result.dimensions.find(
@ -85,10 +86,14 @@ if (isNoCommunity) {
getAppInfo_api(clientId).then((resp: any) => { getAppInfo_api(clientId).then((resp: any) => {
secureKey.value = resp.result.apiServer.secureKey; secureKey.value = resp.result.apiServer.secureKey;
}); });
} else setCurrentView(); } else {
setCurrentView();
}
} }
}); });
} else setCurrentView(); } else {
setCurrentView();
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="wrapper"> <div class="card-wrapper">
<div class="card-header"> <div class="card-header">
<div class="title">{{ title }}</div> <div class="title">{{ title }}</div>
<div class="tools"> <div class="tools">
@ -156,7 +156,7 @@ watch(
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.wrapper { .card-wrapper {
padding: 24px; padding: 24px;
background-color: #fff; background-color: #fff;
.card-header { .card-header {

View File

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

View File

@ -20,9 +20,8 @@
</div> </div>
<div class="right"> <div class="right">
<pro-search <pro-search
type="simple"
:columns="columns" :columns="columns"
target="product" target="channel"
@search="handleSearch" @search="handleSearch"
/> />

View File

@ -15,6 +15,7 @@
:checkStyle="true" :checkStyle="true"
:disabled="!!route.query.id" :disabled="!!route.query.id"
v-model="formData.channel" v-model="formData.channel"
@change="formData.productId = undefined"
/> />
</j-form-item> </j-form-item>
<j-row :gutter="24"> <j-row :gutter="24">
@ -264,7 +265,7 @@ const formData = ref({
name: '', name: '',
channel: 'gb28181-2016', channel: 'gb28181-2016',
photoUrl: getImage('/device-media.png'), photoUrl: getImage('/device-media.png'),
productId: '', productId: undefined,
others: { others: {
access_pwd: '', access_pwd: '',
}, },

View File

@ -13,7 +13,7 @@
sorts: [{ name: 'createTime', order: 'desc' }], sorts: [{ name: 'createTime', order: 'desc' }],
}" }"
:params="params" :params="params"
:gridColumn="3" :gridColumn="2"
> >
<template #headerTitle> <template #headerTitle>
<PermissionButton <PermissionButton
@ -92,6 +92,23 @@
</template> </template>
</CardBox> </CardBox>
</template> </template>
<template #channelNumber="slotProps">
{{ slotProps.channelNumber || 0 }}
</template>
<template #provider="slotProps">
{{ providerType[slotProps.provider] }}
</template>
<template #state="slotProps">
<j-badge
:text="slotProps.state?.text"
:status="
slotProps.state?.value === 'online'
? 'success'
: 'error'
"
/>
</template>
<template #action="slotProps"> <template #action="slotProps">
<j-space :size="16"> <j-space :size="16">
<template <template
@ -125,6 +142,7 @@ import { message } from 'ant-design-vue';
import { getImage } from '@/utils/comm'; import { getImage } from '@/utils/comm';
import { PROVIDER_OPTIONS } from '@/views/media/Device/const'; import { PROVIDER_OPTIONS } from '@/views/media/Device/const';
import { providerType } from './const'; import { providerType } from './const';
import encodeQuery from '@/utils/encodeQuery';
import { useMenuStore } from 'store/menu'; import { useMenuStore } from 'store/menu';
@ -138,6 +156,8 @@ const columns = [
title: 'ID', title: 'ID',
dataIndex: 'id', dataIndex: 'id',
key: 'id', key: 'id',
width: 200,
fixed: 'left',
search: { search: {
type: 'string', type: 'string',
}, },
@ -153,14 +173,14 @@ const columns = [
}, },
{ {
title: '接入方式', title: '接入方式',
dataIndex: 'type', dataIndex: 'provider',
key: 'type', key: 'provider',
scopedSlots: true, scopedSlots: true,
search: { search: {
type: 'select', type: 'select',
options: PROVIDER_OPTIONS, options: PROVIDER_OPTIONS,
handleValue: (v: any) => { handleValue: (v: any) => {
return '123'; return v;
}, },
}, },
}, },
@ -168,6 +188,8 @@ const columns = [
title: '通道数量', title: '通道数量',
dataIndex: 'channelNumber', dataIndex: 'channelNumber',
key: 'channelNumber', key: 'channelNumber',
scopedSlots: true,
width: 100,
}, },
{ {
title: '厂商', title: '厂商',
@ -181,15 +203,31 @@ const columns = [
title: '产品名称', title: '产品名称',
dataIndex: 'productId', dataIndex: 'productId',
key: 'productId', key: 'productId',
scopedSlots: true, // scopedSlots: true,
search: { search: {
type: 'select', type: 'select',
options: [ options: () =>
{ label: '固定地址', value: 'fixed-media' }, new Promise((resolve) => {
{ label: 'GB/T28181', value: 'gb28181-2016' }, DeviceApi.getProductList(
], encodeQuery({
terms: {
messageProtocol$in: [
'gb28181-2016',
'fixed-media',
],
},
}),
).then((resp: any) => {
resolve(
resp.result.map((pItem: any) => ({
label: pItem.name,
value: pItem.id,
})),
);
});
}),
handleValue: (v: any) => { handleValue: (v: any) => {
return '123'; return v;
}, },
}, },
}, },
@ -198,6 +236,7 @@ const columns = [
dataIndex: 'state', dataIndex: 'state',
key: 'state', key: 'state',
scopedSlots: true, scopedSlots: true,
width: 100,
search: { search: {
type: 'select', type: 'select',
options: [ options: [
@ -206,7 +245,7 @@ const columns = [
{ label: '在线', value: 'online' }, { label: '在线', value: 'online' },
], ],
handleValue: (v: any) => { handleValue: (v: any) => {
return '123'; return v;
}, },
}, },
}, },
@ -308,7 +347,8 @@ const getActions = (
key: 'delete', key: 'delete',
text: '删除', text: '删除',
tooltip: { tooltip: {
title: '在线设备无法删除', title:
data.state.value === 'online' ? '在线设备无法删除' : '删除',
}, },
disabled: data.state.value === 'online', disabled: data.state.value === 'online',
popConfirm: { popConfirm: {

View File

@ -25,7 +25,7 @@
<!-- 选择设备 --> <!-- 选择设备 -->
<j-modal <j-modal
title="选择设备" title="选择设备"
width="800px" width="850px"
v-model:visible="visible" v-model:visible="visible"
:maskClosable="false" :maskClosable="false"
:destroyOnClose="true" :destroyOnClose="true"
@ -57,6 +57,7 @@
deviceItem = record; deviceItem = record;
} }
}" }"
:alertRender="false"
:pagination="{ :pagination="{
showSizeChanger: true, showSizeChanger: true,
pageSizeOptions: ['10', '20', '50', '100'], pageSizeOptions: ['10', '20', '50', '100'],
@ -160,6 +161,7 @@ const columns = [
key: 'id', key: 'id',
search: { search: {
type: 'string', type: 'string',
defaultTermType: 'eq',
}, },
}, },
{ {

View File

@ -316,15 +316,10 @@ const getBindUsers = async () => {
* 获取所有用户未绑定的用户 * 获取所有用户未绑定的用户
*/ */
const allUserList = ref([]); const allUserList = ref([]);
const getAllUsers = async () => { const getAllUsers = async (terms?: any) => {
const params = { const params = {
paging: false, paging: false,
terms: [ terms,
{
column: `id$user-third$${props.data.type}_${props.data.provider}$not`,
value: props.data.id,
},
],
}; };
const { result } = await configApi.getPlatformUsers(params); const { result } = await configApi.getPlatformUsers(params);
allUserList.value = result.map((m: any) => ({ allUserList.value = result.map((m: any) => ({
@ -355,11 +350,11 @@ const getTableData = () => {
const bindUser = bindUsers.find( const bindUser = bindUsers.find(
(f: any) => f.thirdPartyUserId === deptUser.id, (f: any) => f.thirdPartyUserId === deptUser.id,
); );
// if (bindUser) { if (bindUser) {
// unBindUser = unBindUsers.find( unBindUser = unBindUsers.find(
// (f: any) => f.id === bindUser.userId, (f: any) => f.id === bindUser.userId,
// ); );
// } }
dataSource.value.push({ dataSource.value.push({
thirdPartyUserId: deptUser.id, thirdPartyUserId: deptUser.id,
thirdPartyUserName: deptUser.name, thirdPartyUserName: deptUser.name,
@ -422,7 +417,12 @@ const handleBind = (row: any) => {
bindVis.value = true; bindVis.value = true;
// formData.value = row; // formData.value = row;
Object.assign(formData.value, row); Object.assign(formData.value, row);
getAllUsers(); getAllUsers([
{
column: `id$user-third$${props.data.type}_${props.data.provider}$not`,
// value: props.data.id,
},
]);
}; };
/** /**

View File

@ -7,41 +7,31 @@
:pagination="false" :pagination="false"
> >
<template #bodyCell="{ column, text, record }"> <template #bodyCell="{ column, text, record }">
<div> <template v-if="column.dataIndex === 'name'">
<template <span>{{ text }}</span>
v-if="['valueType', 'name'].includes(column.dataIndex)" </template>
> <template v-else-if="column.dataIndex === 'valueType'">
<span>{{ text }}</span> <span>{{ record.valueType.type }}</span>
</template> </template>
<template v-else> <template v-else>
<ParamsDropdown <FunctionItem
placeholder="请选择" :builtInList="builtInList"
:options="[]" @change="onChange"
:tabsOptions="tabOptions" :source="record.source"
:metricOption="upperOptions(record.valueType)" :data="record"
v-model:value="record.value" v-model:value="record.value"
> />
<template v-slot="{label}"> </template>
<j-input :value="label" />
</template>
</ParamsDropdown>
</template>
</div>
</template> </template>
</j-table> </j-table>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { PropType } from 'vue'; import { PropType } from 'vue';
import ParamsDropdown from '../../../components/ParamsDropdown'; import FunctionItem from './FunctionItem.vue';
type Emits = {
(e: 'update:modelValue', data: Record<string, any>[]): void;
};
const _emit = defineEmits<Emits>();
const _props = defineProps({ const _props = defineProps({
modelValue: { value: {
type: Array as PropType<Record<string, any>[]>, type: Array as PropType<Record<string, any>[]>,
default: () => undefined, default: () => undefined,
}, },
@ -49,20 +39,13 @@ const _props = defineProps({
type: Array, type: Array,
default: () => [], default: () => [],
}, },
functions: {
type: Array,
default: () => [],
},
}); });
const tabOptions = [ const emit = defineEmits(['update:value']);
{
label: '手动输入',
component: 'string',
key: 'fixed',
},
{
label: '内置参数',
component: 'tree',
key: 'upper',
},
];
const columns = [ const columns = [
{ {
@ -82,33 +65,29 @@ const columns = [
}, },
]; ];
const dataSource = computed({ const dataSource = ref<any[]>([]);
get: () => {
return _props.modelValue || []; watchEffect(() => {
}, const list = (_props.functions || []).map((item: any) => {
set: (val: any) => { const _item = _props.value?.find((i) => i.name === item?.id) || {};
_emit('update:modelValue', val); return {
}, ...item,
..._item,
name: item.name,
};
});
dataSource.value = list;
}); });
const filterParamsData = (type?: string, data?: any[]): any[] => { const onChange = () => {
if (type && data) { const arr = [...dataSource.value].map((item) => {
return data.filter((item) => { return {
if (item.children) { name: item.id,
const _children = filterParamsData(type, item.children); source: item.source,
item.children = _children; upperKey: item.upperKey || item.value,
return _children.length ? true : false; value: item.value,
} else if (item.type === type) { };
// optionMap.current.set(item.id, item); });
return true; emit('update:value', arr);
}
return false;
});
}
return data || [];
};
const upperOptions = (_type: string) => {
return filterParamsData(_type, _props?.builtInList) || [];
}; };
</script> </script>

View File

@ -0,0 +1,117 @@
<template>
<ParamsDropdown
placeholder="请选择"
:options="handleOptions"
:tabsOptions="tabOptions"
:metricOptions="upperOptions"
v-model:value="_value"
v-model:source="_source"
@select="onChange"
>
<template v-slot="{ label }">
<j-input :value="label" />
</template>
</ParamsDropdown>
</template>
<script lang="ts" setup>
import ParamsDropdown from '../../../components/ParamsDropdown';
import { handleParamsData } from './index';
const props = defineProps({
data: {
type: Object,
default: () => {},
},
builtInList: {
type: Array,
default: () => [],
},
value: {
type: String,
},
source: {
type: String,
default: 'fixed',
},
});
const emit = defineEmits(['update:value', 'update:source', 'change']);
const _value = ref();
const _source = ref();
const tabOptions = computed(() => {
return [
{
label: '手动输入',
component: props.data?.valueType?.type,
key: 'fixed',
},
{
label: '内置参数',
component: 'tree',
key: 'upper',
},
];
});
const handleOptions = computed(() => {
const _item = props.data?.valueType || 'int';
const _type = _item?.type;
if (_type === 'boolean') {
return [
{
label: _item.trueText || true,
value: _item.trueValue || true,
},
{
label: _item.falseText || false,
value: _item.falseValue || false,
},
];
}
if (_type === 'enum') {
return _item?.elements.map((i: any) => {
return {
label: i.text,
value: i.value,
};
});
}
return [];
});
const filterParamsData = (type?: string, data?: any[]): any[] => {
if (type && data) {
const list = data.filter((item) => {
if (item.children) {
const _children = filterParamsData(type, item.children);
item.children = _children;
return _children.length ? true : false;
} else if (item.type === type) {
// optionMap.current.set(item.id, item);
return true;
}
return false;
});
return handleParamsData(list);
}
return data || [];
};
const upperOptions = computed(() => {
return filterParamsData(props.data.valueType?.type, props?.builtInList);
});
const onChange = () => {
emit('update:value', _value.value);
emit('update:source', _source.value);
emit('change', { source: _source.value, value: _value.value });
};
watchEffect(() => {
_value.value = props.value;
_source.value = props.source || 'fixed';
});
</script>

View File

@ -1,50 +1,59 @@
<template> <template>
<div> <j-form
<j-form :layout="'vertical'" ref="formRef" :model="modelRef"> :layout="'vertical'"
<j-row> ref="propertyFormRef"
<j-col :span="11"> :model="propertyModelRef"
<j-form-item >
:name="['message', 'properties']" <j-row :gutter="24">
label="读取属性" <j-col :span="12">
:rules="[{ required: true, message: '请选择读取属性' }]" <j-form-item
name="properties"
label="读取属性"
:rules="[{ required: true, message: '请选择读取属性' }]"
>
<j-select
showSearch
placeholder="请选择属性"
v-model:value="propertyModelRef.properties"
@change="onChange"
> >
<j-select <j-select-option
showSearch v-for="item in metadata?.properties || []"
placeholder="请选择属性" :value="item?.id"
v-model:value="modelRef.properties" :key="item?.id"
>{{ item?.name }}</j-select-option
> >
<j-select-option </j-select>
v-for="item in metadata?.properties || []" </j-form-item>
:value="item?.id" </j-col>
:key="item?.id" <j-col :span="12" v-if="propertyModelRef.properties">
>{{ item?.name }}</j-select-option <j-form-item
> name="propertiesValue"
</j-select> label="属性值"
</j-form-item> :rules="[{ required: true, message: '请选择' }]"
</j-col> >
<j-col :span="2"></j-col> <ParamsDropdown
<j-col :span="11"> placeholder="请选择"
<j-form-item :options="handleOptions"
:name="['message', 'propertiesValue']" :tabsOptions="tabOptions"
label="属性值" :metricOptions="upperOptions"
:rules="[{ required: true, message: '请选择' }]" v-model:value="propertyModelRef.propertiesValue"
v-model:source="propertyModelRef.source"
@select="onValueChange"
> >
<ParamsDropdown <template v-slot="{ label }">
icon="icon-canshu" <j-input :value="label" />
placeholder="请选择" </template>
:options="[]" </ParamsDropdown>
:tabsOptions="tabOptions" </j-form-item>
:metricOption="upperOptions(getType)" </j-col>
v-model:value="modelRef.propertiesValue" </j-row>
/> </j-form>
</j-form-item>
</j-col>
</j-row>
</j-form>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import ParamsDropdown from '../../../components/ParamsDropdown';
import { handleParamsData } from './index';
const props = defineProps({ const props = defineProps({
value: { value: {
type: Object, type: Object,
@ -60,37 +69,44 @@ const props = defineProps({
}, },
builtInList: { builtInList: {
type: Array, type: Array,
default: () => [] default: () => [],
}, },
}); });
const formRef = ref(); const emit = defineEmits(['update:value']);
const modelRef = reactive({ const propertyFormRef = ref();
properties: '',
propertiesValue: '', const propertyModelRef = reactive({
source: '', properties: undefined,
propertiesValue: undefined,
source: 'fixed',
}); });
const tabOptions = [
{
label: '手动输入',
component: 'string',
key: 'fixed',
},
{
label: '内置参数',
component: 'tree',
key: 'upper',
},
];
const getType = computed(() => { const getType = computed(() => {
return props.metadata.properties.find((item: any) => item.id === modelRef.properties)?.valueType?.type return props.metadata.properties.find(
}) (item: any) => item.id === propertyModelRef.properties,
);
});
const tabOptions = computed(() => {
return [
{
label: '手动输入',
component: getType.value?.valueType?.type,
key: 'fixed',
},
{
label: '内置参数',
component: 'tree',
key: 'upper',
},
];
});
const filterParamsData = (type?: string, data?: any[]): any[] => { const filterParamsData = (type?: string, data?: any[]): any[] => {
if (type && data) { if (type && data) {
return data.filter((item) => { const list = data.filter((item) => {
if (item.children) { if (item.children) {
const _children = filterParamsData(type, item.children); const _children = filterParamsData(type, item.children);
item.children = _children; item.children = _children;
@ -101,40 +117,74 @@ const filterParamsData = (type?: string, data?: any[]): any[] => {
} }
return false; return false;
}); });
return handleParamsData(list);
} }
return data || []; return data || [];
}; };
const upperOptions = (type: string) => { const upperOptions = computed(() => {
return filterParamsData(type, props?.builtInList) || [] return filterParamsData(getType.value?.valueType?.type, props?.builtInList);
} });
const handleOptions = computed(() => {
const _item = getType.value?.valueType;
const _type = _item?.type;
if (_type === 'boolean') {
return [
{
label: _item.trueText,
value: _item.trueValue,
},
{
label: _item.falseText,
value: _item.falseValue,
},
];
}
if (_type === 'enum') {
return _item?.elements.map((i: any) => {
return {
label: i.text,
value: i.value,
};
});
}
return [];
});
const onChange = () => {
propertyModelRef.propertiesValue = undefined;
propertyModelRef.source = 'fixed'
emit('update:value', {
[`${propertyModelRef.properties}`]: {
value: propertyModelRef?.propertiesValue,
source: propertyModelRef?.source,
},
});
};
const onValueChange = () => {
const obj = {
[`${propertyModelRef.properties}`]: {
value: propertyModelRef?.propertiesValue,
source: propertyModelRef?.source,
},
}
emit('update:value', obj);
};
watch( watch(
() => props.value, () => props.value,
(newVal) => { (newVal) => {
const _keys = Object.keys(newVal)?.[0]; if (newVal) {
if (_keys) { const _keys = Object.keys(newVal)?.[0];
(modelRef.properties = _keys), if (_keys) {
(modelRef.propertiesValue = newVal[_keys]?.value); propertyModelRef.properties = _keys as any;
modelRef.source = newVal[_keys]?.source; propertyModelRef.propertiesValue = newVal[_keys]?.value;
propertyModelRef.source = newVal[_keys]?.source;
}
} }
}, },
{ deep: true, immediate: true }, { deep: true, immediate: true },
); );
const onFormSave = () => {
return new Promise((resolve, reject) => {
formRef.value
.validate()
.then(async (_data: any) => {
//
resolve(_data);
})
.catch((err: any) => {
reject(err);
});
});
};
defineExpose({ onFormSave });
</script> </script>

View File

@ -0,0 +1,10 @@
export const handleParamsData = (data: any[]): any[] => {
return data?.map(item => {
return {
...item,
key: item.column,
disabled: !!item.children?.length,
children: handleParamsData(item.children)
}
}) || []
}

View File

@ -22,7 +22,6 @@
showSearch showSearch
placeholder="请选择功能" placeholder="请选择功能"
v-model:value="modelRef.message.functionId" v-model:value="modelRef.message.functionId"
@change="(val) => onFunctionChange(val, [])"
> >
<j-select-option <j-select-option
v-for="item in metadata?.functions || []" v-for="item in metadata?.functions || []"
@ -37,7 +36,11 @@
:name="['message', 'inputs']" :name="['message', 'inputs']"
:rules="[{ required: true, message: '请输入功能值' }]" :rules="[{ required: true, message: '请输入功能值' }]"
> >
<EditTable v-model:modelValue="modelRef.message.inputs" :builtInList="builtInList" /> <EditTable
:functions="functions"
v-model:value="modelRef.message.inputs"
:builtInList="builtInList"
/>
</j-form-item> </j-form-item>
</template> </template>
<template v-else-if="deviceMessageType === 'READ_PROPERTY'"> <template v-else-if="deviceMessageType === 'READ_PROPERTY'">
@ -49,7 +52,7 @@
<j-select <j-select
showSearch showSearch
placeholder="请选择属性" placeholder="请选择属性"
v-model:value="modelRef.message.properties" v-model:value="modelRef.message.properties[0]"
> >
<j-select-option <j-select-option
v-for="item in metadata?.properties || []" v-for="item in metadata?.properties || []"
@ -77,9 +80,9 @@ import TopCard from '../device/TopCard.vue';
import { detail } from '@/api/device/instance'; import { detail } from '@/api/device/instance';
import EditTable from './EditTable.vue'; import EditTable from './EditTable.vue';
import WriteProperty from './WriteProperty.vue'; import WriteProperty from './WriteProperty.vue';
import { queryBuiltInParams } from '@/api/rule-engine/scene';
import { useSceneStore } from '@/store/scene'; import { useSceneStore } from '@/store/scene';
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia';
import { getParams } from '../../../util'
const sceneStore = useSceneStore(); const sceneStore = useSceneStore();
const { data } = storeToRefs(sceneStore); const { data } = storeToRefs(sceneStore);
@ -118,7 +121,7 @@ const props = defineProps({
type: Number, type: Number,
default: 0, default: 0,
}, },
branchGroup: { branchesName: {
type: Number, type: Number,
default: 0, default: 0,
}, },
@ -128,7 +131,7 @@ const formRef = ref();
const modelRef = reactive({ const modelRef = reactive({
message: { message: {
messageType: undefined, messageType: 'INVOKE_FUNCTION',
functionId: undefined, functionId: undefined,
properties: undefined, properties: undefined,
inputs: [], inputs: [],
@ -149,48 +152,61 @@ const deviceMessageType = computed(() => {
const builtInList = ref<any[]>([]); const builtInList = ref<any[]>([]);
const onFunctionChange = (val: string, values?: any[]) => { const functions = computed(() => {
const _item = (metadata.value?.functions || []).find((item: any) => { const _item = (metadata.value?.functions || []).find((item: any) => {
return val === item.id; return modelRef.message?.functionId === item.id;
}); });
const list = (_item?.inputs || []).map((item: any) => { return _item?.inputs || [];
const _a = values?.find((i) => i.name === item.id); });
return {
id: item.id, const _property = computed(() => {
value: _a?.value, const _item = (metadata.value?.properties || []).find((item: any) => {
valueType: item?.valueType?.type, if (deviceMessageType.value === 'WRITE_PROPERTY') {
..._a, return (
name: item.name, Object.keys(modelRef.message.properties || {})?.[0] === item.id
}; );
}
return modelRef.message?.properties?.[0] === item.id;
}); });
modelRef.message.inputs = list; return _item;
});
const _function = computed(() => {
const _item = (metadata.value?.functions || []).find((item: any) => {
return modelRef.message?.functionId === item.id;
});
return _item;
});
const queryBuiltIn = async () => {
const _params = {
branch: props.thenName,
branchGroup: props.branchesName,
action: props.name - 1,
};
const _data = await getParams(_params, unref(data));
builtInList.value = _data
}; };
const onMessageTypeChange = (val: string) => { const onMessageTypeChange = (val: string) => {
if (['WRITE_PROPERTY', 'INVOKE_FUNCTION'].includes(val)) { const flag = ['WRITE_PROPERTY', 'INVOKE_FUNCTION'].includes(val)
const _params = { modelRef.message = {
branch: props.thenName, messageType: val,
branchGroup: props.branchGroup, functionId: undefined,
action: props.name - 1, properties:(flag ? undefined : []) as any,
}; inputs: [],
queryBuiltInParams(unref(data), _params).then((res: any) => { };
if (res.status === 200) { if (flag) {
builtInList.value = res.result queryBuiltIn();
}
});
} }
}; };
watch( watch(
() => [ () => props.values,
props.values?.productDetail, (newVal) => {
props.values.selectorValues, if (newVal?.productDetail?.id) {
props.values?.selector, if (newVal?.selector === 'fixed') {
], const id = newVal?.selectorValues?.[0]?.value;
([newVal1, newVal2, newVal3]) => {
if (newVal1?.id) {
if (newVal3?.selector === 'fixed') {
const id = newVal2?.[0]?.value;
if (id) { if (id) {
detail(id).then((resp) => { detail(id).then((resp) => {
if (resp.status === 200) { if (resp.status === 200) {
@ -201,7 +217,9 @@ watch(
}); });
} }
} else { } else {
metadata.value = JSON.parse(newVal1?.metadata || '{}'); metadata.value = JSON.parse(
newVal?.productDetail?.metadata || '{}',
);
} }
} }
}, },
@ -211,24 +229,33 @@ watch(
watch( watch(
() => props.values?.message, () => props.values?.message,
(newVal) => { (newVal) => {
console.log(newVal)
if (newVal?.messageType) { if (newVal?.messageType) {
modelRef.message = newVal; modelRef.message = newVal;
if (newVal.messageType === 'INVOKE_FUNCTION' && newVal.functionId) { if (['WRITE_PROPERTY', 'INVOKE_FUNCTION'].includes(newVal.messageType)) {
onFunctionChange(newVal.functionId, newVal?.inputs); queryBuiltIn();
} }
onMessageTypeChange(newVal.messageType)
} }
}, },
{ deep: true, immediate: true }, { immediate: true },
); );
const onFormSave = () => { const onFormSave = () => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
formRef.value formRef.value
.validate() .validate()
.then(async (_data: any) => { .then((_data: any) => {
resolve(_data); //
const obj = {
message: {
...modelRef.message,
..._data.message,
propertiesName:
deviceMessageType.value === 'INVOKE_FUNCTION'
? _function.value?.name
: _property.value?.name,
},
};
resolve(obj);
}) })
.catch((err: any) => { .catch((err: any) => {
reject(err); reject(err);

View File

@ -7,7 +7,7 @@
target="scene-trigger-device-device" target="scene-trigger-device-device"
/> />
<j-divider style="margin: 0" /> <j-divider style="margin: 0" />
<j-pro-table <JProTable
ref="actionRef" ref="actionRef"
model="CARD" model="CARD"
:columns="columns" :columns="columns"
@ -62,7 +62,7 @@
</template> </template>
</CardBox> </CardBox>
</template> </template>
</j-pro-table> </JProTable>
</template> </template>
<script setup lang='ts' name='Product'> <script setup lang='ts' name='Product'>
@ -180,7 +180,6 @@ watchEffect(() => {
<style scoped lang='less'> <style scoped lang='less'>
.search { .search {
margin-bottom: 0; margin-bottom: 0;
padding-right: 0px; padding: 0 0 24px 0;
padding-left: 0px;
} }
</style> </style>

View File

@ -0,0 +1,56 @@
<template>
<j-select
placeholder="请选择关系"
:options="relationList"
show-search
:value="value ? value[0]?.value?.relation : undefined"
@change="onRelationChange"
/>
</template>
<script lang="ts" setup>
import NoticeApi from '@/api/notice/config';
const props = defineProps({
value: {
type: Array,
default: () => [{value: {}}]
},
});
const emit = defineEmits(['update:value', 'change']);
const relationList = ref<any[]>([]);
onMounted(() => {
queryRelationList()
})
const queryRelationList = () => {
NoticeApi.getRelationUsers({
paging: false,
sorts: [{ name: 'createTime', order: 'desc' }],
terms: [{ termType: 'eq', column: 'objectTypeName', value: '设备' }],
}).then((resp) => {
if (resp.status === 200) {
relationList.value = (resp.result as any[]).map((item) => {
return {
label: item.name,
value: item.relation,
};
});
}
});
};
const onRelationChange = (val: any, options: any) => {
const _values = [
{
value: {
objectType: 'user',
relation: val,
},
},
];
emit('update:value', _values);
emit('change', _values, options);
};
</script>

View File

@ -61,7 +61,7 @@
</j-button> </j-button>
<j-button <j-button
danger danger
v-if="tagData.length > 1" v-if="tagList.length > 1"
style="padding: 0 8px" style="padding: 0 8px"
@click="deleteItem(index)" @click="deleteItem(index)"
> >
@ -109,6 +109,7 @@ const addItem = () => {
const deleteItem = (_index: number) => { const deleteItem = (_index: number) => {
tagList.value.splice(_index, 1); tagList.value.splice(_index, 1);
onValueChange();
}; };
const onTypeSelect = (key: any, _index: number) => { const onTypeSelect = (key: any, _index: number) => {
@ -168,26 +169,23 @@ watch(
); );
const onValueChange = () => { const onValueChange = () => {
const newValue = tagList.value const _data = tagList.value.filter((item) => !!item.value);
.filter((item) => !!item.value) const newValue = _data.map((item: any) => {
.map((item: any) => { return {
return { column: item.id,
column: item.id, type: item.type,
type: item.type, value: item.value,
value: item.value, };
}; });
}); const arr = _data.map((item: any) => {
const arr = newValue return {
.filter((item) => !!item.value) column: item.name,
.map((item: any) => { type: item.type,
return { value: item.value,
column: item.name, };
type: item.type, });
value: item.value,
};
});
emits('update:value', [{ value: newValue, name: '标签' }]); emits('update:value', [{ value: newValue, name: '标签' }]);
emits('change', [{ value: newValue, name: '标签' }], undefined); emits('change', [{ value: newValue, name: '标签' }], arr);
}; };
</script> </script>

View File

@ -30,12 +30,9 @@
name="selectorValues" name="selectorValues"
:rules="[{ required: true, message: '请选择关系' }]" :rules="[{ required: true, message: '请选择关系' }]"
> >
<j-select <RelationSelect
placeholder="请选择关系"
v-model:value="modelRef.selectorValues"
:options="relationList"
show-search
@change="onRelationChange" @change="onRelationChange"
v-model:value="modelRef.selectorValues"
/> />
</j-form-item> </j-form-item>
<j-form-item <j-form-item
@ -81,11 +78,13 @@
import { useSceneStore } from '@/store/scene'; import { useSceneStore } from '@/store/scene';
import TopCard from './TopCard.vue'; import TopCard from './TopCard.vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { queryBuiltInParams } from '@/api/rule-engine/scene';
import { getImage } from '@/utils/comm'; import { getImage } from '@/utils/comm';
import NoticeApi from '@/api/notice/config'; import NoticeApi from '@/api/notice/config';
import Device from './Device.vue'; import Device from './Device.vue';
import Tag from './Tag.vue'; import Tag from './Tag.vue';
import RelationSelect from './RelationSelect.vue';
import { getParams } from '../../../util';
import { handleParamsData } from '../../../components/Terms/util';
const props = defineProps({ const props = defineProps({
values: { values: {
@ -100,7 +99,7 @@ const props = defineProps({
type: Number, type: Number,
default: 0, default: 0,
}, },
branchGroup: { branchesName: {
type: Number, type: Number,
default: 0, default: 0,
}, },
@ -130,7 +129,6 @@ const modelRef = reactive({
const list = ref<any[]>([]); const list = ref<any[]>([]);
const builtInList = ref<any[]>([]); const builtInList = ref<any[]>([]);
const tagList = ref<any[]>([]); const tagList = ref<any[]>([]);
const relationList = ref<any[]>([]);
const TypeList = [ const TypeList = [
{ {
@ -191,35 +189,21 @@ const filterTree = (nodes: any[]) => {
const sourceChangeEvent = async () => { const sourceChangeEvent = async () => {
const _params = { const _params = {
branch: props.thenName, branch: props.thenName,
branchGroup: props.branchGroup, branchGroup: props.branchesName,
action: props.name - 1, action: props.name - 1,
}; };
const resp = await queryBuiltInParams(unref(data), _params); //
if (resp.status === 200) { const productId =
const array = filterTree(resp.result as any[]); data.value?.branches?.[props.branchesName].then?.[props.thenName]
// ?.actions?.[props.name > 0 ? props.name - 1 : 0]?.device?.productId;
// if (props.formProductId === DeviceModel.productId)// TODO if (productId === props.values?.productDetail?.id) {
builtInList.value = array; const _data = await getParams(_params, unref(data));
builtInList.value = handleParamsData(filterTree(_data), 'id');
} else {
builtInList.value = [];
} }
}; };
const queryRelationList = () => {
NoticeApi.getRelationUsers({
paging: false,
sorts: [{ name: 'createTime', order: 'desc' }],
terms: [{ termType: 'eq', column: 'objectTypeName', value: '设备' }],
}).then((resp) => {
if (resp.status === 200) {
relationList.value = (resp.result as any[]).map((item) => {
return {
label: item.name,
value: item.relation,
};
});
}
});
};
const filterType = async () => { const filterType = async () => {
const _list = TypeList.filter((item) => item.value === 'fixed'); const _list = TypeList.filter((item) => item.value === 'fixed');
if (unref(data)?.trigger?.type === 'device') { if (unref(data)?.trigger?.type === 'device') {
@ -266,11 +250,8 @@ const filterType = async () => {
} }
}; };
const onSelectorChange = (val: string) => { const onSelectorChange = () => {
modelRef.selectorValues = undefined; modelRef.selectorValues = undefined;
if (val === 'relation') {
queryRelationList();
}
}; };
const onDeviceChange = (_detail: any) => { const onDeviceChange = (_detail: any) => {
@ -303,16 +284,13 @@ const onTagChange = (val: any[], arr: any[]) => {
modelRef.deviceId = 'deviceId'; modelRef.deviceId = 'deviceId';
modelRef.source = 'fixed'; modelRef.source = 'fixed';
} }
if (arr) { emits('save', unref(modelRef), {}, arr ? { tagList: arr } : {});
tagList.value = arr;
}
emits('save', unref(modelRef), {});
}; };
const onVariableChange = (val: any, node: any) => { const onVariableChange = (val: any, node: any) => {
modelRef.deviceId = val; modelRef.deviceId = val;
emits('save', unref(modelRef), node);
modelRef.selectorValues = [{ value: val, name: node.description }] as any; modelRef.selectorValues = [{ value: val, name: node.description }] as any;
emits('save', unref(modelRef), node);
}; };
watchEffect(() => { watchEffect(() => {

View File

@ -29,7 +29,7 @@
v-else-if="current === 1" v-else-if="current === 1"
:name="name" :name="name"
:parallel="parallel" :parallel="parallel"
:branchGroup="branchGroup" :branchesName="branchesName"
:thenName="thenName" :thenName="thenName"
:values="DeviceModel" :values="DeviceModel"
@save="onDeviceSave" @save="onDeviceSave"
@ -38,7 +38,7 @@
<Action <Action
v-else-if="current === 2" v-else-if="current === 2"
:name="name" :name="name"
:branchGroup="branchGroup" :branchesName="branchesName"
:thenName="thenName" :thenName="thenName"
:values="DeviceModel" :values="DeviceModel"
ref="actionRef" ref="actionRef"
@ -91,7 +91,7 @@ const props = defineProps({
type: Number, type: Number,
default: 0, default: 0,
}, },
branchGroup: { branchesName: {
type: Number, type: Number,
default: 0, default: 0,
}, },
@ -132,18 +132,18 @@ const onCancel = () => {
const onSave = (_data: any) => { const onSave = (_data: any) => {
const item: any = { const item: any = {
selector: DeviceModel.selector, selector: DeviceModel.selector,
source: DeviceModel.source, source: DeviceModel.source,
selectorValues: DeviceModel.selectorValues, selectorValues: DeviceModel.selectorValues,
productId: DeviceModel.productId, productId: DeviceModel.productId,
message: _data.message, message: _data.message,
}; };
// //
if (DeviceModel.selector === 'variable') { if (DeviceModel.selector === 'variable') {
item.selector = 'fixed'; item.selector = 'fixed';
} }
if (DeviceModel.selector === 'relation') { if (DeviceModel.selector === 'relation') {
item.upperKey = 'scene.deviceId'; item.upperKey = 'scene.deviceId';
} }
const _options: any = { const _options: any = {
name: '-', // name: '-', //
@ -154,23 +154,24 @@ const onSave = (_data: any) => {
productName: DeviceModel.productDetail.name, productName: DeviceModel.productDetail.name,
relationName: DeviceModel.relationName, relationName: DeviceModel.relationName,
triggerName: data.value.options?.trigger?.name || '触发设备', triggerName: data.value.options?.trigger?.name || '触发设备',
taglist: [], tagList: [],
columns: [], columns: [],
otherColumns: [], otherColumns: [],
}; };
_options.name = DeviceModel.deviceDetail?.name; _options.name =
DeviceModel.deviceDetail?.name || DeviceModel.selectorValues?.[0]?.name;
const _type = _data.message.messageType; const _type = _data.message.messageType;
if (_type === 'INVOKE_FUNCTION') { if (_type === 'INVOKE_FUNCTION') {
_options.type = '执行'; _options.type = '执行';
_options.properties = DeviceModel.propertiesName; _options.properties = _data.message.propertiesName;
} }
if (_type === 'READ_PROPERTY') { if (_type === 'READ_PROPERTY') {
_options.type = '读取'; _options.type = '读取';
_options.properties = DeviceModel.propertiesName; _options.properties = _data.message.propertiesName;
} }
if (_type === 'WRITE_PROPERTY') { if (_type === 'WRITE_PROPERTY') {
_options.type = '设置'; _options.type = '设置';
_options.properties = DeviceModel.propertiesName; _options.properties = _data.message.propertiesName;
_options.propertiesValue = _options.propertiesValue =
typeof DeviceModel.propertiesValue === 'object' typeof DeviceModel.propertiesValue === 'object'
? JSON.stringify(DeviceModel.propertiesValue) ? JSON.stringify(DeviceModel.propertiesValue)
@ -183,6 +184,13 @@ const onSave = (_data: any) => {
} }
} }
if (_options.selector === 'tag') { if (_options.selector === 'tag') {
// const arr = _data.map((item: any) => {
// return {
// column: item.name,
// type: item.type,
// value: item.value,
// };
// });
_options.taglist = DeviceModel.tagList.map((it) => ({ _options.taglist = DeviceModel.tagList.map((it) => ({
name: it.column || it.name, name: it.column || it.name,
type: it.type ? (it.type === 'and' ? '并且' : '或者') : '', type: it.type ? (it.type === 'and' ? '并且' : '或者') : '',
@ -205,6 +213,8 @@ const save = async (step?: number) => {
if (deviceRef.value) { if (deviceRef.value) {
await deviceRef.value?.onFormSave(); await deviceRef.value?.onFormSave();
current.value = 2; current.value = 2;
} else if (DeviceModel.selectorValues.length) {
current.value = 2;
} }
} else { } else {
if (actionRef.value) { if (actionRef.value) {
@ -228,8 +238,8 @@ const prev = () => {
const saveClick = () => save(); const saveClick = () => save();
const onDeviceSave = (_data: any, _detail: any) => { const onDeviceSave = (_data: any, _detail: any, obj?: any) => {
Object.assign(DeviceModel, _data); Object.assign(DeviceModel, { ..._data, ...obj });
DeviceModel.deviceDetail = _detail; DeviceModel.deviceDetail = _detail;
}; };
@ -241,6 +251,28 @@ watch(
detail(newValue.productId).then((resp) => { detail(newValue.productId).then((resp) => {
if (resp.status === 200) { if (resp.status === 200) {
DeviceModel.productDetail = resp.result; DeviceModel.productDetail = resp.result;
if (
DeviceModel.selector === 'tag' &&
DeviceModel.selectorValues[0]?.value
) {
const metadata = JSON.parse(
DeviceModel.productDetail?.metadata || '{}',
);
const tags = metadata.tags || [];
const arr = DeviceModel.selectorValues[0]?.value
.filter((item: any) => !!item.value)
.map((item: any) => {
return {
column:
tags.find(
(i: any) => i.id === item.column,
)?.name || item.column,
type: item.type,
value: item.value,
};
});
DeviceModel.tagList = arr;
}
} }
}); });
} }

View File

@ -19,7 +19,7 @@
:options='columnOptions' :options='columnOptions'
icon='icon-zhihangdongzuoxie-1' icon='icon-zhihangdongzuoxie-1'
type='column' type='column'
value-name='column' value-name='id'
label-name='fullName' label-name='fullName'
placeholder='请选择参数' placeholder='请选择参数'
v-model:value='paramsValue.column' v-model:value='paramsValue.column'
@ -39,21 +39,27 @@
v-if='showDouble' v-if='showDouble'
icon='icon-canshu' icon='icon-canshu'
placeholder='参数值' placeholder='参数值'
value-name='id'
label-name='name'
:options='valueOptions' :options='valueOptions'
:metricOptions='metricOption' :metricOptions='columnOptions'
:tabsOptions='tabsOptions' :tabsOptions='tabsOptions'
v-model:value='paramsValue.value.value' v-model:value='paramsValue.value.value'
v-model:source='paramsValue.value.source' v-model:source='paramsValue.value.source'
@select='valueSelect'
/> />
<ParamsDropdown <ParamsDropdown
v-else v-else
icon='icon-canshu' icon='icon-canshu'
placeholder='参数值' placeholder='参数值'
value-name='id'
label-name='name'
:options='valueOptions' :options='valueOptions'
:metricOptions='metricOption' :metricOptions='columnOptions'
:tabsOptions='tabsOptions' :tabsOptions='tabsOptions'
v-model:value='paramsValue.value.value' v-model:value='paramsValue.value.value'
v-model:source='paramsValue.value.source' v-model:source='paramsValue.value.source'
@select='valueSelect'
/> />
<j-popconfirm title='确认删除?' @confirm='onDelete'> <j-popconfirm title='确认删除?' @confirm='onDelete'>
<div v-show='showDelete' class='button-delete'> <AIcon type='CloseOutlined' /></div> <div v-show='showDelete' class='button-delete'> <AIcon type='CloseOutlined' /></div>
@ -74,7 +80,6 @@ import DropdownButton from '../../components/DropdownButton'
import { getOption } from '../../components/DropdownButton/util' import { getOption } from '../../components/DropdownButton/util'
import ParamsDropdown, { DoubleParamsDropdown } from '../../components/ParamsDropdown' import ParamsDropdown, { DoubleParamsDropdown } from '../../components/ParamsDropdown'
import { inject } from 'vue' import { inject } from 'vue'
import { ContextKey } from '../../components/Terms/util'
import { useSceneStore } from 'store/scene' import { useSceneStore } from 'store/scene'
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
@ -112,7 +117,7 @@ const props = defineProps({
type: Number, type: Number,
default: 0 default: 0
}, },
branchName: { actionName: {
type: Number, type: Number,
default: 0 default: 0
}, },
@ -120,6 +125,10 @@ const props = defineProps({
type: Number, type: Number,
default: 0 default: 0
}, },
branchName: {
type: Number,
default: 0
},
value: { value: {
type: Object as PropType<TermsType>, type: Object as PropType<TermsType>,
default: () => ({ default: () => ({
@ -144,27 +153,22 @@ const paramsValue = reactive<TermsType>({
}) })
const showDelete = ref(false) const showDelete = ref(false)
const columnOptions: any = inject(ContextKey) // const columnOptions: any = inject('filter-params') //
const termTypeOptions = ref<Array<{ id: string, name: string}>>([]) // const termTypeOptions = ref<Array<{ id: string, name: string}>>([]) //
const valueOptions = ref<any[]>([]) // const valueOptions = ref<any[]>([]) //
const metricOption = ref<any[]>([]) // const arrayParamsKey = ['nbtw', 'btw', 'in', 'nin']
const tabsOptions = ref<Array<TabsOption>>([{ label: '内置参数', key: 'upper', component: 'tree' }])
// { label: '', key: 'fixed', component: 'string' },
const tabsOptions = ref<Array<TabsOption>>(
[
{ label: '手动输入', key: 'fixed', component: 'string' },
{ label: '内置参数', key: 'upper', component: 'tree' }
]
)
const handOptionByColumn = (option: any) => { const handOptionByColumn = (option: any) => {
if (option) { if (option) {
termTypeOptions.value = option.termTypes || [] termTypeOptions.value = option.termTypes || []
metricOption.value = option.metrics || []
tabsOptions.value.length = 1
tabsOptions.value[0].component = option.dataType tabsOptions.value[0].component = option.dataType
if (option.metrics && option.metrics.length) {
tabsOptions.value.push(
{ label: '指标值', key: 'metric', component: 'select' }
)
}
if (option.dataType === 'boolean') { if (option.dataType === 'boolean') {
valueOptions.value = [ valueOptions.value = [
{ label: '是', value: true }, { label: '是', value: true },
@ -177,23 +181,17 @@ const handOptionByColumn = (option: any) => {
} }
} else { } else {
termTypeOptions.value = [] termTypeOptions.value = []
metricOption.value = []
valueOptions.value = [] valueOptions.value = []
} }
} }
watchEffect(() => { watchEffect(() => {
const option = getOption(columnOptions.value, paramsValue.column, 'column') const option = getOption(columnOptions.value, paramsValue.column, 'id')
handOptionByColumn(option) handOptionByColumn(option)
}) })
const showDouble = computed(() => { const showDouble = computed(() => {
const isRange = paramsValue.termType ? ['nbtw', 'btw', 'in', 'nin'].includes(paramsValue.termType) : false const isRange = paramsValue.termType ? arrayParamsKey.includes(paramsValue.termType) : false
if (metricOption.value.length) {
metricOption.value = metricOption.value.filter(item => isRange ? item.range : !item.range)
} else {
metricOption.value = []
}
return isRange return isRange
}) })
@ -218,14 +216,19 @@ const columnSelect = () => {
emit('update:value', { ...paramsValue }) emit('update:value', { ...paramsValue })
} }
const termsTypeSelect = () => { const termsTypeSelect = (e: { key: string }) => {
const value = arrayParamsKey.includes(e.key) ? [ undefined, undefined ] : undefined
paramsValue.value = { paramsValue.value = {
source: tabsOptions.value[0].key, source: tabsOptions.value[0].key,
value: undefined value: value
} }
emit('update:value', { ...paramsValue }) emit('update:value', { ...paramsValue })
} }
const valueSelect = () => {
emit('update:value', { ...paramsValue })
}
const termAdd = () => { const termAdd = () => {
const terms = { const terms = {
column: undefined, column: undefined,
@ -237,11 +240,11 @@ const termAdd = () => {
type: 'and', type: 'and',
key: `params_${new Date().getTime()}` key: `params_${new Date().getTime()}`
} }
formModel.value.branches?.[props.branchName]?.then?.[props.thenName]?.actions?.[props.name].terms?.push(terms) formModel.value.branches?.[props.branchName]?.then?.[props.thenName]?.actions?.[props.actionName].terms?.[props.termsName].terms?.push(terms)
} }
const onDelete = () => { const onDelete = () => {
formModel.value.branches?.[props.branchName]?.then?.[props.thenName]?.actions?.[props.name].terms?.splice(props.name, 1) formModel.value.branches?.[props.branchName]?.then?.[props.thenName]?.actions?.[props.actionName].terms?.[props.termsName].terms?.splice(props.name, 1)
} }
nextTick(() => { nextTick(() => {

View File

@ -1,11 +1,203 @@
<template> <template>
<div class='terms-params filter-group'>
<div class='terms-params-warp'>
<div v-if='!isFirst' class='term-type-warp'>
<DropdownButton
:options='[
{ label: "并且", value: "and" },
{ label: "或者", value: "or" },
]'
type='type'
v-model:value='formModel.branches[branchName].then[thenName].actions[actionName].terms[name].type'
/>
</div>
<div
class='terms-params-content'
@mouseover='mouseover'
@mouseout='mouseout'
>
<j-popconfirm
title='确认删除?'
@confirm='onDelete'
>
<div v-show='showDelete' class='terms-params-delete'>
<AIcon type='CloseOutlined' />
</div>
</j-popconfirm>
<j-form-item
v-for='(item, index) in termsOptions'
:key='item.key'
:name='["branches", branchName, "then", thenName, "actions", actionName, "terms", name, "terms", index]'
:rules='rules'
>
<FilterItem
v-model:value='formModel.branches[branchName].then[thenName].actions[actionName].terms[name].terms[index]'
:isFirst='index === 0'
:isLast='index === termsOptions.length - 1'
:showDeleteBtn='termsOptions.length !== 1'
:name='index'
:termsName='name'
:actionName='actionName'
:thenName='thenName'
:branchName='branchName'
/>
</j-form-item>
</div>
<div v-if='isLast' class='terms-group-add' @click='addTerms'>
<div class='terms-content'>
<AIcon type='PlusOutlined' style='font-size: 12px;padding-right: 4px;' />
<span>分组</span>
</div>
</div>
</div>
</div>
</template> </template>
<script setup lang='ts' name='FilterGroup'> <script setup lang='ts' name='FilterGroup'>
export default { import { storeToRefs } from 'pinia'
name: 'FilterGroup' import { useSceneStore } from 'store/scene'
import DropdownButton from '../../components/DropdownButton'
import FilterItem from './FilterCondition.vue'
import { isArray } from 'lodash-es'
import { provide } from 'vue'
import { randomString } from '@/utils/utils'
import { useParams } from '@/views/rule-engine/Scene/Save/util'
const sceneStore = useSceneStore()
const { data: formModel } = storeToRefs(sceneStore)
const props = defineProps({
isFirst: {
type: Boolean,
default: true
},
isLast: {
type: Boolean,
default: true
},
showDeleteBtn: {
type: Boolean,
default: true
},
name: {
type: Number,
default: 0
},
branchName: {
type: Number,
default: 0
},
thenName: {
type: Number,
default: 0
},
actionName: {
type: Number,
default: 0
}
})
const { columnOptions } = useParams({
branch: props.branchName,
branchGroup: props.thenName,
action: props.actionName
}, [
formModel.value.branches![props.branchName].then[props.thenName].actions[props.actionName]
])
provide('filter-params', columnOptions)
const termsOptions = computed(() => {
return formModel.value.branches![props.branchName].then[props.thenName].actions[props.actionName].terms?.[props.name].terms
})
const showDelete = ref(false)
const mouseover = () => {
showDelete.value = true
} }
const mouseout = () => {
showDelete.value = false
}
const addTerms = () => {
const item: any = {
type: 'and',
key: randomString(),
terms: [
{
column: undefined,
value: {
type: 'fixed',
value: undefined
},
termType: undefined,
type: 'and',
key: randomString()
}
]
}
formModel.value
.branches![props.branchName]
.then[props.thenName]
.actions[props.actionName]
.terms?.push(item)
}
const onDelete = () => {
formModel.value
.branches![props.branchName]
.then[props.thenName]
.actions[props.actionName]
.terms?.splice(props.name, 1)
}
const rules = [
{
validator(_: any, v?: Record<string, any>) {
console.log('-----v',v)
if (v !== undefined) {
if (!Object.keys(v).length) {
return Promise.reject(new Error('该数据已发生变更,请重新配置'))
}
if (!v.column) {
return Promise.reject(new Error('请选择参数'))
}
if (!v.termType) {
return Promise.reject(new Error('请选择操作符'))
}
if (v.value === undefined) {
return Promise.reject(new Error('请选择或输入参数值'))
} else {
if (
isArray(v.value.value) &&
v.value.value.some((_v: any) => _v === undefined)
) {
return Promise.reject(new Error('请选择或输入参数值'))
} else if (v.value.value === undefined) {
return Promise.reject(new Error('请选择或输入参数值'))
}
}
} else {
return Promise.reject(new Error('请选择参数'))
}
return Promise.resolve()
}
}
]
// watchEffect(() => {
// if (formModel.value.branches![props.branchName].then[props.thenName].actions[props.actionName]) {
// getParams()
// }
// })
</script> </script>
<style scoped> <style scoped>

View File

@ -19,14 +19,16 @@
v-if="data?.executor === 'alarm'" v-if="data?.executor === 'alarm'"
> >
<template v-if="data?.alarm?.mode === 'trigger'"> <template v-if="data?.alarm?.mode === 'trigger'">
满足条件后将触发<j-button style="padding: 0;" 满足条件后将触发<j-button
style="padding: 0"
type="link" type="link"
@click.stop="triggerVisible = true" @click.stop="triggerVisible = true"
>关联此场景的告警</j-button >关联此场景的告警</j-button
> >
</template> </template>
<template v-else> <template v-else>
满足条件后将解除<j-button style="padding: 0;" 满足条件后将解除<j-button
style="padding: 0"
type="link" type="link"
@click.stop="triggerVisible = true" @click.stop="triggerVisible = true"
>关联此场景的告警</j-button >关联此场景的告警</j-button
@ -278,10 +280,16 @@
/> />
{{ data?.options?.type }} {{ data?.options?.type }}
<span <span
v-for="i in data?.options?.taglist || []" v-for="(i, _index) in data?.options?.taglist ||
[]"
:key="i.value" :key="i.value"
> >
{{ i.type }} {{
_index !== 0 &&
_index !==
(data?.options?.taglist || []).length &&
i.type
}}
{{ i.name }}{{ i.value }} {{ i.name }}{{ i.value }}
</span> </span>
{{ data?.options?.productName }} {{ data?.options?.productName }}
@ -318,19 +326,33 @@
</j-popconfirm> </j-popconfirm>
</div> </div>
<template v-if="!isLast && type === 'serial'"> <template v-if="!isLast && type === 'serial'">
<div :class='["actions-item-filter-warp", termsOptions.length ? "filter-border" : ""]'> <div
<template v-if='termsOptions.length'> :class="[
<div class='actions-item-filter-warp-tip'> 'actions-item-filter-warp',
满足此条件后执行后续动作 termsOptions.length ? 'filter-border' : '',
]"
>
<template v-if="termsOptions.length">
<div class="actions-item-filter-warp-tip">
满足此条件后执行后续动作
</div>
<div class="actions-item-filter-overflow">
<FilterGroup
v-for="(item, index) in termsOptions"
:key="item.key"
:branchName="branchesName"
:thenName="thenName"
:actionName="name"
:name="index"
:isLast="index === termsOptions.length - 1"
:isFirst="index === 0"
/>
</div>
</template>
<div v-else class="filter-add-button">
<AIcon type="PlusOutlined" style="padding-right: 4px" />
<span>添加过滤条件</span>
</div> </div>
<div class='actions-item-filter-overflow'>
</div>
</template>
<div v-else class='filter-add-button'>
<AIcon type='PlusOutlined' style='padding-right: 4px;' />
<span>添加过滤条件</span>
</div>
</div> </div>
</template> </template>
<!-- 编辑 --> <!-- 编辑 -->
@ -370,7 +392,8 @@ import ActionTypeComponent from '../Modal/ActionTypeComponent.vue';
import TriggerAlarm from '../TriggerAlarm/index.vue'; import TriggerAlarm from '../TriggerAlarm/index.vue';
import { useSceneStore } from '@/store/scene'; import { useSceneStore } from '@/store/scene';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { iconMap, itemNotifyIconMap, typeIconMap } from './util' import { iconMap, itemNotifyIconMap, typeIconMap } from './util';
import FilterGroup from './FilterGroup.vue';
const sceneStore = useSceneStore(); const sceneStore = useSceneStore();
const { data: _data } = storeToRefs(sceneStore); const { data: _data } = storeToRefs(sceneStore);
@ -381,8 +404,8 @@ const props = defineProps({
default: 0, default: 0,
}, },
thenName: { thenName: {
type: Number, type: Number,
default: 0, default: 0,
}, },
name: { name: {
type: Number, type: Number,
@ -412,11 +435,15 @@ const triggerVisible = ref<boolean>(false);
const actionType = ref(''); const actionType = ref('');
const termsOptions = computed(() => { const termsOptions = computed(() => {
if (!props.parallel) { // if (!props.parallel) {
return _data.value.branches![props.branchesName].then?.[props.thenName].actions?.[props.name].terms || [] //
} return (
return [] _data.value.branches![props.branchesName].then?.[props.thenName]
}) .actions?.[props.name].terms || []
);
}
return [];
});
const onDelete = () => { const onDelete = () => {
emit('delete'); emit('delete');
@ -454,6 +481,14 @@ const onPropsCancel = () => {
actionType.value = ''; actionType.value = '';
}; };
watch(
() => props.data,
() => {
if (props.data) {
}
},
{ immediate: true, deep: true },
);
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@ -574,16 +609,16 @@ const onPropsCancel = () => {
} }
.actions-item-filter-warp-tip { .actions-item-filter-warp-tip {
position: absolute; position: absolute;
top: 0; top: 0;
left: 16px; left: 16px;
z-index: 2; z-index: 2;
color: rgba(0, 0, 0, 0.55); color: rgba(0, 0, 0, 0.55);
font-weight: 800; font-weight: 800;
font-size: 14px; font-size: 14px;
line-height: 1; line-height: 1;
background-color: #fff; background-color: #fff;
transform: translateY(-50%); transform: translateY(-50%);
} }
.actions-item-filter-overflow { .actions-item-filter-overflow {
@ -594,11 +629,11 @@ const onPropsCancel = () => {
row-gap: 16px; row-gap: 16px;
} }
.filter-add-button{ .filter-add-button {
width: 100%; width: 100%;
color: rgba(0, 0, 0, 0.3); color: rgba(0, 0, 0, 0.3);
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
} }
.terms-params { .terms-params {
@ -615,28 +650,6 @@ const onPropsCancel = () => {
align-items: baseline; align-items: baseline;
} }
.terms-params-content {
position: relative;
display: flex;
background-color: #fafafa;
border: unset;
row-gap: 16px;
.terms-params-item {
display: flex;
align-items: center;
}
.ant-form-item {
margin-bottom: 8px;
&:not(:first-child) {
.ant-form-item-explain-error {
padding-left: 80px !important;
}
}
}
}
.term-type-warp { .term-type-warp {
// display: inline-block; // display: inline-block;
width: 50px; width: 50px;

View File

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<template v-if="actionType === 'device'"> <template v-if="actionType === 'device'">
<Device v-bind="props" :value="data?.device" @cancel="onCancel" @save="onPropsOk" :thenName="branchesName" /> <Device v-bind="props" :value="data?.device" @cancel="onCancel" @save="onPropsOk" />
</template> </template>
<template v-else-if="actionType === 'notify'"> <template v-else-if="actionType === 'notify'">
<Notify :options="data?.options" :value="data?.notify" @cancel="onCancel" @save="onPropsOk" /> <Notify :options="data?.options" :value="data?.notify" @cancel="onCancel" @save="onPropsOk" />
@ -24,7 +24,7 @@ const props = defineProps({
type: Number, type: Number,
default: 0, default: 0,
}, },
branchGroup: { thenName: {
type: Number, type: Number,
default: 0, default: 0,
}, },

View File

@ -47,7 +47,7 @@ const props = defineProps({
type: Number, type: Number,
default: 0, default: 0,
}, },
branchGroup: { thenName: {
type: Number, type: Number,
default: 0, default: 0,
}, },

View File

@ -279,7 +279,6 @@ const onChange = (
} else { } else {
_values = getObj(_source, _value, isRelation); _values = getObj(_source, _value, isRelation);
} }
console.log(_values, '_values')
emit('update:value', _values); emit('update:value', _values);
emit('change', { sendTo: _names.filter((item) => !!item).join(',') }); emit('change', { sendTo: _names.filter((item) => !!item).join(',') });
}; };

View File

@ -2,6 +2,8 @@
<ParamsDropdown <ParamsDropdown
v-model:value='myValue[0]' v-model:value='myValue[0]'
v-model:source='mySource' v-model:source='mySource'
:valueName='valueName'
:labelName='labelName'
:options='options' :options='options'
:icon='icon' :icon='icon'
:placeholder='placeholder' :placeholder='placeholder'
@ -12,6 +14,8 @@
<ParamsDropdown <ParamsDropdown
v-model:value='myValue[1]' v-model:value='myValue[1]'
v-model:source='mySource' v-model:source='mySource'
:valueName='valueName'
:labelName='labelName'
:icon='icon' :icon='icon'
:placeholder='placeholder' :placeholder='placeholder'
:tabs-options='tabsOptions' :tabs-options='tabsOptions'

View File

@ -81,7 +81,7 @@ import ValueItem from '@/components/ValueItem/index.vue'
import type { ValueType } from './typings' import type { ValueType } from './typings'
import { defaultSetting } from './typings' import { defaultSetting } from './typings'
import { DropdownMenus, DropdownTimePicker} from '../DropdownButton' import { DropdownMenus, DropdownTimePicker} from '../DropdownButton'
import { getComponent, getOption } from '../DropdownButton/util' import { getOption } from '../DropdownButton/util'
type Emit = { type Emit = {
(e: 'update:value', data: ValueType): void (e: 'update:value', data: ValueType): void
@ -109,29 +109,26 @@ nextTick(() => {
const tabsChange = (e: string) => { const tabsChange = (e: string) => {
mySource.value = e mySource.value = e
myValue.value = undefined myValue.value = undefined
}
const updateValue = () => {
emit('update:source', mySource.value) emit('update:source', mySource.value)
emit('update:value', myValue.value) emit('update:value', undefined)
emit('select', {})
} }
const treeSelect = (e: any) => { const treeSelect = (v: any, option: any) => {
const node = option.node
visible.value = false visible.value = false
label.value = e.fullname || e.name label.value = node.fullname || node.name
emit('update:value', e[props.valueName]) emit('update:value', node[props.valueName])
emit('select', e) emit('select', node)
} }
const valueItemChange = (e: string) => { const valueItemChange = (e: string) => {
console.log('valueItemSelect', e)
label.value = e label.value = e
emit('update:value', e) emit('update:value', e)
emit('select', e) emit('select', e)
} }
const onSelect = (e: string, option: any) => { const onSelect = (e: string, option: any) => {
console.log(e, option)
visible.value = false visible.value = false
label.value = option.label label.value = option.label
emit('update:value', e) emit('update:value', e)
@ -150,7 +147,8 @@ const visibleChange = (v: boolean) => {
} }
watchEffect(() => { watchEffect(() => {
const option = getOption(props.options, props.value as string, props.valueName) // label const _options = props.source === 'upper' ? props.metricOptions : props.options
const option = getOption(_options, props.value as string, props.valueName) // label
myValue.value = props.value myValue.value = props.value
mySource.value = props.source mySource.value = props.source
if (option) { if (option) {

View File

@ -149,6 +149,7 @@ const termTypeOptions = ref<Array<{ id: string, name: string}>>([]) // 条件值
const valueOptions = ref<any[]>([]) // const valueOptions = ref<any[]>([]) //
const metricOption = ref<any[]>([]) // termType const metricOption = ref<any[]>([]) // termType
const tabsOptions = ref<Array<TabsOption>>([{ label: '手动输入', key: 'manual', component: 'string' }]) const tabsOptions = ref<Array<TabsOption>>([{ label: '手动输入', key: 'manual', component: 'string' }])
const arrayParamsKey = ['nbtw', 'btw', 'in', 'nin']
let metricsCacheOption: any[] = [] // let metricsCacheOption: any[] = [] //
const handOptionByColumn = (option: any) => { const handOptionByColumn = (option: any) => {
@ -187,7 +188,7 @@ watchEffect(() => {
}) })
const showDouble = computed(() => { const showDouble = computed(() => {
const isRange = paramsValue.termType ? ['nbtw', 'btw', 'in', 'nin'].includes(paramsValue.termType) : false const isRange = paramsValue.termType ? arrayParamsKey.includes(paramsValue.termType) : false
if (metricsCacheOption.length) { if (metricsCacheOption.length) {
metricOption.value = metricsCacheOption.filter(item => isRange ? item.range : !item.range) metricOption.value = metricsCacheOption.filter(item => isRange ? item.range : !item.range)
} else { } else {
@ -217,10 +218,11 @@ const columnSelect = () => {
emit('update:value', { ...paramsValue }) emit('update:value', { ...paramsValue })
} }
const termsTypeSelect = () => { const termsTypeSelect = (e: { key: string }) => {
const value = arrayParamsKey.includes(e.key) ? [ undefined, undefined ] : undefined
paramsValue.value = { paramsValue.value = {
source: tabsOptions.value[0].key, source: tabsOptions.value[0].key,
value: undefined value: value
} }
emit('update:value', { ...paramsValue }) emit('update:value', { ...paramsValue })
} }

View File

@ -47,7 +47,7 @@ import { storeToRefs } from 'pinia';
import { useSceneStore } from 'store/scene' import { useSceneStore } from 'store/scene'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import { provide } from 'vue' import { provide } from 'vue'
import { ContextKey } from './util' import { ContextKey, handleParamsData } from './util'
import { getParseTerm } from '@/api/rule-engine/scene' import { getParseTerm } from '@/api/rule-engine/scene'
import type { FormModelType } from '@/views/rule-engine/Scene/typings' import type { FormModelType } from '@/views/rule-engine/Scene/typings'
import Branches from './Branchs.vue' import Branches from './Branchs.vue'
@ -65,17 +65,6 @@ const change = (e: boolean) => {
open.value = e open.value = e
} }
const handleParamsData = (data: any[]): any[] => {
return data?.map(item => {
return {
...item,
key: item.column,
disabled: !!item.children,
children: handleParamsData(item.children)
}
}) || []
}
const queryColumn = (dataModel: FormModelType) => { const queryColumn = (dataModel: FormModelType) => {
const cloneDevice = cloneDeep(dataModel) const cloneDevice = cloneDeep(dataModel)
cloneDevice.branches = cloneDevice.branches?.filter(item => !!item) cloneDevice.branches = cloneDevice.branches?.filter(item => !!item)

View File

@ -33,6 +33,19 @@
} }
} }
.terms-params-delete {
.deleteBtn();
&.danger {
color: #e50012;
background-color: rgba(229, 0, 18, 0.1);
}
&.filter-terms-params-delete {
transform: translateY(6px);
}
}
.actions-terms { .actions-terms {
.actions-terms-warp { .actions-terms-warp {
display: flex; display: flex;
@ -115,11 +128,8 @@
.terms-params-content { .terms-params-content {
position: relative; position: relative;
display: flex; display: flex;
// flex-wrap: wrap; padding: 8px 8px 0px 8px;
padding: 8px;
padding-bottom: 0;
border: 1px dashed #e0e0e0; border: 1px dashed #e0e0e0;
//background-color: #fafafa;
border-radius: 6px; border-radius: 6px;
row-gap: 16px; row-gap: 16px;
.terms-params-item { .terms-params-item {
@ -162,6 +172,19 @@
border-radius: 2px; border-radius: 2px;
} }
} }
&.filter-group {
.terms-params-content {
background-color: #fafafa;
border: none;
}
.terms-params-delete {
transform: translateY(6px);
}
}
} }
.terms-params-item { .terms-params-item {
@ -189,15 +212,4 @@
} }
} }
.terms-params-delete {
.deleteBtn();
&.danger {
color: #e50012;
background-color: rgba(229, 0, 18, 0.1);
}
&.filter-terms-params-delete {
transform: translateY(6px);
}
}

View File

@ -1 +1,12 @@
export const ContextKey = 'columnOptions' export const ContextKey = 'columnOptions'
export const handleParamsData = (data: any[], key: string = 'column'): any[] => {
return data?.map(item => {
return {
...item,
key: item[key],
disabled: !!item.children,
children: handleParamsData(item.children, key)
}
}) || []
}

View File

@ -0,0 +1,49 @@
import { queryBuiltInParams } from '@/api/rule-engine/scene'
import { handleParamsData } from './components/Terms/util'
import { useSceneStore } from 'store/scene'
import { storeToRefs } from 'pinia'
import type { FormModelType } from '@/views/rule-engine/Scene/typings'
interface Params {
branch: number
branchGroup: number
action: number
}
export const getParams = (params: Params, sceneModel: FormModelType): Promise<any[]> => {
return new Promise(res => {
const data = sceneModel.branches!.filter(item => !!item)
queryBuiltInParams({
...sceneModel,
branches: data
}, params).then(resp => {
if (resp.success) {
res(resp.result as any[])
}
})
})
}
/**
* @param params
*/
export const useParams = (params: Params, effect: any[] = []) => {
const sceneStore = useSceneStore()
const { data: formModel } = storeToRefs(sceneStore)
const columnOptions = ref<any[]>([])
const handleParams = async () => {
const _data = await getParams(params, formModel.value)
columnOptions.value = handleParamsData(_data, 'id')
}
watchEffect(() => {
if (effect[0]) {
handleParams()
}
})
return {
columnOptions
}
}

View File

@ -2,7 +2,7 @@
<div class="request-table-container"> <div class="request-table-container">
<j-table <j-table
:columns="columns" :columns="columns"
:datj-source="tableData" :data-source="tableData"
:pagination="false" :pagination="false"
size="small" size="small"
bordered bordered

View File

@ -94,12 +94,13 @@
<AIcon type="PlusOutlined" />新增 <AIcon type="PlusOutlined" />新增
</j-button> </j-button>
</div> </div>
<MonacoEditor <j-monaco-editor
v-if="refStr" v-if="refStr"
v-model:modelValue="requestBody.code"
style="height: 300px; width: 100%"
theme="vs"
ref="editorRef" ref="editorRef"
language="json"
style="height: 100%"
theme="vs"
v-model:modelValue="requestBody.code"
/> />
</div> </div>
</div> </div>
@ -115,7 +116,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { JsonViewer } from 'vue3-json-viewer'; import { JsonViewer } from 'vue3-json-viewer';
import 'vue3-json-viewer/dist/index.css'; import 'vue3-json-viewer/dist/index.css';
import MonacoEditor from '@/components/MonacoEditor/index.vue';
import type { apiDetailsType } from '../typing'; import type { apiDetailsType } from '../typing';
import InputCard from './InputCard.vue'; import InputCard from './InputCard.vue';
import { cloneDeep, toLower } from 'lodash'; import { cloneDeep, toLower } from 'lodash';

View File

@ -98,11 +98,11 @@
<div> <div>
<p>使用和签名相同的算法(不需要对响应结果排序)</p> <p>使用和签名相同的算法(不需要对响应结果排序)</p>
<div> <div>
<MonacoEditor <j-monaco-editor
style="width: 100%; height: 620px"
theme="vs-dark"
language="java" language="java"
v-model="javaStr1" style="height: 370px"
theme="vs-dark"
v-model:modelValue="javaStr1"
/> />
</div> </div>
</div> </div>
@ -119,20 +119,20 @@
<h3>添加 SDK 依赖</h3> <h3>添加 SDK 依赖</h3>
<div class="h3-text">将以下Maven依赖加入到pom.xml文件中</div> <div class="h3-text">将以下Maven依赖加入到pom.xml文件中</div>
<div> <div>
<MonacoEditor <j-monaco-editor
style="width: 100%; height: 100px"
theme="vs-dark"
v-model="javaStr2"
language="java" language="java"
style="height: 370px"
theme="vs-dark"
v-model:modelValue="javaStr2"
/> />
</div> </div>
<h3>SDK 客户端的初始化和请求方式</h3> <h3>SDK 客户端的初始化和请求方式</h3>
<div> <div>
<MonacoEditor <j-monaco-editor
style="width: 100%; height: 370px"
theme="vs-dark"
v-model="javaStr"
language="java" language="java"
style="height: 370px"
theme="vs-dark"
v-model:modelValue="javaStr"
/> />
</div> </div>
</div> </div>
@ -141,7 +141,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { getImage } from '@/utils/comm'; import { getImage } from '@/utils/comm';
import MonacoEditor from '@/components/MonacoEditor/index.vue';
const data = [ const data = [
{ {

View File

@ -260,14 +260,17 @@ function menuChange(
// //
if (row.buttons && row.buttons.length > 0) setStatus(row, 'buttons'); if (row.buttons && row.buttons.length > 0) setStatus(row, 'buttons');
else setStatus(row, 'children'); else setStatus(row, 'children');
// //
if (row.selectAccesses !== undefined) { if (row.accessSupport && row.accessSupport.value === 'support') {
if (!row.granted) { //
if (row.selectAccesses && !row.granted && !row.indeterminate)
row.selectAccesses = ''; row.selectAccesses = '';
} else if (row.selectAccesses === '') { // 'creator'
else if (!row.selectAccesses && (row.granted || row.indeterminate))
row.selectAccesses = 'creator'; row.selectAccesses = 'creator';
}
} }
// //
if (row.parentId) { if (row.parentId) {
// // // //
@ -401,3 +404,11 @@ type tableItemType = {
assetAccesses?: any[]; assetAccesses?: any[];
}; };
</script> </script>
<style lang="less" scoped>
.permiss-tree-container {
:deep(.ant-checkbox-wrapper) {
margin-left: 0;
}
}
</style>

1417
yarn.lock

File diff suppressed because it is too large Load Diff