fix: bug#28560【通知配置】微信-企业消息的同步用户页面,列表字段展示错误,【钉钉用户名】需改为【企业微信用户名】

* feat: 社区版功能过滤

* fix: bug#28523 【设备】设备运行状态界面,设备在线状态下,点击断开链接,事件展示错误

* fix: bug#28521 产品(设备)需按照当前产品选择协议来确定是否展示远程升级

* feat: 2.2新增功能社区版功能屏蔽

* feat: 场景联动定时场景按日历社区版屏蔽

* fix: bug#28524 【产品】产品选择设备接入网关,有多个配置时,只展示了一个

* fix: 修改iot平台主题色

* fix: 优化设备接入配置

* fix: 兼容2.1版本创建的菜单数据

* fix: 菜单配置兼容2.1菜单

* fix: bug#28524 【产品】产品选择设备接入网关,有多个配置时,只展示了一个

* fix: bug#28520【设备】当配置未开启时,前端应不展示配置按钮

* fix: bug#28533仪表盘本年流量消耗界面展示需优化

* fix: bug#28520、28534【设备】设备详情-物模型-其他配置 在点击配置时界面提示错误、【设备】当配置未开启时,前端应不展示配置按钮

* fix: bug#28531【设备】详情中查看预处理数据时提示错误

* fix: bug#28545【菜单管理】新增菜单,权限控制处无法正常加载出权限

* fix: 菜单配置->菜单权限同步功能

* fix: bug#28520【设备】当配置未开启时,前端应不展示配置按钮

* fix(media): bug#28595屏蔽固定地址通道列表的厂商字段

* fix(NoticeRule): 文字组织替换部门

* fix(Alarm): bug#28571手动触发展示说明

* fix(media): bug#28553界面展示异常

* fix(iotCard): bug#28567

* fix(media): bug#28595屏蔽固定地址通道列表的厂商字段

* fix: bug#28560【通知配置】微信-企业消息的同步用户页面,列表字段展示错误,【钉钉用户名】需改为【企业微信用户名】

* fix: bug#28544【通知配置】用户自己创建的数据,没有权限调试,报错:暂无权限,请联系管理员!

* fix: bug#28549【采集器】在点位进行批量操作时,开启推送控制,进行保存异常

* fix: bug#28540【组织管理】设备资产分配页,设备列表形式展示,资产权限显示不完整

* fix: bug#28537【产品】新增的网关设备,接入方式选择"边缘网关接入"时,物模型中功能定义界面展示异常

* fix: bug#28550【远程升级】升级任务中的任务详情列表中文案修改

* fix: bug#28551【通知模版】和【通知配置】两者保存的查询条件共用了

* fix: bug#28560【通知配置】微信-企业消息的同步用户页面,列表字段展示错误,【钉钉用户名】需改为【企业微信用户名】
This commit is contained in:
XieYongHong 2024-08-12 16:11:27 +08:00 committed by GitHub
parent 3da54bb59b
commit 2b3e3280ac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 2254 additions and 2106 deletions

View File

@ -1,7 +1,4 @@
export default {
theme: {
'primary-color': '#1677FF',
},
logo: '/favicon.ico', // 浏览器标签页logo
title: 'Jetlinks', // 浏览器标签页title
layout: {

View File

@ -33,7 +33,7 @@ watch(() => JSON.stringify(route.query || {}), () => {
ConfigProvider.config({
theme: {
primaryColor: "#315efb"
primaryColor: "#1677FF"
}
})
</script>

View File

@ -662,7 +662,7 @@ export const queryDeviceThreshold = (productId: string, deviceId: string, prope
* @param productId
* @param propertyId
*/
export const queryProductThreshold = (productId: string, propertyId: string) => server.get(`/message/preprocessor/product/${productId}/property/${propertyId}`)
export const queryProductThreshold = (productId: string, propertyId: string,hiddenError:boolean) => server.get(`/message/preprocessor/product/${productId}/property/${propertyId}`,{},{},hiddenError)
/**
* -

View File

@ -35,8 +35,8 @@ const defaultOwnParams = [
{
terms: [
{
value: "%show\":true%",
termType: "like",
value: "%show\":false%",
termType: "nlike",
column: "options"
}
],

View File

@ -2,6 +2,7 @@ import { defineStore } from 'pinia';
import { systemVersion } from '@/api/comm'
import { useMenuStore } from './menu'
import {getDetails_api, settingDetail} from '@/api/system/basis';
import { queryProductThreshold } from '@/api/device/instance'
import type { ConfigInfoType } from '@/views/system/Basis/typing';
import { LocalStore } from '@/utils/comm'
import { SystemConst } from '@/utils/consts'
@ -24,6 +25,7 @@ type SystemStateType = {
pure: boolean
}
calendarTagColor:Map<string,string>
showThreshold:boolean
}
export const useSystem = defineStore('system', {
@ -42,11 +44,13 @@ export const useSystem = defineStore('system', {
collapsed: false,
pure: false
},
calendarTagColor:new Map()
calendarTagColor:new Map(),
showThreshold: true
}),
actions: {
getSystemVersion(): Promise<any[]> {
this.getSystemConfig();
this.getThreshold()
return new Promise(async (res, rej) => {
const resp = await systemVersion()
if (resp.success && resp.result) {
@ -100,6 +104,13 @@ export const useSystem = defineStore('system', {
this.calendarTagColor.set(i, answer.result[i]);
});
}
},
async getThreshold(){
await queryProductThreshold('test','test',true).catch((res:any)=>{
if(res.response.status === 404){
this.showThreshold = false
}
})
}
}
})

View File

@ -2,10 +2,6 @@
@import 'jetlinks-ui-components/es/style/variable.less';
@import './scrollbar.less';
html {
--ant-primary-color: #1677FF !important;
}
.ellipsisFn(@num: 1, @width: 100%) {
display: -webkit-box;
max-width: @width;

View File

@ -90,11 +90,12 @@ export const patch = function <T>(url: string, data = {}, ext: any = {}) {
* @param {Object} [ext]
* @returns {AxiosInstance}
*/
export const get = function <T>(url: string, params = {}, ext?: any) {
export const get = function <T>(url: string, params = {}, ext?: any, _hideError = false) {
return request<any, AxiosResponseRewrite<T>>({
method: 'GET',
url,
params,
_hideError,
...ext
})
}
@ -200,7 +201,7 @@ const errorHandler = (error: any) => {
path: LoginPath
})
}, 0)
} else if (status === 404) {
} else if (status === 404 && !error.config._hideError) {
const data = error?.response?.data
const message = error?.response?.data?.message || `${data?.error} ${data?.path}`
showNotification(error?.code, message, '404')

View File

@ -570,10 +570,18 @@ const onCheckChange = () => {
};
const handleBatchDelete = () => {
if (!_selectedRowKeys.value.length) {
onlyMessage('至少选择一条数据', 'error');
return
}
handleDelete();
};
const handleBatchUpdate = () => {
if (!_selectedRowKeys.value.length) {
onlyMessage('至少选择一条数据', 'error');
return
}
const dataSet = new Set(_selectedRowKeys.value);
const dataMap = new Map();
tableRef?.value?._dataSource.forEach((i: any) => {

View File

@ -19,9 +19,9 @@ const systemNotice = [
},
{
provider: 'alarm-org',
name: '部门告警',
name: '组织告警',
description:
'当部门类型的告警被触发时,你将在已订阅的方式中收到通知',
'当组织类型的告警被触发时,你将在已订阅的方式中收到通知',
},
{
provider: 'alarm-other',

View File

@ -170,7 +170,7 @@ const columns = [
dataIndex: 'completeTime',
},
{
title: '设备版本',
title: '固件版本',
key: 'version',
dataIndex: 'version',
width: 100,

View File

@ -68,6 +68,11 @@ const events: any = ref(undefined);
watch(
() => current.value,
(value) => {
tabList.value = [{
key: 'property',
tab: '属性',
type: 'property',
}]
const metadata = JSON.parse(value?.metadata || '{}');
properties.value = metadata.properties;
events.value = metadata.events;

View File

@ -134,9 +134,11 @@ import { useMenuStore } from '@/store/menu';
import { useRouterParams } from '@/utils/hooks/useParams';
import { EventEmitter } from '@/utils/utils';
import { usePermissionStore } from '@/store/permission';
import { isNoCommunity } from '@/utils/utils';
import { useSystem } from '@/store/system';
const menuStory = useMenuStore();
const { showThreshold } = useSystem();
const route = useRoute();
const routerParams = useRouterParams();
const instanceStore = useInstanceStore();
@ -212,19 +214,24 @@ const getStatus = (id: string) => {
const getDetail = () => {
const keys = list.value.map((i) => i.key);
if (permissionStore.hasPermission('rule-engine/Alarm/Log:view')) {
if (permissionStore.hasPermission('rule-engine/Alarm/Log:view') && isNoCommunity && showThreshold) {
list.value.push({
key: 'AlarmRecord',
tab: '预处理数据',
});
}
if (permissionStore.hasPermission('iot-card/CardManagement:view')) {
if (permissionStore.hasPermission('iot-card/CardManagement:view') && isNoCommunity) {
list.value.push({
key: 'CardManagement',
tab: '物联网卡',
});
}
if (permissionStore.hasPermission('device/Firmware:view')) {
if (
permissionStore.hasPermission('device/Firmware:view') &&
instanceStore.current?.features?.find(
(item: any) => item?.id === 'supportFirmware',
) && isNoCommunity
) {
list.value.push({
key: 'Firmware',
tab: '远程升级',

View File

@ -1,6 +1,9 @@
<!-- 设备接入 -->
<template>
<div v-if="access.id === undefined || null" style='margin-top: 20%; transform: translateY(-50%);'>
<div
v-if="access.id === undefined || null"
style="margin-top: 20%; transform: translateY(-50%)"
>
<j-empty :image="simpleImage">
<template #description>
<span
@ -25,7 +28,7 @@
type="primary"
size="small"
:tooltip="{
title: tooltip
title: tooltip,
}"
:disabled="checkDisabled"
ghost
@ -74,66 +77,80 @@
<div v-else>{{ '暂无连接信息' }}</div>
</div>
<!-- 产品类型 -->
<j-form ref="pluginFormRef" :model="productData" layout="vertical" v-if='productTypes.length'>
<j-form-item name='id' label='产品类型' :rules='[{ required: true, message: "请选择产品类型"}]'>
<j-select
v-model:value='productData.id'
:options='productTypes'
@change='productTypeChange'
placeholder='请选择产品类型'
/>
</j-form-item>
<j-form
ref="pluginFormRef"
:model="productData"
layout="vertical"
v-if="productTypes.length"
>
<j-form-item
name="id"
label="产品类型"
:rules="[{ required: true, message: '请选择产品类型' }]"
>
<j-select
v-model:value="productData.id"
:options="productTypes"
@change="productTypeChange"
placeholder="请选择产品类型"
/>
</j-form-item>
</j-form>
<!-- 其它接入配置 -->
<Title
v-if="metadata?.name"
:data="metadata?.name"
class="config"
>
<template #extra>
<j-tooltip title="此配置来自于产品接入方式所选择的协议">
<AIcon
type="QuestionCircleOutlined"
style="margin-left: 2px"
/>
</j-tooltip>
</template>
</Title>
<j-form ref="formRef" :model="formData.data" layout="vertical">
<j-form-item
:name="item.property"
v-for="item in metadata?.properties || []"
:key="item"
:label="item.name"
:rules="[
{
required: !!item?.type?.expands?.required,
message: `${
item.type.type === 'enum' || 'boolean'
? '请选择'
: '请输入'
}${item.name}`,
},
]"
<div v-for="(i,index) in metadata">
<Title v-if="i?.name" :data="i?.name" class="config">
<template #extra>
<j-tooltip
title="此配置来自于产品接入方式所选择的协议"
>
<AIcon
type="QuestionCircleOutlined"
style="margin-left: 2px"
/>
</j-tooltip>
</template>
</Title>
<j-form
ref="formRef"
:model="formData.data"
layout="vertical"
>
<j-input
placeholder="请输入"
v-if="item.type.type === 'string'"
v-model:value="formData.data[item.property]"
></j-input>
<j-input-password
placeholder="请输入"
v-if="item.type.type === 'password'"
v-model:value="formData.data[item.property]"
></j-input-password>
<j-select
placeholder="请选择"
v-if="item.type.type === 'enum' || item.type.type === 'boolean'"
v-model:value="formData.data[item.property]"
:options="getOptions(item)"
<j-form-item
:name="item.property"
v-for="item in i?.properties || []"
:key="item"
:label="item.name"
:rules="[
{
required: !!item?.type?.expands?.required,
message: `${
item.type.type === 'enum' || 'boolean'
? '请选择'
: '请输入'
}${item.name}`,
},
]"
>
<!-- <j-select-option
<j-input
placeholder="请输入"
v-if="item.type.type === 'string'"
v-model:value="formData.data[item.property]"
></j-input>
<j-input-password
placeholder="请输入"
v-if="item.type.type === 'password'"
v-model:value="formData.data[item.property]"
></j-input-password>
<j-select
placeholder="请选择"
v-if="
item.type.type === 'enum' ||
item.type.type === 'boolean'
"
v-model:value="formData.data[item.property]"
:options="getOptions(item)"
>
<!-- <j-select-option
v-for="el in item?.type?.type === 'enum' &&
item?.type?.elements
? item?.type?.elements
@ -143,10 +160,19 @@
>
{{ el.text }}
</j-select-option> -->
</j-select>
<j-input-number v-if="['int','float','double','long'].includes(item.type.type)" v-model:value="formData.data[item.property]" placeholder="请输入"></j-input-number>
</j-form-item>
</j-form>
</j-select>
<j-input-number
v-if="
['int', 'float', 'double', 'long'].includes(
item.type.type,
)
"
v-model:value="formData.data[item.property]"
placeholder="请输入"
></j-input-number>
</j-form-item>
</j-form>
</div>
<Title data="存储策略">
<template #extra>
<j-tooltip
@ -175,7 +201,7 @@
type="primary"
@click="submitDevice"
hasPermission="device/Instance:update"
:loading='submitLoading'
:loading="submitLoading"
>保存</PermissionButton
>
</j-col>
@ -254,26 +280,30 @@
</div>
<!-- 选择设备 -->
<AccessModal
v-if='visible'
:product-id='productStore.current.id'
:deviceType='productStore.current.deviceType'
:accessId='accessId'
:providersList='dataSource'
@cancel=' visible = false'
@submit='checkAccess'
v-if="visible"
:product-id="productStore.current.id"
:deviceType="productStore.current.deviceType"
:accessId="accessId"
:providersList="dataSource"
@cancel="visible = false"
@submit="checkAccess"
/>
<!-- 物模型处理方式 -->
<MetaDataModal
v-if="metadataVisible"
:metadata="productData.metadata"
:access="access"
:data="metadataModalCacheData"
@cancel="
() => {
(metadataVisible = false), (metadataModalCacheData = {});
}
"
@submit="MetaDataModalSubmit"
/>
<!-- 物模型处理方式 -->
<MetaDataModal
v-if='metadataVisible'
:metadata='productData.metadata'
:access='access'
:data='metadataModalCacheData'
@cancel=' () => { metadataVisible = false, metadataModalCacheData = {}}'
@submit='MetaDataModalSubmit'
/>
</template>
<script lang="ts" setup name='AccessConfig'>
<script lang="ts" setup name="AccessConfig">
import { useProductStore } from '@/store/product';
import { ConfigMetadata } from '@/views/device/Product/typings';
import { Empty } from 'jetlinks-ui-components';
@ -282,20 +312,21 @@ import { usePermissionStore } from '@/store/permission';
import { steps, steps1 } from './util';
import './index.less';
import {
getProviders,
_deploy,
_undeploy,
queryList,
getConfigView,
getConfigMetadata,
productGuide,
productGuideSave,
getStoragList,
saveDevice,
updateDevice,
detail,
modify, getAccessConfig
} from '@/api/device/product'
getProviders,
_deploy,
_undeploy,
queryList,
getConfigView,
getConfigMetadata,
productGuide,
productGuideSave,
getStoragList,
saveDevice,
updateDevice,
detail,
modify,
getAccessConfig,
} from '@/api/device/product';
import Driver from 'driver.js';
import 'driver.js/dist/driver.min.css';
@ -304,16 +335,20 @@ import type { TableColumnType } from 'ant-design-vue';
import { useMenuStore } from '@/store/menu';
import _ from 'lodash-es';
import { accessConfigTypeFilter } from '@/utils/setting';
import AccessModal from './accessModal.vue'
import MetaDataModal from './metadataModal.vue'
import { getPluginData, getProductByPluginId, savePluginData } from '@/api/link/plugin'
import { detail as queryPluginAccessDetail } from '@/api/link/accessConfig'
import AccessModal from './accessModal.vue';
import MetaDataModal from './metadataModal.vue';
import {
getPluginData,
getProductByPluginId,
savePluginData,
} from '@/api/link/plugin';
import { detail as queryPluginAccessDetail } from '@/api/link/accessConfig';
import { onlyMessage } from '@/utils/comm';
import { pick } from 'lodash-es';
const productStore = useProductStore();
const tableRef = ref();
const formRef = ref();
const formRef = ref([]);
const menuStore = useMenuStore();
const permissionStore = usePermissionStore();
const render = new marked.Renderer();
@ -326,9 +361,9 @@ marked.setOptions({
const simpleImage = ref(Empty.PRESENTED_IMAGE_SIMPLE);
const visible = ref<boolean>(false);
const access = ref<Record<string, any>>({});
const accessId = ref<string>(productStore.current.accessId)
const accessId = ref<string>(productStore.current.accessId);
const config = ref<any>({});
const metadata = ref<ConfigMetadata>({ properties: [] });
const metadata = ref<ConfigMetadata[]>([{ properties: [] }]);
const dataSource = ref<string[]>([]);
const storageList = ref<any[]>([]);
const markdownToHtml = shallowRef('');
@ -350,7 +385,7 @@ const formData = reactive<Record<string, any>>({
data: productStore.current?.configuration || {},
});
//
const getOptions = (i:any) =>{
const getOptions = (i: any) => {
if (i.type.type === 'enum') {
return (i.type?.elements || []).map((item) => {
return {
@ -367,26 +402,26 @@ const getOptions = (i:any) =>{
{
label: i.type?.trueText,
value: i.type?.trueValue,
}
},
];
}
return undefined;
}
const fun = () =>{
console.log(formData.data,productStore.current?.configuration)
}
fun()
};
const fun = () => {
console.log(formData.data, productStore.current?.configuration);
};
fun();
//
const productTypes = ref([])
const productTypes = ref([]);
const productData = reactive({
id: undefined,
metadata: {} //
})
const pluginFormRef = ref()
const metadataVisible = ref(false)
const metadataModalCacheData = ref()
id: undefined,
metadata: {}, //
});
const pluginFormRef = ref();
const metadataVisible = ref(false);
const metadataModalCacheData = ref();
const submitLoading = ref(false)
const submitLoading = ref(false);
/**
* 显示弹窗
*/
@ -554,44 +589,48 @@ const queryAccessDetail = async (id: string) => {
}).then((res: any) => {
if (res.status === 200) {
access.value = res.result.data[0];
if (access.value?.transportDetail?.metadata) {
const metadata = JSON.parse(access.value?.transportDetail?.metadata)
productData.metadata = pick(metadata,['functions','properties','events','tags'])
}
if (access.value?.transportDetail?.metadata) {
const metadata = JSON.parse(
access.value?.transportDetail?.metadata,
);
productData.metadata = pick(metadata, [
'functions',
'properties',
'events',
'tags',
]);
}
}
});
};
const handleColumns = () => {
const Group = {
title: '分组',
dataIndex: 'group',
key: 'group',
ellipsis: true,
align: 'center',
width: 100,
customCell: (record: any, rowIndex: number) => {
const obj = {
children: record,
rowSpan: 0,
};
const list = config.value?.routes || [];
const Group = {
title: '分组',
dataIndex: 'group',
key: 'group',
ellipsis: true,
align: 'center',
width: 100,
customCell: (record: any, rowIndex: number) => {
const obj = {
children: record,
rowSpan: 0,
};
const list = config.value?.routes || [];
const arr = list.filter(
(res: any) => res.group === record.group,
);
const arr = list.filter((res: any) => res.group === record.group);
const isRowIndex =
rowIndex === 0 ||
list[rowIndex - 1].group !== record.group;
isRowIndex && (obj.rowSpan = arr.length);
const isRowIndex =
rowIndex === 0 || list[rowIndex - 1].group !== record.group;
isRowIndex && (obj.rowSpan = arr.length);
return obj;
},
};
columnsMQTT.value = [Group, ...ColumnsMQTT];
columnsHTTP.value = [Group, ...ColumnsHTTP];
}
return obj;
},
};
columnsMQTT.value = [Group, ...ColumnsMQTT];
columnsHTTP.value = [Group, ...ColumnsHTTP];
};
/**
* 查询协议信息
@ -603,7 +642,7 @@ const getConfigDetail = (
getConfigView(messageProtocol, transportProtocol).then((resp) => {
if (resp.status === 200) {
config.value = resp.result;
handleColumns()
handleColumns();
if (config.value?.document) {
markdownToHtml.value = marked(config.value.document);
}
@ -640,56 +679,73 @@ const getGuide = async (isDriver1: boolean = false) => {
};
const checkAccess = async (data: any) => {
console.log(data)
visible.value = false
accessId.value = data.access.id
access.value = data.access
config.value = data.access?.transportDetail || {}
productTypes.value = []
productData.id = undefined
productData.metadata = {}
metadata.value = data.metadata?.[0] || {
properties: []
}
if (metadata.value?.properties) {
metadata.value?.properties.forEach((item) => {
if (
item.name === '流传输模式' &&
(!productStore.current?.configuration ||
!productStore.current?.configuration.hasOwnProperty(
item.property,
))
) {
formData.data[item.property] =
item.type.expands?.defaultValue;
}
});
}
if (data.access.channel === 'plugin') { //
markdownToHtml.value = ''
productTypes.value = data.productTypes.map(item => ({ ...item, label: item.name, value: item.id}))
} else {
handleColumns()
markdownToHtml.value = config.value?.document ? marked(config.value.document) : '';
getGuide(!!data.metadata.length); //
if (data.access?.transportDetail?.metadata) {
const metadata = JSON.parse(data.access?.transportDetail?.metadata)
productData.metadata = pick(metadata,['functions','properties','events','tags'])
visible.value = false;
accessId.value = data.access.id;
access.value = data.access;
config.value = data.access?.transportDetail || {};
productTypes.value = [];
productData.id = undefined;
productData.metadata = {};
metadata.value = data.metadata || [
{
properties: [],
},
];
if (metadata.value.length) {
metadata.value.forEach((i: any) => {
i?.properties.forEach((item: any) => {
if (
item.name === '流传输模式' &&
(!productStore.current?.configuration ||
!productStore.current?.configuration.hasOwnProperty(
item.property,
))
) {
formData.data[item.property] =
item.type.expands?.defaultValue;
}
});
});
}
}
}
if (data.access.channel === 'plugin') {
//
markdownToHtml.value = '';
productTypes.value = data.productTypes.map((item) => ({
...item,
label: item.name,
value: item.id,
}));
} else {
handleColumns();
markdownToHtml.value = config.value?.document
? marked(config.value.document)
: '';
getGuide(!!data.metadata.length); //
if (data.access?.transportDetail?.metadata) {
const metadata = JSON.parse(data.access?.transportDetail?.metadata);
productData.metadata = pick(metadata, [
'functions',
'properties',
'events',
'tags',
]);
}
}
};
const productTypeChange = (id: string, items: any) => {
productData.metadata = items?.metadata ? pick(items.metadata,['functions','properties','events','tags']) : {}
}
productData.metadata = items?.metadata
? pick(items.metadata, ['functions', 'properties', 'events', 'tags'])
: {};
};
/**
* 获取协议类型名称
*/
const getProvidersList = async () => {
const res: any = await getProviders();
dataSource.value = res.result;
const res: any = await getProviders();
dataSource.value = res.result;
};
/**
@ -699,22 +755,26 @@ const getData = async (accessId?: string) => {
const _accessId = accessId || productStore.current?.accessId;
if (productStore.current?.id) {
getConfigMetadata(productStore.current?.id).then((resp: any) => {
metadata.value = (resp?.result[0] as ConfigMetadata) || {
properties: [],
};
metadata.value = resp?.result || [
{
properties: [],
},
];
// udp
if (metadata.value?.properties) {
metadata.value?.properties.forEach((item) => {
if (
item.name === '流传输模式' &&
(!productStore.current?.configuration ||
!productStore.current?.configuration.hasOwnProperty(
item.property,
))
) {
formData.data[item.property] =
item.type.expands?.defaultValue;
}
if (metadata.value.length) {
metadata.value.forEach((i: any) => {
i?.properties.forEach((item: any) => {
if (
item.name === '流传输模式' &&
(!productStore.current?.configuration ||
!productStore.current?.configuration.hasOwnProperty(
item.property,
))
) {
formData.data[item.property] =
item.type.expands?.defaultValue;
}
});
});
}
if (accessId) {
@ -729,31 +789,45 @@ const getData = async (accessId?: string) => {
// if (metadataResp.success) {
// metadata.value = (metadataResp.result?.[0] as ConfigMetadata[]) || [];
// }
queryAccessDetail(_accessId);
if (productStore.current?.accessProvider === 'plugin_gateway') {
queryPluginAccessDetail(_accessId).then(async res => { //
if (res.success) {
const pluginRes = await getPluginData('product', _accessId, productStore.current?.id)
const resp = await getProductByPluginId(res.result.channelId).catch(() => ({ success: false, result: []}))
if (resp.success) {
productTypes.value = resp.result.map(item => {
if (pluginRes?.result?.externalId === item.id) {
productData.id = pluginRes?.result?.externalId
productData.metadata = pick(item.metadata,['functions','properties','events','tags'])
queryAccessDetail(_accessId);
if (productStore.current?.accessProvider === 'plugin_gateway') {
queryPluginAccessDetail(_accessId).then(async (res) => {
//
if (res.success) {
const pluginRes = await getPluginData(
'product',
_accessId,
productStore.current?.id,
);
const resp = await getProductByPluginId(
res.result.channelId,
).catch(() => ({ success: false, result: [] }));
if (resp.success) {
productTypes.value = resp.result.map((item) => {
if (pluginRes?.result?.externalId === item.id) {
productData.id = pluginRes?.result?.externalId;
productData.metadata = pick(item.metadata, [
'functions',
'properties',
'events',
'tags',
]);
}
return {
...item,
label: item.name,
value: item.id,
};
});
}
}
return { ...item, label: item.name, value: item.id }
})
}
}
})
} else {
getConfigDetail(
productStore.current?.messageProtocol || '',
productStore.current?.transportProtocol || '',
);
}
});
} else {
getConfigDetail(
productStore.current?.messageProtocol || '',
productStore.current?.transportProtocol || '',
);
}
}
getStoragList().then((resp: any) => {
if (resp.status === 200) {
@ -766,100 +840,106 @@ const getData = async (accessId?: string) => {
* 保存设备接入
*/
const submitDevice = async () => {
if (pluginFormRef.value) { //
const pluginRef = await pluginFormRef.value.validate();
if (!pluginRef) return
if (pluginFormRef.value) {
//
const pluginRef = await pluginFormRef.value.validate();
if (!pluginRef) return;
}
const res = await formRef.value.validate();
if (!res) return
const allValidate = formRef.value.map((item:any)=>{
return item?.validate()
})
const res = await Promise.all(allValidate)
// const res = await formRef.value.validate();
if (!res) return;
const values = { storePolicy: form.storePolicy, ...formData.data };
const id = productStore.current?.id;
//
const _metadata = JSON.parse(productStore.current?.metadata || '{}')
const _metadata = JSON.parse(productStore.current?.metadata || '{}');
if (
(_metadata.properties?.length ||
_metadata.events?.length ||
_metadata.functions?.length ||
_metadata.tags?.length
) &&
(
productData.metadata?.properties?.length ||
productData.metadata?.events?.length ||
productData.metadata?.functions?.length ||
productData.metadata?.tags?.length
)
(_metadata.properties?.length ||
_metadata.events?.length ||
_metadata.functions?.length ||
_metadata.tags?.length) &&
(productData.metadata?.properties?.length ||
productData.metadata?.events?.length ||
productData.metadata?.functions?.length ||
productData.metadata?.tags?.length)
) {
metadataModalCacheData.value = {
id,
values,
productTypeId: productData.id
}
metadataVisible.value = true
metadataModalCacheData.value = {
id,
values,
productTypeId: productData.id,
};
metadataVisible.value = true;
} else {
updateAccessData(id, values)
updateAccessData(id, values);
}
};
const updateAccessData = async (id: string, values: any) => {
const result: any = {};
// flatObj(values, result);
// const { storePolicy, ...extra } = result;
const { storePolicy, ...extra } = values
// undefinednull
let _metadata = ''
if (productStore.current?.metadata) {
_metadata = productStore.current?.metadata
} else if (productData.metadata && Object.keys(productData.metadata).length) {
_metadata = JSON.stringify(productData.metadata)
}
// ()
const accessObj = {
...productStore.current,
metadata: _metadata,
transportProtocol: access.value?.transport,
protocolName: access.value?.protocolDetail?.name,
accessId: access.value?.id,
accessName: access.value?.name,
accessProvider: access.value?.provider,
messageProtocol: access.value?.protocol,
}
submitLoading.value = true
const updateDeviceResp = await updateDevice(accessObj).catch(() => { success: false})
if (!updateDeviceResp.success) {
submitLoading.value = false
}
if (access.value?.provider === "plugin_gateway" && productData.id) {
await savePluginData(
'product',
access.value?.id,
productStore.current.id,
productData.id
).catch(() => ({}))
}
//
const resp = await modify(id || '', {
id: id,
configuration: { ...extra },
storePolicy: storePolicy,
}).finally(()=>{
submitLoading.value = false
})
if (resp.status === 200) {
onlyMessage('操作成功!');
productStore.current!.storePolicy = storePolicy;
if ((window as any).onTabSaveSuccess) {
if (resp.result) {
(window as any).onTabSaveSuccess(resp);
setTimeout(() => window.close(), 300);
}
} else {
getDetailInfo();
const result: any = {};
// flatObj(values, result);
// const { storePolicy, ...extra } = result;
const { storePolicy, ...extra } = values;
// undefinednull
let _metadata = '';
if (productStore.current?.metadata) {
_metadata = productStore.current?.metadata;
} else if (
productData.metadata &&
Object.keys(productData.metadata).length
) {
_metadata = JSON.stringify(productData.metadata);
}
}
}
// ()
const accessObj = {
...productStore.current,
metadata: _metadata,
transportProtocol: access.value?.transport,
protocolName: access.value?.protocolDetail?.name,
accessId: access.value?.id,
accessName: access.value?.name,
accessProvider: access.value?.provider,
messageProtocol: access.value?.protocol,
};
submitLoading.value = true;
const updateDeviceResp = await updateDevice(accessObj).catch(() => {
success: false;
});
if (!updateDeviceResp.success) {
submitLoading.value = false;
}
if (access.value?.provider === 'plugin_gateway' && productData.id) {
await savePluginData(
'product',
access.value?.id,
productStore.current.id,
productData.id,
).catch(() => ({}));
}
//
const resp = await modify(id || '', {
id: id,
configuration: { ...extra },
storePolicy: storePolicy,
}).finally(() => {
submitLoading.value = false;
});
if (resp.status === 200) {
onlyMessage('操作成功!');
productStore.current!.storePolicy = storePolicy;
if ((window as any).onTabSaveSuccess) {
if (resp.result) {
(window as any).onTabSaveSuccess(resp);
setTimeout(() => window.close(), 300);
}
} else {
getDetailInfo();
}
}
};
const flatObj = (obj: any, result: any) => {
Object.keys(obj).forEach((key: string) => {
@ -872,16 +952,16 @@ const flatObj = (obj: any, result: any) => {
};
const getDetailInfo = async () => {
await productStore.getDetail(productStore.detail.id)
MetaDataModalSubmit()
await productStore.getDetail(productStore.detail.id);
MetaDataModalSubmit();
};
const MetaDataModalSubmit = () => {
//
productStore.tabActiveKey = 'Metadata'
}
//
productStore.tabActiveKey = 'Metadata';
};
getProvidersList()
getProvidersList();
/**
* 初始化
*/
@ -892,21 +972,21 @@ watchEffect(() => {
});
const tooltip = computed(() => {
if (productStore.current?.count > 0) {
return '产品下有设备实例时不能更换接入方式'
}
if (productStore.current.state === 1) {
return '停用产品后才可更换接入方式'
}
return ''
})
if (productStore.current?.count > 0) {
return '产品下有设备实例时不能更换接入方式';
}
if (productStore.current.state === 1) {
return '停用产品后才可更换接入方式';
}
return '';
});
const checkDisabled = computed(() => {
if (productStore.current?.count > 0 || productStore.current.state === 1) {
return true
}
return false
})
if (productStore.current?.count > 0 || productStore.current.state === 1) {
return true;
}
return false;
});
nextTick(() => {
getData();

View File

@ -160,7 +160,10 @@ import { useMenuStore } from '@/store/menu';
import { useRouterParams } from '@/utils/hooks/useParams';
import { EventEmitter } from '@/utils/utils';
import { usePermissionStore } from '@/store/permission';
import { isNoCommunity } from '@/utils/utils';
import { useSystem } from '@/store/system';
const { showThreshold } = useSystem()
const permissionStore = usePermissionStore();
const menuStory = useMenuStore();
const route = useRoute();
@ -274,45 +277,29 @@ const getProtocol = async () => {
productStore.current?.messageProtocol,
);
if (res.status === 200) {
const paring = res.result?.transports[0]?.features?.find(
const transport = res.result?.transports?.find((item: any) => {
return item.id === productStore.current?.transportProtocol;
});
const paring = transport?.features?.find(
(item: any) => item.id === 'transparentCodec',
);
const supportFirmware = transport?.features?.find(
(item: any) => item.id === 'supportFirmware',
);
if (paring) {
list.value = [
{
key: 'Info',
tab: '配置信息',
},
{
key: 'Metadata',
tab: '物模型',
class: 'objectModel',
},
{
key: 'Device',
tab: '设备接入',
},
{
key: 'DataAnalysis',
tab: '数据解析',
},
];
} else {
list.value = [
{
key: 'Info',
tab: '配置信息',
},
{
key: 'Metadata',
tab: '物模型',
class: 'objectModel',
},
{
key: 'Device',
tab: '设备接入',
},
];
list.value.push({
key: 'DataAnalysis',
tab: '数据解析',
});
}
if (
supportFirmware &&
permissionStore.hasPermission('device/Firmware:view') && isNoCommunity
) {
list.value.push({
key: 'Firmware',
tab: '远程升级',
});
}
}
//
@ -329,19 +316,13 @@ const getProtocol = async () => {
if (
permissionStore.hasPermission(
'rule-engine/Alarm/Configuration:view',
)
) && isNoCommunity && showThreshold
) {
list.value.push({
key: 'AlarmRecord',
tab: '预处理数据',
});
}
if (permissionStore.hasPermission('device/Firmware:view')) {
list.value.push({
key: 'Firmware',
tab: '远程升级',
});
}
}
};
/**

View File

@ -653,6 +653,11 @@ watch(() => metadata.value, () => {
group: undefined
}
}
if(props.type === 'functions' && !item.output){
item['output'] = {
}
}
return item
})
initOptions(dataSource.value)

View File

@ -268,6 +268,7 @@ import { omit, cloneDeep } from 'lodash-es';
import { PopoverModal } from '@/components/Metadata/Table';
import { useTableWrapper } from '@/components/Metadata/Table/context';
import { useThreshold } from './hooks';
import { useSystem } from '@/store/system';
const props = defineProps({
value: {
@ -309,6 +310,7 @@ const props = defineProps({
const type = inject('_metadataType');
const { showThreshold } = useSystem()
const productStore = useProductStore();
const deviceStore = useInstanceStore();
const tableWrapperRef = useTableWrapper();
@ -382,7 +384,7 @@ const showMetrics = computed(() => {
const showExtra = computed(() => {
return (
['int', 'long', 'float', 'double'].includes(props.type as any) &&
props.metadataType === 'properties'
props.metadataType === 'properties' && showThreshold
);
});

View File

@ -90,7 +90,7 @@ export const useThreshold = (props: Record<string, any>) => {
}
const thresholdDetailQuery = () => {
if (props.target === 'product') {
queryProduct(productStore.current.id, props.id)
queryProduct(productStore.current.id, props.id,false)
} else {
queryDevice(deviceStore.current.productId, deviceStore.current.id, props.id)
}

File diff suppressed because it is too large Load Diff

View File

@ -204,11 +204,7 @@ const rules = {
};
const filterOption = (input: string, option: any) => {
return (
option.componentOptions.children[0].text
.toLowerCase()
.indexOf(input.toLowerCase()) >= 0
);
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
const platformConfigList = computed(() => {

View File

@ -45,7 +45,7 @@
<j-col :span="8">
<div class="data-statistics-item">
<div class="flow-info" style="width: 100%">
<div class="label">本年流量消耗1</div>
<div class="label">本年流量消耗</div>
<j-tooltip placement="bottomLeft">
<template #title>
<span>{{ yearTotal }} M</span>

View File

@ -9,7 +9,7 @@
/>
<div class="right">
<pro-search
:columns="columns"
:columns="newColumns"
target="channel"
@search="handleSearch"
/>
@ -17,7 +17,7 @@
<JProTable
ref="listRef"
model="table"
:columns="columns"
:columns="newColumns"
:request="(e:any) => ChannelApi.list(e, route?.query.id as string)"
:defaultParams="{
sorts: [{ name: 'modifyTime', order: 'desc' }],
@ -238,6 +238,14 @@ const columns = [
},
];
const newColumns = computed(()=>{
if(route.query.type=== 'fixed-media'){
return columns.filter(item=>item.key!=='manufacturer')
}else{
return columns
}
});
const params = ref<Record<string, any>>({});
const deviceData = ref<any>();

View File

@ -32,6 +32,7 @@
flex-direction: column;
flex-grow: 1;
padding-left: @padding;
height: calc(100vh - 190px);
.top {
display: flex;

View File

@ -151,8 +151,10 @@ const getTemplateList = async () => {
};
const { result } = await ConfigApi.getTemplate(params, props.data.id);
templateList.value = result;
formData.value.templateId = result[0]?.id as string;
getTemplateDetail();
if (result.length) {
formData.value.templateId = result[0]?.id as string;
getTemplateDetail();
}
};
watch(
@ -173,7 +175,7 @@ const getTemplateDetail = async () => {
(m: any) => ({
...m,
type: m.expands ? m.expands.businessType : m.type,
value: undefined,
value: undefined,
//
otherRules:
m.id === 'calledNumber' || m.id === 'phoneNumber'

View File

@ -207,7 +207,7 @@ const onTreeSelect = (keys: any) => {
const columns = [
{
title: '钉钉用户名',
title: props.data.type === 'weixin' ? '企业微信用户名' : '钉钉用户名',
dataIndex: 'thirdPartyUserName',
key: 'thirdPartyUserName',
},

View File

@ -3,7 +3,7 @@
<page-container>
<pro-search
:columns="columns"
target="notice-config"
target="notice-template"
@search="handleSearch"
/>
<FullPage>

View File

@ -34,7 +34,7 @@
{{ value.name }}
</span>
</Ellipsis>
<div v-if="showBindTags && activeBranches.length">
<div v-if="showBindTags && activeBranches.length && value.triggerType === 'device'">
<div style="margin-top: 16px; margin-bottom: 8px" class="card-item-content-text">
关联条件
</div>

View File

@ -1,285 +1,291 @@
<template>
<j-form
ref='timerForm'
:model='formModel'
:colon='false'
layout='vertical'
>
<j-form-item name='trigger'>
<j-radio-group
v-model:value='formModel.trigger'
:options='triggerOptions'
option-type='button'
button-style='solid'
@change='triggerChange'
/>
</j-form-item>
<j-form-item v-if='showCron' name='cron' :rules="cronRules">
<j-input placeholder='corn表达式' v-model:value='formModel.cron' @change='updateValue' />
</j-form-item>
<j-form-item v-else-if="showMulti" name="multi" :rules="multiRules">
<Calendar v-model:value="formModel.multi" @change='updateValue'/>
</j-form-item>
<template v-else>
<j-form-item name='when'>
<WhenOption v-model:value='formModel.when' :type='formModel.trigger' @change='updateValue' />
</j-form-item>
<j-form-item name='mod'>
<j-radio-group
v-model:value='formModel.mod'
:options='[
{ label: "周期执行", value: "period" },
{ label: "执行一次", value: "once" },
]'
option-type='button'
button-style='solid'
@change='updateValue'
/>
</j-form-item>
</template>
<j-space v-if='showOnce && !showMulti' style='display: flex;gap: 24px'>
<j-form-item :name="['once', 'time']">
<j-time-picker
valueFormat='HH:mm:ss'
v-model:value='formModel.once.time'
style='width: 100%'
format='HH:mm:ss'
@change='updateValue'
/>
</j-form-item>
<j-form-item> 执行一次</j-form-item>
</j-space>
<j-space v-if='showPeriod && !showMulti' style='display: flex;gap: 24px'>
<j-form-item>
<j-time-range-picker
valueFormat='HH:mm:ss'
:value='[
formModel.period.from,
formModel.period.to,
]'
@change='(v) => {
formModel.period.from = v[0]
formModel.period.to = v[1]
updateValue()
}'
/>
</j-form-item>
<j-form-item></j-form-item>
<j-form-item
:name='["period", "every"]'
:rules='[{ required: true, message: "请输入时间" }]'
>
<j-input-number
placeholder='请输入时间'
style='max-width: 170px'
:precision='0'
:min='1'
:max='unitMax'
v-model:value='formModel.period.every'
@change='updateValue'
>
<template #addonAfter>
<j-select
v-model:value='formModel.period.unit'
:options='[
{ label: "秒", value: "seconds" },
{ label: "分", value: "minutes" },
{ label: "小时", value: "hours" },
]'
@select='periodUnitChange'
<j-form ref="timerForm" :model="formModel" :colon="false" layout="vertical">
<j-form-item name="trigger">
<j-radio-group
v-model:value="formModel.trigger"
:options="triggerOptions"
option-type="button"
button-style="solid"
@change="triggerChange"
/>
</template>
</j-input-number>
</j-form-item>
<j-form-item>执行一次</j-form-item>
</j-space>
</j-form>
</j-form-item>
<j-form-item v-if="showCron" name="cron" :rules="cronRules">
<j-input
placeholder="corn表达式"
v-model:value="formModel.cron"
@change="updateValue"
/>
</j-form-item>
<j-form-item v-else-if="showMulti" name="multi" :rules="multiRules">
<Calendar v-model:value="formModel.multi" @change="updateValue" />
</j-form-item>
<template v-else>
<j-form-item name="when">
<WhenOption
v-model:value="formModel.when"
:type="formModel.trigger"
@change="updateValue"
/>
</j-form-item>
<j-form-item name="mod">
<j-radio-group
v-model:value="formModel.mod"
:options="[
{ label: '周期执行', value: 'period' },
{ label: '执行一次', value: 'once' },
]"
option-type="button"
button-style="solid"
@change="updateValue"
/>
</j-form-item>
</template>
<j-space v-if="showOnce && !showMulti" style="display: flex; gap: 24px">
<j-form-item :name="['once', 'time']">
<j-time-picker
valueFormat="HH:mm:ss"
v-model:value="formModel.once.time"
style="width: 100%"
format="HH:mm:ss"
@change="updateValue"
/>
</j-form-item>
<j-form-item> 执行一次</j-form-item>
</j-space>
<j-space
v-if="showPeriod && !showMulti"
style="display: flex; gap: 24px"
>
<j-form-item>
<j-time-range-picker
valueFormat="HH:mm:ss"
:value="[formModel.period.from, formModel.period.to]"
@change="
(v) => {
formModel.period.from = v[0];
formModel.period.to = v[1];
updateValue();
}
"
/>
</j-form-item>
<j-form-item></j-form-item>
<j-form-item
:name="['period', 'every']"
:rules="[{ required: true, message: '请输入时间' }]"
>
<j-input-number
placeholder="请输入时间"
style="max-width: 170px"
:precision="0"
:min="1"
:max="unitMax"
v-model:value="formModel.period.every"
@change="updateValue"
>
<template #addonAfter>
<j-select
v-model:value="formModel.period.unit"
:options="[
{ label: '秒', value: 'seconds' },
{ label: '分', value: 'minutes' },
{ label: '小时', value: 'hours' },
]"
@select="periodUnitChange"
/>
</template>
</j-input-number>
</j-form-item>
<j-form-item>执行一次</j-form-item>
</j-space>
</j-form>
</template>
<script setup lang='ts' name='Timer'>
import type { PropType } from 'vue'
import dayjs from 'dayjs'
import WhenOption from './WhenOption.vue'
import {cloneDeep, pick} from 'lodash-es'
import type { OperationTimer } from '../../../typings'
import { defineExpose } from 'vue'
import Calendar from './Calendar.vue'
import cronstrue from 'cronstrue'
<script setup lang="ts" name="Timer">
import type { PropType } from 'vue';
import dayjs from 'dayjs';
import WhenOption from './WhenOption.vue';
import { cloneDeep, pick } from 'lodash-es';
import type { OperationTimer } from '../../../typings';
import { defineExpose } from 'vue';
import Calendar from './Calendar.vue';
import cronstrue from 'cronstrue';
import { isNoCommunity } from '@/utils/utils';
type NameType = string[] | string
type NameType = string[] | string;
type Emit = {
(e: 'update:value', data: Partial<OperationTimer>): void
}
(e: 'update:value', data: Partial<OperationTimer>): void;
};
const props = defineProps({
name: {
type: [String, Array] as PropType<NameType>,
default: ''
},
value: {
type: Object,
default: () => ({})
},
type: {
type: String,
default: undefined
}
})
name: {
type: [String, Array] as PropType<NameType>,
default: '',
},
value: {
type: Object,
default: () => ({}),
},
type: {
type: String,
default: undefined,
},
});
const emit = defineEmits<Emit>()
const unitMax = ref<number>(99)
const emit = defineEmits<Emit>();
const unitMax = ref<number>(99);
const cronRules = [
{ max: 64, message: '最多可输入64个字符' },
{
validator: async (_: any, v: string) => {
if (v) {
try {
console.log(v, cronstrue.toString(v))
} catch (e) {
return Promise.reject(new Error('请输入正确的cron表达式'));
}
} else {
return Promise.reject(new Error('请输入cron表达式'));
}
return Promise.resolve();
}
}
]
{ max: 64, message: '最多可输入64个字符' },
{
validator: async (_: any, v: string) => {
if (v) {
try {
console.log(v, cronstrue.toString(v));
} catch (e) {
return Promise.reject(new Error('请输入正确的cron表达式'));
}
} else {
return Promise.reject(new Error('请输入cron表达式'));
}
return Promise.resolve();
},
},
];
const multiRules = [
{
validator: async (_: any, v: string) => {
if (!v.spec?.length) {
return Promise.reject('请添加自定义日历规则');
} else {
const index = v.spec.findIndex(item => !item.scheduleTags.length)
if (index > -1) {
return Promise.reject(`规则【${index + 1}】请选择日期类型`);
}
}
{
validator: async (_: any, v: string) => {
if (!v.spec?.length) {
return Promise.reject('请添加自定义日历规则');
} else {
const index = v.spec.findIndex(
(item) => !item.scheduleTags.length,
);
if (index > -1) {
return Promise.reject(`规则【${index + 1}】请选择日期类型`);
}
}
return Promise.resolve()
}
}
]
return Promise.resolve();
},
},
];
const triggerOptions = computed(() => {
let _options = [
{ label: "按周", value: "week" },
{ label: "按月", value: "month" },
{ label: "cron表达式", value: "cron" },
{ label: "自定义日历", value: "multi" }
]
let _options = isNoCommunity ? [
{ label: '按周', value: 'week' },
{ label: '按月', value: 'month' },
{ label: 'cron表达式', value: 'cron' },
{ label: '自定义日历', value: 'multi' },
] : [
{ label: '按周', value: 'week' },
{ label: '按月', value: 'month' },
{ label: 'cron表达式', value: 'cron' },
]
// if (props.type === 'timer') {
// _options = [..._options, {
// label: "", value: "multi"
// }]
// }
return _options
})
// if (props.type === 'timer') {
// _options = [..._options, {
// label: "", value: "multi"
// }]
// }
return _options;
});
const formModel = reactive<OperationTimer>({
trigger: 'week',
when: props.value.when || [],
mod: 'period',
cron: undefined,
once: {
time: dayjs(new Date()).format('HH:mm:ss')
},
period: {
from: dayjs(new Date()).startOf('day').format('HH:mm:ss'),
to: dayjs(new Date()).endOf('day').format('HH:mm:ss'),
every: 1,
unit: 'seconds'
},
multi: {
type: "and",
spec: []
}
})
const timerForm = ref()
trigger: 'week',
when: props.value.when || [],
mod: 'period',
cron: undefined,
once: {
time: dayjs(new Date()).format('HH:mm:ss'),
},
period: {
from: dayjs(new Date()).startOf('day').format('HH:mm:ss'),
to: dayjs(new Date()).endOf('day').format('HH:mm:ss'),
every: 1,
unit: 'seconds',
},
multi: {
type: 'and',
spec: [],
},
});
const timerForm = ref();
const showCron = computed(() => {
return formModel.trigger === 'cron'
})
return formModel.trigger === 'cron';
});
const showMulti = computed(() => {
return formModel.trigger === 'multi'
})
return formModel.trigger === 'multi';
});
const showOnce = computed(() => {
return formModel.trigger !== 'cron' && formModel.mod === 'once'
})
return formModel.trigger !== 'cron' && formModel.mod === 'once';
});
const showPeriod = computed(() => {
return formModel.trigger !== 'cron' && formModel.mod === 'period'
})
return formModel.trigger !== 'cron' && formModel.mod === 'period';
});
const updateValue = () => {
const cloneValue = cloneDeep(formModel)
let keys: string[] = ['trigger']
if (cloneValue.trigger === 'cron') {
keys.push('cron')
} else if (cloneValue.trigger === 'multi') {
keys.push('multi')
} else {
keys = keys.concat(['mod', 'when'])
if (cloneValue.mod === 'period') {
keys.push('period')
const cloneValue = cloneDeep(formModel);
let keys: string[] = ['trigger'];
if (cloneValue.trigger === 'cron') {
keys.push('cron');
} else if (cloneValue.trigger === 'multi') {
keys.push('multi');
} else {
keys.push('once')
keys = keys.concat(['mod', 'when']);
if (cloneValue.mod === 'period') {
keys.push('period');
} else {
keys.push('once');
}
}
}
emit('update:value', pick(cloneValue, keys))
}
emit('update:value', pick(cloneValue, keys));
};
const triggerChange = () => {
formModel.when = []
formModel.cron = undefined
updateValue()
}
formModel.when = [];
formModel.cron = undefined;
updateValue();
};
/**
* 频率单位切换
* @param v
*/
const periodUnitChange = (v: any) => {
if(v === 'hours') {
unitMax.value = 99999
} else {
unitMax.value = 99
}
formModel.period!.every = 1
updateValue()
}
if (v === 'hours') {
unitMax.value = 99999;
} else {
unitMax.value = 99;
}
formModel.period!.every = 1;
updateValue();
};
defineExpose({
validateFields: () => new Promise(async (resolve) => {
const data = await timerForm.value?.validateFields()
resolve(data)
})
})
validateFields: () =>
new Promise(async (resolve) => {
const data = await timerForm.value?.validateFields();
resolve(data);
}),
});
Object.assign(formModel, props.value)
formModel.when = props.value.when || []
Object.assign(formModel, props.value);
formModel.when = props.value.when || [];
watchEffect(() => {
if(props.value?.period?.unit === 'hours') {
unitMax.
value = 99999
} else {
unitMax.value = 99
}
})
if (props.value?.period?.unit === 'hours') {
unitMax.value = 99999;
} else {
unitMax.value = 99;
}
});
</script>
<style scoped lang='less'>
</style>
<style scoped lang="less"></style>

View File

@ -305,8 +305,8 @@ const queryParams = {
{
terms: [
{
value: '%show":true%',
termType: 'like',
value: '%show":false%',
termType: 'nlike',
column: 'options',
type: 'and',
},

View File

@ -458,7 +458,7 @@ const form = reactive<formType>({
headerTheme: configInfo.front?.headerTheme,
logo: configInfo.front?.logo || '/logo.png',
ico: configInfo.front?.ico || '/favicon.ico',
showRecordNumber: configInfo.front?.showRecordNumber,
showRecordNumber: configInfo.front?.showRecordNumber || false,
recordNumber: configInfo.front?.recordNumber,
background: configInfo.front?.background || '/images/login.png',
apiKey: configInfo.amap?.apiKey,

View File

@ -17,9 +17,6 @@
}"
:params="queryParams"
:rowSelection="{
// selectedRowKeys: table._selectedRowKeys.value,
// onChange:(keys:string[])=>table._selectedRowKeys.value = [...keys],
// onSelectNone: table.cancelSelect
selectedRowKeys: table._selectedRowKeys.value,
onSelect: table.onSelect,
onSelectAll: table.onSelectAll,
@ -307,6 +304,7 @@ const columns = [
dataIndex: 'permission',
key: 'permission',
ellipsis: true,
width: 300,
scopedSlots: true,
},
{

View File

@ -326,7 +326,7 @@ const form = reactive({
init: () => {
//
routeParams.id &&
routeParams.id ?
getMenuInfo_api(routeParams.id).then((resp: any) => {
form.data = {
...(resp.result as formType),
@ -338,7 +338,7 @@ const form = reactive({
};
form.sourceCode = resp.result.code;
showPermissionChoose.value = true
});
}) : showPermissionChoose.value = true
if (isNoCommunity) {
//
@ -350,8 +350,8 @@ const form = reactive({
{
terms: [
{
value: '%show":true%',
termType: 'like',
value: '%show":false%',
termType: 'nlike',
column: 'options',
},
],

View File

@ -10,30 +10,30 @@
<div class="content">
<j-card title="菜单配置" style="width: 80%">
<div class="tree">
<j-scrollbar>
<j-tree
v-if="treeData.length !== 0"
defaultExpandAll
multiple
draggable
:tree-data="treeData"
@select="onSelect"
:selectedKeys="selectedKeys"
@drop="onDrop"
@dragend="onDragend"
>
<template #title="row">
<div class="tree-content">
<div class="tree-content-title">
<AIcon type="HolderOutlined" />
<div style="margin-left: 8px">
{{ row.name }}
</div>
</div>
</div>
</template>
</j-tree>
</j-scrollbar>
<j-scrollbar>
<j-tree
v-if="treeData.length !== 0"
defaultExpandAll
multiple
draggable
:tree-data="treeData"
@select="onSelect"
:selectedKeys="selectedKeys"
@drop="onDrop"
@dragend="onDragend"
>
<template #title="row">
<div class="tree-content">
<div class="tree-content-title">
<AIcon type="HolderOutlined" />
<div style="margin-left: 8px">
{{ row.name }}
</div>
</div>
</div>
</template>
</j-tree>
</j-scrollbar>
</div>
</j-card>
</div>
@ -43,6 +43,17 @@
style="margin-left: 10%"
>保存</j-button
>
<PermissionButton
type="primary"
:popConfirm="{
title: '确认同步系统菜单权限?',
okText: ' 确定',
cancelText: '取消',
onConfirm: synchronization,
}"
style="margin-left: 20px"
>同步系统菜单权限
</PermissionButton>
</j-card>
<j-modal
modalType="message"
@ -72,20 +83,18 @@ import {
mergeArr,
findAllParentsAndChildren,
handleSorts,
handleSortsArr
handleSortsArr,
} from './utils';
import BaseMenu from '@/views/init-home/data/baseMenu';
import type { AntTreeNodeDropEvent } from 'ant-design-vue/es/tree';
import { cloneDeep } from 'lodash-es';
import { onlyMessage } from '@/utils/comm';
import {
USER_CENTER_MENU_CODE,
messageSubscribe
} from '@/utils/consts';
import { USER_CENTER_MENU_CODE, messageSubscribe } from '@/utils/consts';
import { protocolList } from '@/utils/consts';
import { getProviders } from '@/api/data-collect/channel';
import { isNoCommunity } from '@/utils/utils';
import { USER_CENTER_MENU_DATA } from '@/views/init-home/data/baseMenu'
import { USER_CENTER_MENU_DATA } from '@/views/init-home/data/baseMenu';
import { unionBy } from 'lodash';
const selectedKeys: any = ref([]);
const treeData = ref<any>([]);
@ -121,15 +130,15 @@ const params = {
*/
let filterProtocolList: any[] = [];
const getProvidersFn = async () => {
if(!isNoCommunity){
return
}else{
if (!isNoCommunity) {
return;
} else {
const res: any = await getProviders();
filterProtocolList = protocolList.filter((item) => {
return res.result?.find((val: any) => item.alias == val.id);
})
return res.result?.find((val: any) => item.alias == val.id);
});
}
}
};
getProvidersFn();
/**
* 作用过滤掉非选中菜单重新组成新的数组
@ -162,46 +171,52 @@ getProvidersFn();
* @param selectedKeys 选中的菜单
* 选中和非选中改变show的值
*/
const dealTree = (nodes: Array<any>, selectedKeys: Array<any>,parentId?:string) =>{
const dealTree = (
nodes: Array<any>,
selectedKeys: Array<any>,
parentId?: string,
) => {
const filtered = [];
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (!node.code) {
continue;
}
node.parentId = parentId ? undefined : parentId
node?.options ? node.options.show = false : node.options = { show : false }
node.parentId = parentId ? undefined : parentId;
node?.options
? (node.options.show = false)
: (node.options = { show: false });
if (selectedKeys.indexOf(node.code) !== -1) {
node.options.show = true
node.options.show = true;
if (node.children) {
node.children = dealTree(node.children, selectedKeys,node.id);
node.children = dealTree(node.children, selectedKeys, node.id);
}
} else if (node.children) {
node.children = dealTree(node.children, selectedKeys,node.id);
const children =node.children.filter((item:any)=>{
item.options.show === true
})
node.children = dealTree(node.children, selectedKeys, node.id);
const children = node.children.filter((item: any) => {
item.options.show === true;
});
if (children.length > 0) {
node.options.show = true
node.options.show = true;
}
}else{
node.options.show = false
} else {
node.options.show = false;
}
filtered.push(node)
filtered.push(node);
}
return filtered;
}
};
const handleOk = async () => {
// const _dataArr = filterTree(cloneDeep(treeData.value), selectedKeys.value);
const _dataArr = dealTree(cloneDeep(treeData.value),selectedKeys.value)
const _dataSorts = handleSorts(_dataArr)
const _dataArr = dealTree(cloneDeep(treeData.value), selectedKeys.value);
const _dataSorts = handleSorts(_dataArr);
loading.value = true;
_dataSorts.push(USER_CENTER_MENU_DATA)
_dataSorts.push(USER_CENTER_MENU_DATA);
const res = await updateMenus(_dataSorts).catch(() => {});
if (res?.status === 200) {
onlyMessage('操作成功', 'success');
location.reload()
location.reload();
}
loading.value = false;
visible.value = false;
@ -245,6 +260,36 @@ const onDragend = (info: AntTreeNodeDropEvent) => {
}
};
const synchronization = async () => {
const menu = synchronizationMenu(cloneDeep(systemMenu.value), BaseMenu);
menu.push(USER_CENTER_MENU_DATA);
const res = await updateMenus(menu).catch(() => {});
if (res?.status === 200) {
onlyMessage('操作成功', 'success');
location.reload();
}
};
//
const synchronizationMenu = (menu: any, baseMenu: any) => {
const newMenu = menu.map((i: any) => {
baseMenu.find((item: any) => {
if (i.id === item.id) {
i.buttons = unionBy(i.buttons, item.buttons, 'id');
i.permissions = unionBy(
i.permissions,
item.permissions,
'permission',
);
if (item.children && i.children) {
i.children = synchronizationMenu(i.children, item.children);
}
}
});
return i;
});
return unionBy(newMenu, baseMenu, 'code');
};
onMounted(() => {
getSystemPermission_api().then((resp: any) => {
// const filterBaseMenu = BaseMenu.filter(item => ![
@ -258,9 +303,9 @@ onMounted(() => {
if (resp.status == 200) {
systemMenu.value = resp.result?.filter(
(item: { code: string }) =>
![
USER_CENTER_MENU_CODE,messageSubscribe
].includes(item.code),
![USER_CENTER_MENU_CODE, messageSubscribe].includes(
item.code,
),
);
//
// initData(baseMenu.value); // keyname
@ -285,7 +330,7 @@ const filterMenus = (menus: any[]) => {
if (!filterProtocolList.length && item.code == 'link/DataCollect') {
return false;
}
return item
return item;
});
};
</script>

View File

@ -132,7 +132,7 @@ export const inItSelected = (Menu:any) =>{
arr.forEach((item: any) => {
item.title = item.code;
item.key = item.code; // treeData需要唯一key
item?.options?.show ? checkedKeys.push(item.code) : '';
item?.options?.show === false ? '' : checkedKeys.push(item.code);
if (item?.children) {
getMap(item?.children);
}

View File

@ -211,8 +211,8 @@ const table = reactive({
type: 'or',
terms: [
{
value: '%show":true%',
termType: 'like',
value: '%show":false%',
termType: 'nlike',
column: 'options',
},
],

View File

@ -72,7 +72,7 @@ const systemNotice = [
},
{
provider: 'alarm-org',
name: '部门告警',
name: '组织告警',
},
{
provider: 'alarm-other',

View File

@ -5,5 +5,5 @@
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts", "src/vite-env.d.ts", "config/**/*.ts", "plugin/**/*.ts"]
"include": ["vite.config.ts", "src/vite-env.d.ts", "config/**/*.ts", "plugin/**/*.ts", "src/**/*.vue"]
}

View File

@ -96,8 +96,8 @@ export default defineConfig(({ mode}) => {
// target: 'http://192.168.32.244:8881',
// target: 'http://192.168.32.217:8844', //张本地
// target: 'http://120.77.179.54:8844', // 120测试
target: 'http://192.168.33.46:8844', // 本地开发环境
// target: 'http://192.168.32.167:8844', // 本地开发环境1
// target: 'http://192.168.33.46:8844', // 本地开发环境
target: 'http://192.168.33.97:8844', // 本地开发环境1
// target: 'http://192.168.33.6:38848', // 社区版开发环境
// target: 'http://192.168.32.207:8844', // 刘本地
// target: 'http://192.168.32.187:8844', // 谭本地
@ -114,7 +114,6 @@ export default defineConfig(({ mode}) => {
less: {
modifyVars: {
'root-entry-name': 'variable',
'primary-color': '#1677FF',
hack: `true; @import (reference) "${path.resolve('src/style/variable.less')}";`,
},
javascriptEnabled: true,