Merge remote-tracking branch 'origin/dev' into dev

# Conflicts:
#	vite.config.ts
This commit is contained in:
xieyonghong 2023-02-23 16:37:51 +08:00
commit 4258311ed6
34 changed files with 7247 additions and 5181 deletions

View File

@ -403,3 +403,59 @@ export const treeMapping = (data?: any) => server.post(`/data-collect/channel/_a
* @returns * @returns
*/ */
export const saveMapping = (thingId: any, provider: string, data?: any) => server.patch(`/things/collector/device/${thingId}/${provider}`, data) export const saveMapping = (thingId: any, provider: string, data?: any) => server.patch(`/things/collector/device/${thingId}/${provider}`, data)
/**
*
* @param deviceId
* @param data
* @returns
*/
export const edgeChannel = (deviceId: string, data?: any) => server.post(`/edge/operations/${deviceId}/data-collector-channel-list/invoke`, data)
/**
*
* @param deviceId
* @param data
* @returns
*/
export const edgeCollector = (deviceId: string, data?: any) => server.post(`/edge/operations/${deviceId}/data-collector-list/invoke`, data)
/**
*
* @param deviceId
* @param data
* @returns
*/
export const edgePoint = (deviceId: string, data?: any) => server.post(`/edge/operations/${deviceId}/data-collector-point-list/invoke`, data)
/**
*
* @param deviceId
* @param data
* @returns
*/
export const getEdgeMap = (deviceId: string, data?: any) => server.post(`/edge/operations/${deviceId}/device-collector-list/invoke`, data)
/**
*
* @param deviceId
* @param data
* @returns
*/
export const removeEdgeMap = (deviceId: string, data?: any) => server.post(`/edge/operations/${deviceId}/device-collector-delete/invoke`, data)
/**
*
* @param deviceId
* @param data
* @returns
*/
export const treeEdgeMap = (deviceId: string, data?: any) => server.post(`/edge/operations/${deviceId}/data-collector-channel-tree/invoke`, data)
/**
*
* @param deviceId
* @param data
* @returns
*/
export const saveEdgeMap = (deviceId: string, data?: any) => server.post(`/edge/operations/${deviceId}/device-collector-save/invoke`, data)

View File

@ -11,8 +11,8 @@ export default {
// 修改 // 修改
update: (data: any) => patch(`/notifier/template`, data), update: (data: any) => patch(`/notifier/template`, data),
del: (id: any) => remove(`/notifier/template/${id}`), del: (id: any) => remove(`/notifier/template/${id}`),
getConfig: (data: any) => post<BindConfig>(`/notifier/config/_query/no-paging?paging=false`, data), getConfig: (data: any) => post<BindConfig[]>(`/notifier/config/_query/no-paging?paging=false`, data),
getTemplateDetail: (id: string) => get(`/notifier/template/${id}/detail`), getTemplateDetail: (id: string) => get<any>(`/notifier/template/${id}/detail`),
debug: (data: any, configId: string, templateId: string) => post(`/notifier/${configId}/${templateId}/_send`, data), debug: (data: any, configId: string, templateId: string) => post(`/notifier/${configId}/${templateId}/_send`, data),
getHistory: (data: any, id: string) => post(`/notify/history/template/${id}/_query`, data), getHistory: (data: any, id: string) => post(`/notify/history/template/${id}/_query`, data),
// 钉钉/微信, 根据配置获取部门和用户 // 钉钉/微信, 根据配置获取部门和用户

View File

@ -51,7 +51,8 @@ const iconKeys = [
'playCircleOutlined', 'playCircleOutlined',
'RightOutlined', 'RightOutlined',
'FileTextOutlined', 'FileTextOutlined',
'UploadOutlined' 'UploadOutlined',
'ArrowLeftOutlined'
] ]
const Icon = (props: {type: string}) => { const Icon = (props: {type: string}) => {

View File

@ -0,0 +1,36 @@
<svg width="58" height="50" viewBox="0 0 58 50" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M29 49.5794C45.0163 49.5794 58 46.8079 58 43.3891C58 39.9702 45.0163 37.1987 29 37.1987C12.9837 37.1987 0 39.9702 0 43.3891C0 46.8079 12.9837 49.5794 29 49.5794Z" fill="#FAFAFA"/>
<path d="M33.2639 9.99902L9.93594 16.3635C9.47351 16.4896 9.43643 17.1312 9.88123 17.3098L26.5406 23.999L49.1273 18.1035C49.5839 17.9843 49.6364 17.3571 49.206 17.1637L33.2639 9.99902Z" fill="#FAFAFA" stroke="#E0E0E0"/>
<path d="M24.2975 4.69484C25.7779 5.25264 26.8448 8.08321 25.7779 9.45134L22.4067 10.9999C21.6665 10.2491 20.7123 8.2462 21.0824 6.52395C21.2746 5.62954 21.4884 5.19123 22.1983 4.7653C22.8554 4.37107 23.7889 4.5032 24.2975 4.69484Z" fill="white" stroke="#E0E0E0"/>
<path d="M35.2162 3.78468C33.7358 4.34248 32.6689 7.17306 33.7358 8.54119L37.107 10.0897C37.8472 9.33897 38.8014 7.33604 38.4313 5.61379C38.2391 4.71939 38.0253 4.28107 37.3153 3.85514C36.6583 3.46091 35.7248 3.59304 35.2162 3.78468Z" fill="white" stroke="#E0E0E0"/>
<mask id="path-5-inside-1_990_11491" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M42.5449 17.5308C42.5432 17.4907 42.5411 17.4504 42.5387 17.4099C42.3461 14.2156 40.8848 9.80379 37.5252 7.09156C35.6005 5.42908 33.0395 4.3785 29.6986 4.65434C26.3456 4.78127 23.9278 6.13333 22.2162 8.01686C19.2086 11.1131 18.2885 15.6671 18.4811 18.8607C18.4835 18.9011 18.4863 18.9414 18.4894 18.9814C18.515 19.5938 18.6444 20.1629 18.8655 20.688C20.262 24.3125 25.1708 25.5541 30.934 25.2065C30.9736 25.2042 31.0131 25.2017 31.0526 25.1991C36.7632 24.8346 41.4308 23.0119 42.3762 19.2717C42.533 18.7234 42.5931 18.1424 42.5449 17.5308Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M42.5449 17.5308C42.5432 17.4907 42.5411 17.4504 42.5387 17.4099C42.3461 14.2156 40.8848 9.80379 37.5252 7.09156C35.6005 5.42908 33.0395 4.3785 29.6986 4.65434C26.3456 4.78127 23.9278 6.13333 22.2162 8.01686C19.2086 11.1131 18.2885 15.6671 18.4811 18.8607C18.4835 18.9011 18.4863 18.9414 18.4894 18.9814C18.515 19.5938 18.6444 20.1629 18.8655 20.688C20.262 24.3125 25.1708 25.5541 30.934 25.2065C30.9736 25.2042 31.0131 25.2017 31.0526 25.1991C36.7632 24.8346 41.4308 23.0119 42.3762 19.2717C42.533 18.7234 42.5931 18.1424 42.5449 17.5308Z" fill="white"/>
<path d="M42.5387 17.4099L43.5369 17.3497L43.5369 17.3497L42.5387 17.4099ZM42.5449 17.5308L41.5459 17.5738L41.5466 17.5916L41.548 17.6094L42.5449 17.5308ZM37.5252 7.09156L36.8715 7.84832L36.8841 7.8592L36.897 7.86964L37.5252 7.09156ZM29.6986 4.65434L29.7364 5.65362L29.7587 5.65278L29.7809 5.65095L29.6986 4.65434ZM22.2162 8.01686L22.9335 8.71363L22.9451 8.7017L22.9563 8.68938L22.2162 8.01686ZM18.4811 18.8607L17.4829 18.9209L18.4811 18.8607ZM18.4894 18.9814L19.4885 18.9396L19.4878 18.9217L19.4864 18.904L18.4894 18.9814ZM18.8655 20.688L19.7987 20.3284L19.7931 20.3141L19.7871 20.2999L18.8655 20.688ZM31.0526 25.1991L30.9889 24.2011L30.9878 24.2012L31.0526 25.1991ZM42.3762 19.2717L41.4148 18.9968L41.4105 19.0117L41.4067 19.0266L42.3762 19.2717ZM41.5405 17.4701C41.5426 17.5049 41.5444 17.5394 41.5459 17.5738L43.544 17.4879C43.5421 17.442 43.5397 17.3959 43.5369 17.3497L41.5405 17.4701ZM36.897 7.86964C39.9727 10.3527 41.359 14.4604 41.5405 17.4701L43.5369 17.3497C43.3331 13.9708 41.7968 9.2549 38.1533 6.31347L36.897 7.86964ZM38.1788 6.33479C36.0609 4.50532 33.2375 3.35875 29.6163 3.65773L29.7809 5.65095C32.8416 5.39825 35.1402 6.35285 36.8715 7.84832L38.1788 6.33479ZM22.9563 8.68938C24.496 6.99503 26.6647 5.76991 29.7364 5.65362L29.6608 3.65505C26.0266 3.79264 23.3597 5.27163 21.4761 7.34434L22.9563 8.68938ZM21.4989 7.3201C18.2372 10.6779 17.2792 15.5428 17.4829 18.9209L19.4793 18.8005C19.2978 15.7915 20.18 11.5483 22.9335 8.71363L21.4989 7.3201ZM17.4829 18.9209C17.4857 18.967 17.4888 19.013 17.4924 19.0588L19.4864 18.904C19.4837 18.8697 19.4814 18.8352 19.4793 18.8005L17.4829 18.9209ZM19.7871 20.2999C19.6131 19.8867 19.5093 19.435 19.4885 18.9396L17.4903 19.0232C17.5208 19.7525 17.6756 20.439 17.9439 21.0761L19.7871 20.2999ZM17.9324 21.0475C18.7723 23.2274 20.6451 24.6048 22.941 25.3838C25.2296 26.1603 28.0393 26.3829 30.9942 26.2047L30.8738 24.2084C28.0655 24.3777 25.5393 24.1534 23.5835 23.4898C21.6351 22.8288 20.3553 21.7731 19.7987 20.3284L17.9324 21.0475ZM30.9942 26.2047C31.0353 26.2023 31.0763 26.1997 31.1174 26.197L30.9878 24.2012C30.9499 24.2037 30.9119 24.2061 30.8738 24.2084L30.9942 26.2047ZM41.4067 19.0266C41.0298 20.5176 39.8986 21.7128 38.0652 22.6014C36.2245 23.4935 33.7708 24.0236 30.9889 24.2011L31.1163 26.1971C34.045 26.0101 36.7804 25.4466 38.9375 24.4012C41.1019 23.3522 42.7772 21.766 43.3457 19.5167L41.4067 19.0266ZM43.3377 19.5465C43.5279 18.8813 43.5992 18.1806 43.5419 17.4523L41.548 17.6094C41.587 18.1042 41.5381 18.5654 41.4148 18.9968L43.3377 19.5465Z" fill="#E0E0E0" mask="url(#path-5-inside-1_990_11491)"/>
<ellipse cx="21.5982" cy="19.5977" rx="2.27602" ry="1.30058" transform="rotate(-3.45082 21.5982 19.5977)" fill="#F4F4F4"/>
<ellipse cx="2.27602" cy="1.30058" rx="2.27602" ry="1.30058" transform="matrix(-0.974459 0.224564 0.224564 0.974459 43.8887 15.7603)" fill="#F4F4F4"/>
<circle cx="26.0809" cy="15.0666" r="1.84249" transform="rotate(-3.45082 26.0809 15.0666)" fill="#E0E0E0"/>
<circle cx="26.6121" cy="14.6075" r="0.650291" transform="rotate(-3.45082 26.6121 14.6075)" fill="white"/>
<circle cx="25.4848" cy="15.971" r="0.325146" transform="rotate(-3.45082 25.4848 15.971)" fill="white"/>
<circle cx="37.7645" cy="14.362" r="1.84249" transform="rotate(-3.45082 37.7645 14.362)" fill="#E0E0E0"/>
<circle cx="38.2937" cy="13.9029" r="0.650291" transform="rotate(-3.45082 38.2937 13.9029)" fill="white"/>
<circle cx="37.1683" cy="15.2669" r="0.325146" transform="rotate(-3.45082 37.1683 15.2669)" fill="white"/>
<ellipse cx="32.3087" cy="16.6698" rx="1.51282" ry="1.23036" transform="rotate(-3.45082 32.3087 16.6698)" fill="#E0E0E0"/>
<mask id="mask0_990_11491" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="28" y="19" width="8" height="4">
<path d="M28.8401 21.0038C29.1929 20.5749 29.549 20.2953 30.0249 20.0923C30.5162 19.8826 31.1577 19.7447 32.1 19.6404C33.021 19.5386 33.5501 19.5974 33.9102 19.7125C34.2573 19.8235 34.4861 19.9957 34.8185 20.246C34.8296 20.2544 34.8408 20.2628 34.8521 20.2713C35.02 20.3977 35.1717 20.6012 35.2384 20.787C35.2708 20.8773 35.274 20.938 35.2703 20.9682C35.2681 20.9866 35.2652 20.9882 35.2637 20.989C35.2635 20.9891 35.2633 20.9892 35.2632 20.9893C34.8844 21.3249 34.6341 21.5424 34.2159 21.7233C33.7851 21.9096 33.148 22.067 32.0018 22.1938C30.8548 22.3207 30.2398 22.3016 29.8387 22.2257C29.513 22.164 29.319 22.0665 29.0343 21.9233C28.9772 21.8947 28.9166 21.8642 28.8504 21.8317C28.7416 21.7784 28.657 21.6792 28.6311 21.5668C28.609 21.4712 28.6107 21.2826 28.8401 21.0038Z" fill="#FD7477" stroke="#666666"/>
</mask>
<g mask="url(#mask0_990_11491)">
<path d="M31.2438 19.4706L31.0537 18.6566C31.0373 18.5863 31.1139 18.545 31.1837 18.5631C31.3003 18.5935 31.4791 18.5924 31.6924 18.4731C32.0018 18.3001 32.1775 17.9936 32.2267 17.862L32.3581 20.041L32.0351 20.0605C31.6628 20.083 31.3286 19.8338 31.2438 19.4706Z" fill="white" stroke="#E0E0E0" stroke-width="0.5"/>
<path d="M33.4522 19.3375L33.543 18.5065C33.5509 18.4348 33.4699 18.403 33.4028 18.4294C33.2906 18.4735 33.113 18.4939 32.887 18.4012C32.559 18.2666 32.3477 17.9835 32.2831 17.8587L32.4145 20.0378L32.7374 20.0183C33.1097 19.9958 33.4116 19.7083 33.4522 19.3375Z" fill="white" stroke="#E0E0E0" stroke-width="0.5"/>
</g>
<path d="M31.421 18.7266C30.2561 19.2905 29.7431 20.4957 29.7177 20.5566C29.7081 20.5796 29.7032 20.6044 29.7031 20.6293C29.7031 20.6543 29.708 20.679 29.7175 20.7021C29.727 20.7252 29.741 20.7462 29.7587 20.7639C29.7763 20.7815 29.7972 20.7956 29.8203 20.8052C29.8434 20.8147 29.8681 20.8197 29.893 20.8197C29.918 20.8198 29.9428 20.8149 29.9658 20.8053C29.9889 20.7958 30.0099 20.7818 30.0276 20.7642C30.0453 20.7466 30.0593 20.7256 30.0689 20.7026C30.0756 20.6866 30.6789 19.416 32.19 19.1705C33.6941 18.926 34.9386 20.104 34.9503 20.119C34.9812 20.1584 35.0264 20.184 35.076 20.1903C35.1257 20.1966 35.1758 20.183 35.2155 20.1526C35.2553 20.1222 35.2814 20.0773 35.2882 20.0277C35.2951 19.9781 35.2821 19.9278 35.2522 19.8878C35.0034 19.5814 34.7147 19.3099 34.3937 19.0805C33.6826 18.5696 32.926 18.3613 32.2053 18.4785C31.9332 18.5227 31.669 18.6063 31.421 18.7266Z" fill="#E0E0E0" stroke="#E0E0E0" stroke-width="0.1"/>
<line x1="23.7287" y1="18.1659" x2="23.5991" y2="18.5272" stroke="#E0E0E0" stroke-width="0.7" stroke-linecap="round"/>
<line x1="24.9494" y1="18.31" x2="24.893" y2="18.4672" stroke="#E0E0E0" stroke-width="0.7" stroke-linecap="round"/>
<line x1="0.35" y1="-0.35" x2="0.733819" y2="-0.35" transform="matrix(0.44853 0.893768 0.893768 -0.44853 40.4297 15.9731)" stroke="#E0E0E0" stroke-width="0.7" stroke-linecap="round"/>
<line x1="0.35" y1="-0.35" x2="0.517055" y2="-0.35" transform="matrix(0.448552 0.893757 0.893757 -0.448552 39.2363 16.2622)" stroke="#E0E0E0" stroke-width="0.7" stroke-linecap="round"/>
<path d="M9 17.5027C9 17.1455 9.36388 16.9035 9.69336 17.0416L25 23.4605V46.2127L9 38.7176V17.5027Z" fill="#FBFBFB" stroke="#E0E0E0"/>
<path d="M8.74631 16.66L24.8561 23.8265L22.245 30.3166L6.15202 22.5625L8.74631 16.66Z" fill="white" stroke="#E0E0E0"/>
<path d="M50 18.2859C50 17.9597 49.6926 17.7208 49.3765 17.8014L25.5 23.8878V45.8708C25.5 46.2033 25.8185 46.4432 26.1381 46.3513L50 39.4934V18.2859Z" fill="#F4F4F4" stroke="#E0E0E0"/>
<path d="M50.0635 18.0495C49.9823 17.7978 49.719 17.6529 49.4628 17.719L26.1262 23.7393L28.0893 30.3834L51.865 23.6308L50.0635 18.0495Z" fill="white" stroke="#E0E0E0"/>
</svg>

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -0,0 +1,44 @@
<template>
<Empty v-bind="baseProps">
<template v-for="item in renderArr" :key="item" #[item]="scope">
<slot :name="item" :scope="scope"></slot>
</template>
</Empty>
</template>
<script lang="ts" setup name="JEmpty">
import { Empty } from 'ant-design-vue';
import { useSlots } from 'vue';
import NoData from './assets/nodata.svg';
import { omit } from 'lodash-es';
import type { PropType, CSSProperties } from 'vue';
const slots = useSlots();
const renderArr = Object.keys(slots);
console.log(renderArr);
const props = defineProps({
description: {
type: String,
default: '暂无数据',
},
image: {
type: String,
default: NoData,
},
imageStyle: {
type: Object as PropType<CSSProperties>,
default: () => {
return { height: '60px' };
},
},
});
const baseProps = omit(props, ...renderArr);
</script>
<style lang="less" scoped>
:deep(.ant-empty-description) {
color: #b3b3b3;
font-size: 14px;
}
</style>

View File

@ -4,7 +4,7 @@
<a-popconfirm v-bind="popConfirm" :disabled="!isPermission || props.disabled"> <a-popconfirm v-bind="popConfirm" :disabled="!isPermission || props.disabled">
<a-tooltip v-if="tooltip" v-bind="tooltip"> <a-tooltip v-if="tooltip" v-bind="tooltip">
<slot v-if="noButton"></slot> <slot v-if="noButton"></slot>
<a-button v-else v-bind="_buttonProps" :disabled="_isPermission" > <a-button v-else v-bind="_buttonProps" :disabled="_isPermission" :style="props.style">
<slot></slot> <slot></slot>
<template #icon> <template #icon>
<slot name="icon"></slot> <slot name="icon"></slot>
@ -22,7 +22,7 @@
<template v-else-if="tooltip"> <template v-else-if="tooltip">
<a-tooltip v-bind="tooltip"> <a-tooltip v-bind="tooltip">
<slot v-if="noButton"></slot> <slot v-if="noButton"></slot>
<a-button v-else v-bind="_buttonProps" :disabled="_isPermission" > <a-button v-else v-bind="_buttonProps" :disabled="_isPermission" :style="props.style">
<slot></slot> <slot></slot>
<template #icon> <template #icon>
<slot name="icon"></slot> <slot name="icon"></slot>
@ -32,7 +32,7 @@
</template> </template>
<template v-else> <template v-else>
<slot v-if="noButton"></slot> <slot v-if="noButton"></slot>
<a-button v-else v-bind="_buttonProps" :disabled="_isPermission" > <a-button v-else v-bind="_buttonProps" :disabled="_isPermission" :style="props.style">
<slot></slot> <slot></slot>
<template #icon> <template #icon>
<slot name="icon"></slot> <slot name="icon"></slot>
@ -42,7 +42,7 @@
</template> </template>
<a-tooltip v-else title="没有权限"> <a-tooltip v-else title="没有权限">
<slot v-if="noButton"></slot> <slot v-if="noButton"></slot>
<a-button v-else v-bind="_buttonProps" :disabled="_isPermission" > <a-button v-else v-bind="_buttonProps" :disabled="_isPermission" :style="props.style">
<slot></slot> <slot></slot>
<template #icon> <template #icon>
<slot name="icon"></slot> <slot name="icon"></slot>
@ -51,7 +51,7 @@
</a-tooltip> </a-tooltip>
</template> </template>
<script setup lang="ts" name="PermissionButton"> <script setup lang="ts" name="PermissionButton">
import { PropType } from 'vue' import { CSSProperties, PropType } from 'vue'
import { TooltipProps, PopconfirmProps } from 'ant-design-vue/es' import { TooltipProps, PopconfirmProps } from 'ant-design-vue/es'
import { buttonProps } from 'ant-design-vue/es/button/button' import { buttonProps } from 'ant-design-vue/es/button/button'
import { usePermissionStore } from '@/store/permission'; import { usePermissionStore } from '@/store/permission';
@ -85,6 +85,9 @@ const props = defineProps({
hasPermission: { hasPermission: {
type: String || Array, type: String || Array,
}, },
style: {
type: Object as PropType<CSSProperties>
},
...buttonProps() ...buttonProps()
}) })

View File

@ -1,11 +1,12 @@
import { UnorderedListOutlined, AppstoreOutlined } from '@ant-design/icons-vue' import { UnorderedListOutlined, AppstoreOutlined } from '@ant-design/icons-vue'
import styles from './index.module.less' import styles from './index.module.less'
import { Pagination, Table, Empty, Spin, Alert } from 'ant-design-vue' import { Pagination, Table, Spin, Alert } from 'ant-design-vue'
import type { TableProps } from 'ant-design-vue/es/table' import type { TableProps } from 'ant-design-vue/es/table'
import type { TooltipProps } from 'ant-design-vue/es/tooltip' import type { TooltipProps } from 'ant-design-vue/es/tooltip'
import type { PopconfirmProps } from 'ant-design-vue/es/popconfirm' import type { PopconfirmProps } from 'ant-design-vue/es/popconfirm'
import { CSSProperties, PropType } from 'vue'; import { CSSProperties, PropType } from 'vue';
import type { JColumnsProps } from './types' import type { JColumnsProps } from './types'
import JEmpty from '@/components/Empty/index.vue'
enum ModelEnum { enum ModelEnum {
TABLE = 'TABLE', TABLE = 'TABLE',
@ -146,7 +147,6 @@ const JTable = defineComponent<JTableProps>({
} }
} as any, } as any,
setup(props: JTableProps, { slots, emit, expose }) { setup(props: JTableProps, { slots, emit, expose }) {
const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE
const _model = ref<keyof typeof ModelEnum>(props.model ? props.model : ModelEnum.CARD); // 模式切换 const _model = ref<keyof typeof ModelEnum>(props.model ? props.model : ModelEnum.CARD); // 模式切换
const column = ref<number>(props.gridColumn || 4); const column = ref<number>(props.gridColumn || 4);
const _dataSource = ref<Record<string, any>[]>([]) const _dataSource = ref<Record<string, any>[]>([])
@ -154,6 +154,7 @@ const JTable = defineComponent<JTableProps>({
const pageSize = ref<number>(6) const pageSize = ref<number>(6)
const total = ref<number>(0) const total = ref<number>(0)
const loading = ref<boolean>(true) const loading = ref<boolean>(true)
const loading1 = ref<boolean>(true)
const _columns = computed(() => props.columns.filter(i => !(i?.hideInTable))) const _columns = computed(() => props.columns.filter(i => !(i?.hideInTable)))
@ -244,10 +245,6 @@ const JTable = defineComponent<JTableProps>({
window.onresize = null window.onresize = null
}) })
watchEffect(() => {
// console.log(props.bodyStyle)
})
/** /**
* *
* @param _params * @param _params
@ -292,7 +289,8 @@ const JTable = defineComponent<JTableProps>({
</div> </div>
</div> </div>
{/* content */} {/* content */}
<div class={styles['jtable-content']}> {
!loading.value ? <div class={styles['jtable-content']}>
{ {
props.alertRender && props?.rowSelection && props?.rowSelection?.selectedRowKeys && props.rowSelection.selectedRowKeys?.length ? props.alertRender && props?.rowSelection && props?.rowSelection?.selectedRowKeys && props.rowSelection.selectedRowKeys?.length ?
<div class={styles['jtable-alert']}> <div class={styles['jtable-alert']}>
@ -323,7 +321,7 @@ const JTable = defineComponent<JTableProps>({
) )
} }
</div> : </div> :
<div><Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /></div> <div><JEmpty style="margin: 10% 0" /></div>
} }
</div> : </div> :
<div> <div>
@ -343,12 +341,14 @@ const JTable = defineComponent<JTableProps>({
} else { } else {
return record?.[column?.dataIndex] || '' return record?.[column?.dataIndex] || ''
} }
} },
emptyText: () => <JEmpty style="margin: 10% 0" />
}} }}
/> />
</div> </div>
} }
</div> </div> : <div style="width: 100%; height: 400px"></div>
}
{/* 分页 */} {/* 分页 */}
{ {
(!!_dataSource.value.length) && !props.noPagination && props.type === 'PAGE' && (!!_dataSource.value.length) && !props.noPagination && props.type === 'PAGE' &&

View File

@ -11,6 +11,7 @@ import FileFormat from './FileFormat/index.vue'
import JUpload from './JUpload/index.vue' import JUpload from './JUpload/index.vue'
import { BasicLayoutPage, BlankLayoutPage, PageContainer } from './Layout' import { BasicLayoutPage, BlankLayoutPage, PageContainer } from './Layout'
import Ellipsis from './Ellipsis/index.vue' import Ellipsis from './Ellipsis/index.vue'
import JEmpty from './Empty/index.vue'
export default { export default {
install(app: App) { install(app: App) {
@ -28,5 +29,6 @@ export default {
.component('BlankLayoutPage', BlankLayoutPage) .component('BlankLayoutPage', BlankLayoutPage)
.component('PageContainer', PageContainer) .component('PageContainer', PageContainer)
.component('Ellipsis', Ellipsis) .component('Ellipsis', Ellipsis)
.component('JEmpty', JEmpty)
} }
} }

View File

@ -226,11 +226,11 @@
:key="index" :key="index"
:header=" :header="
item.productKey item.productKey
? aliyunProductList.find( ? (aliyunProductList.find(
(i) => (i) =>
i.productKey === i.productKey ===
item.productKey, item.productKey,
)?.productName )?.productName || `产品映射${index + 1}`)
: `产品映射${index + 1}` : `产品映射${index + 1}`
" "
> >
@ -356,12 +356,14 @@
</a-row> </a-row>
</a-form> </a-form>
<div v-if="type === 'edit'"> <div v-if="type === 'edit'">
<a-button <PermissionButton
:loading="loading"
type="primary" type="primary"
:loading="loading"
@click="saveBtn" @click="saveBtn"
>保存</a-button :hasPermission="['Northbound/AliCloud:add', 'Northbound/AliCloud:update']"
> >
保存
</PermissionButton>
</div> </div>
</a-col> </a-col>
<a-col :span="8"> <a-col :span="8">
@ -497,11 +499,11 @@ const saveBtn = () => {
.then(async (data: any) => { .then(async (data: any) => {
const product = (aliyunProductList.value || []).find( const product = (aliyunProductList.value || []).find(
(item: any) => (item: any) =>
item?.bridgeProductKey === data?.bridgeProductKey, item?.productKey === data?.bridgeProductKey,
); );
data.bridgeProductName = product?.productName || ''; data.bridgeProductName = product?.productName || '';
loading.value = true; loading.value = true;
const resp = await savePatch(toRaw(modelRef)); const resp = await savePatch({...toRaw(modelRef), ...data});
loading.value = false; loading.value = false;
if (resp.status === 200) { if (resp.status === 200) {
message.success('操作成功!'); message.success('操作成功!');

View File

@ -1,6 +1,6 @@
<template> <template>
<page-container> <page-container>
<Search :columns="columns" target="northbound-dueros" @search="handleSearch" /> <Search :columns="columns" target="northbound-aliyun" @search="handleSearch" />
<JTable <JTable
ref="instanceRef" ref="instanceRef"
:columns="columns" :columns="columns"
@ -10,7 +10,14 @@
> >
<template #headerTitle> <template #headerTitle>
<a-space> <a-space>
<a-button type="primary" @click="handleAdd">新增</a-button> <PermissionButton
type="primary"
@click="handleAdd"
hasPermission="Northbound/AliCloud:add"
>
<template #icon><AIcon type="PlusOutlined" /></template>
新增
</PermissionButton>
</a-space> </a-space>
</template> </template>
<template #card="slotProps"> <template #card="slotProps">
@ -57,30 +64,12 @@
</a-row> </a-row>
</template> </template>
<template #actions="item"> <template #actions="item">
<a-tooltip <PermissionButton
v-bind="item.tooltip"
:title="item.disabled && item.tooltip.title"
>
<a-popconfirm
v-if="item.popConfirm"
v-bind="item.popConfirm"
:disabled="item.disabled"
>
<a-button :disabled="item.disabled">
<AIcon
type="DeleteOutlined"
v-if="item.key === 'delete'"
/>
<template v-else>
<AIcon :type="item.icon" />
<span>{{ item?.text }}</span>
</template>
</a-button>
</a-popconfirm>
<template v-else>
<a-button
:disabled="item.disabled" :disabled="item.disabled"
:popConfirm="item.popConfirm"
:tooltip="item.tooltip"
@click="item.onClick" @click="item.onClick"
:hasPermission="'Northbound/AliCloud:' + item.key"
> >
<AIcon <AIcon
type="DeleteOutlined" type="DeleteOutlined"
@ -90,9 +79,7 @@
<AIcon :type="item.icon" /> <AIcon :type="item.icon" />
<span>{{ item?.text }}</span> <span>{{ item?.text }}</span>
</template> </template>
</a-button> </PermissionButton>
</template>
</a-tooltip>
</template> </template>
</CardBox> </CardBox>
</template> </template>
@ -103,38 +90,23 @@
/> />
</template> </template>
<template #action="slotProps"> <template #action="slotProps">
<a-space :size="16"> <a-space>
<a-tooltip <template
v-for="i in getActions(slotProps, 'table')" v-for="i in getActions(slotProps, 'table')"
:key="i.key" :key="i.key"
v-bind="i.tooltip"
> >
<a-popconfirm <PermissionButton
v-if="i.popConfirm"
v-bind="i.popConfirm"
:disabled="i.disabled" :disabled="i.disabled"
:popConfirm="i.popConfirm"
:tooltip="i.tooltip"
style="padding: 0px"
@click="i.onClick"
type="link"
:hasPermission="'Northbound/AliCloud:' + i.key"
> >
<a-button <template #icon><AIcon :type="i.icon" /></template>
:disabled="i.disabled" </PermissionButton>
style="padding: 0" </template>
type="link"
><AIcon :type="i.icon"
/></a-button>
</a-popconfirm>
<a-button
style="padding: 0"
type="link"
v-else
@click="i.onClick && i.onClick(slotProps)"
>
<a-button
:disabled="i.disabled"
style="padding: 0"
type="link"
><AIcon :type="i.icon"
/></a-button>
</a-button>
</a-tooltip>
</a-space> </a-space>
</template> </template>
</JTable> </JTable>
@ -240,7 +212,7 @@ const getActions = (
}, },
}, },
{ {
key: 'edit', key: 'update',
text: '编辑', text: '编辑',
tooltip: { tooltip: {
title: '编辑', title: '编辑',
@ -257,7 +229,7 @@ const getActions = (
title: data.state?.value !== 'disabled' ? '禁用' : '启用', title: data.state?.value !== 'disabled' ? '禁用' : '启用',
}, },
icon: icon:
data.state.value !== 'notActive' data.state.value !== 'disabled'
? 'StopOutlined' ? 'StopOutlined'
: 'CheckCircleOutlined', : 'CheckCircleOutlined',
popConfirm: { popConfirm: {

View File

@ -43,7 +43,11 @@
]" ]"
> >
<a-select <a-select
:disabled="type !== 'edit' && modelRef.id && modelRef.id !== ':id'" :disabled="
type !== 'edit' &&
modelRef.id &&
modelRef.id !== ':id'
"
placeholder="请选择产品" placeholder="请选择产品"
v-model:value="modelRef.id" v-model:value="modelRef.id"
show-search show-search
@ -410,12 +414,14 @@
</a-row> </a-row>
</a-form> </a-form>
<div v-if="type === 'edit'"> <div v-if="type === 'edit'">
<a-button <PermissionButton
:loading="loading"
type="primary" type="primary"
:loading="loading"
@click="saveBtn" @click="saveBtn"
>保存</a-button :hasPermission="['Northbound/DuerOS:add', 'Northbound/DuerOS:update']"
> >
保存
</PermissionButton>
</div> </div>
</a-col> </a-col>
<a-col :span="8"> <a-col :span="8">
@ -616,8 +622,8 @@ const getTypesActions = (val: string) => {
const saveBtn = async () => { const saveBtn = async () => {
const tasks: any[] = []; const tasks: any[] = [];
for (let i = 0; i < command.value.length; i++) { for (let i = 0; i < command.value.length; i++) {
const res = await (command.value[i] as any)?.saveBtn() const res = await (command.value[i] as any)?.saveBtn();
if(!res || (res?.errorFields && res.errorFields.length)) { if (!res || (res?.errorFields && res.errorFields.length)) {
actionActiveKey.value.push(String(i)); actionActiveKey.value.push(String(i));
tasks.push(false); tasks.push(false);
} else { } else {
@ -629,7 +635,7 @@ const saveBtn = async () => {
.then(async (data: any) => { .then(async (data: any) => {
if (tasks.every((item) => item) && data) { if (tasks.every((item) => item) && data) {
loading.value = true; loading.value = true;
const resp = await savePatch(toRaw(modelRef)); const resp = await savePatch(data);
loading.value = false; loading.value = false;
if (resp.status === 200) { if (resp.status === 200) {
message.success('操作成功!'); message.success('操作成功!');
@ -642,10 +648,16 @@ const saveBtn = async () => {
const _arr = err.errorFields.map((item: any) => item.name); const _arr = err.errorFields.map((item: any) => item.name);
_arr.map((item: string | any[]) => { _arr.map((item: string | any[]) => {
if (item.length >= 3) { if (item.length >= 3) {
if(item[0] === 'propertyMappings' && !propertyActiveKey.value.includes(item[1])){ if (
item[0] === 'propertyMappings' &&
!propertyActiveKey.value.includes(item[1])
) {
propertyActiveKey.value.push(item[1]); propertyActiveKey.value.push(item[1]);
} }
if(item[0] === 'actionMappings' && !actionActiveKey.value.includes(item[1])){ if (
item[0] === 'actionMappings' &&
!actionActiveKey.value.includes(item[1])
) {
actionActiveKey.value.push(item[1]); actionActiveKey.value.push(item[1]);
} }
} }

View File

@ -14,7 +14,14 @@
> >
<template #headerTitle> <template #headerTitle>
<a-space> <a-space>
<a-button type="primary" @click="handleAdd">新增</a-button> <PermissionButton
type="primary"
@click="handleAdd"
hasPermission="Northbound/DuerOS:add"
>
<template #icon><AIcon type="PlusOutlined" /></template>
新增
</PermissionButton>
</a-space> </a-space>
</template> </template>
<template #card="slotProps"> <template #card="slotProps">
@ -55,30 +62,14 @@
</a-row> </a-row>
</template> </template>
<template #actions="item"> <template #actions="item">
<a-tooltip <PermissionButton
v-bind="item.tooltip"
:title="item.disabled && item.tooltip.title"
>
<a-popconfirm
v-if="item.popConfirm"
v-bind="item.popConfirm"
:disabled="item.disabled"
>
<a-button :disabled="item.disabled">
<AIcon
type="DeleteOutlined"
v-if="item.key === 'delete'"
/>
<template v-else>
<AIcon :type="item.icon" />
<span>{{ item?.text }}</span>
</template>
</a-button>
</a-popconfirm>
<template v-else>
<a-button
:disabled="item.disabled" :disabled="item.disabled"
:popConfirm="item.popConfirm"
:tooltip="{
...item.tooltip,
}"
@click="item.onClick" @click="item.onClick"
:hasPermission="'Northbound/DuerOS:' + item.key"
> >
<AIcon <AIcon
type="DeleteOutlined" type="DeleteOutlined"
@ -88,9 +79,7 @@
<AIcon :type="item.icon" /> <AIcon :type="item.icon" />
<span>{{ item?.text }}</span> <span>{{ item?.text }}</span>
</template> </template>
</a-button> </PermissionButton>
</template>
</a-tooltip>
</template> </template>
</CardBox> </CardBox>
</template> </template>
@ -104,38 +93,25 @@
{{ slotProps.applianceType.text }} {{ slotProps.applianceType.text }}
</template> </template>
<template #action="slotProps"> <template #action="slotProps">
<a-space :size="16"> <a-space>
<a-tooltip <template
v-for="i in getActions(slotProps, 'table')" v-for="i in getActions(slotProps, 'table')"
:key="i.key" :key="i.key"
v-bind="i.tooltip"
> >
<a-popconfirm <PermissionButton
v-if="i.popConfirm"
v-bind="i.popConfirm"
:disabled="i.disabled" :disabled="i.disabled"
:popConfirm="i.popConfirm"
:tooltip="{
...i.tooltip,
}"
style="padding: 0px"
@click="i.onClick"
type="link"
:hasPermission="'Northbound/DuerOS:' + i.key"
> >
<a-button <template #icon><AIcon :type="i.icon" /></template>
:disabled="i.disabled" </PermissionButton>
style="padding: 0" </template>
type="link"
><AIcon :type="i.icon"
/></a-button>
</a-popconfirm>
<a-button
style="padding: 0"
type="link"
v-else
@click="i.onClick && i.onClick(slotProps)"
>
<a-button
:disabled="i.disabled"
style="padding: 0"
type="link"
><AIcon :type="i.icon"
/></a-button>
</a-button>
</a-tooltip>
</a-space> </a-space>
</template> </template>
</JTable> </JTable>
@ -143,17 +119,24 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { query, _undeploy, _deploy, _delete, queryProductList, queryTypes } from '@/api/northbound/dueros'; import {
query,
_undeploy,
_deploy,
_delete,
queryProductList,
queryTypes,
} from '@/api/northbound/dueros';
import type { ActionsType } from '@/components/Table/index.vue'; import type { ActionsType } from '@/components/Table/index.vue';
import { getImage } from '@/utils/comm'; import { getImage } from '@/utils/comm';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { useMenuStore } from 'store/menu' import { useMenuStore } from 'store/menu';
const router = useRouter(); const router = useRouter();
const instanceRef = ref<Record<string, any>>({}); const instanceRef = ref<Record<string, any>>({});
const params = ref<Record<string, any>>({}); const params = ref<Record<string, any>>({});
const current = ref<Record<string, any>>({}); const current = ref<Record<string, any>>({});
const menuStory = useMenuStore() const menuStory = useMenuStore();
const statusMap = new Map(); const statusMap = new Map();
statusMap.set('enabled', 'success'); statusMap.set('enabled', 'success');
@ -238,14 +221,14 @@ const columns = [
* 新增 * 新增
*/ */
const handleAdd = () => { const handleAdd = () => {
menuStory.jumpPage('Northbound/DuerOS/Detail', { id: ':id'}) menuStory.jumpPage('Northbound/DuerOS/Detail', { id: ':id' });
}; };
/** /**
* 查看 * 查看
*/ */
const handleView = (id: string) => { const handleView = (id: string) => {
menuStory.jumpPage('Northbound/DuerOS/Detail', { id }, { type: 'view' }) menuStory.jumpPage('Northbound/DuerOS/Detail', { id }, { type: 'view' });
}; };
const getActions = ( const getActions = (
@ -266,14 +249,18 @@ const getActions = (
}, },
}, },
{ {
key: 'edit', key: 'update',
text: '编辑', text: '编辑',
tooltip: { tooltip: {
title: '编辑', title: '编辑',
}, },
icon: 'EditOutlined', icon: 'EditOutlined',
onClick: () => { onClick: () => {
menuStory.jumpPage('Northbound/DuerOS/Detail', { id: data.id }, { type: 'edit' }) menuStory.jumpPage(
'Northbound/DuerOS/Detail',
{ id: data.id },
{ type: 'edit' },
);
}, },
}, },
{ {
@ -283,7 +270,7 @@ const getActions = (
title: data.state?.value !== 'disabled' ? '禁用' : '启用', title: data.state?.value !== 'disabled' ? '禁用' : '启用',
}, },
icon: icon:
data.state.value !== 'notActive' data.state.value !== 'disabled'
? 'StopOutlined' ? 'StopOutlined'
: 'CheckCircleOutlined', : 'CheckCircleOutlined',
popConfirm: { popConfirm: {

View File

@ -0,0 +1,117 @@
<template>
<a-select allowClear v-model:value="_value" @change="onChange" placeholder="请选择" style="width: 100%">
<a-select-option
v-for="item in list"
:key="item.id"
:value="item.id"
:label="item.name"
:filter-option="filterOption"
>{{ item.name }}</a-select-option
>
</a-select>
</template>
<script lang="ts" setup>
import {
edgeCollector,
edgePoint,
} from '@/api/device/instance';
const _props = defineProps({
modelValue: {
type: String,
default: undefined,
},
type: {
type: String,
default: 'POINT',
},
id: {
type: String,
default: '',
},
edgeId: {
type: String,
default: '',
}
});
const filterOption = (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
type Emits = {
(e: 'update:modelValue', data: string | undefined): void;
};
const emit = defineEmits<Emits>();
const list = ref<any[]>([]);
const _value = ref<string | undefined>(undefined);
watchEffect(() => {
_value.value = _props.modelValue;
});
const onChange = (_val: string) => {
emit('update:modelValue', _val);
};
const getCollector = async (_val: string) => {
if (!_val) {
return [];
} else {
const resp = await edgeCollector(_props.edgeId, {
terms: [
{
terms: [
{
column: 'channelId',
value: _val,
},
],
},
],
});
if (resp.status === 200) {
list.value = (resp.result as any[])?.[0] || []
}
}
};
const getPoint = async (_val: string) => {
if (!_val) {
return [];
} else {
const resp = await edgePoint(_props.edgeId, {
terms: [
{
terms: [
{
column: 'collectorId',
value: _val,
},
],
},
],
});
if (resp.status === 200) {
list.value = (resp.result as any[])?.[0] || []
}
}
};
watchEffect(() => {
if (_props.id) {
if (_props.type === 'POINT') {
getPoint(_props.id);
} else {
getCollector(_props.id);
}
} else {
list.value = [];
}
});
</script>
<style lang="less" scoped>
</style>

View File

@ -0,0 +1,189 @@
<template>
<a-modal
width="900px"
title="批量映射"
visible
@ok="handleClick"
@cancel="handleClose"
>
<div class="map-tree">
<div class="map-tree-top">
采集器的点位名称与属性名称一致时将自动映射绑定有多个采集器点位名称与属性名称一致时以第1个采集器的点位数据进行绑定
</div>
<a-spin :spinning="loading">
<div class="map-tree-content">
<a-card class="map-tree-content-card" title="源数据">
<a-tree
checkable
:height="300"
:tree-data="dataSource"
:checkedKeys="checkedKeys"
@check="onCheck"
/>
</a-card>
<div style="width: 100px">
<a-button
:disabled="rightList.length >= leftList.length"
@click="onRight"
>加入右侧</a-button
>
</div>
<a-card class="map-tree-content-card" title="采集器">
<a-list
size="small"
:data-source="rightList"
class="map-tree-content-card-list"
>
<template #renderItem="{ item }">
<a-list-item>
{{ item.title }}
<template #actions>
<a-popconfirm
title="确定删除?"
@confirm="_delete(item.key)"
>
<AIcon type="DeleteOutlined" />
</a-popconfirm>
</template>
</a-list-item>
</template>
</a-list>
</a-card>
</div>
</a-spin>
</div>
</a-modal>
</template>
<script lang="ts" setup>
import { treeEdgeMap, saveEdgeMap } from '@/api/device/instance';
import { message } from 'ant-design-vue/es';
const _props = defineProps({
metaData: {
type: Array,
default: () => []
},
deviceId: {
type: String,
default: '',
},
edgeId: {
type: String,
default: '',
}
});
const _emits = defineEmits(['close', 'save']);
const checkedKeys = ref<string[]>([]);
const leftList = ref<any[]>([]);
const rightList = ref<any[]>([]);
const dataSource = ref<any[]>([]);
const loading = ref<boolean>(false);
const handleData = (data: any[], type: string) => {
data.forEach((item) => {
item.key = item.id;
item.title = item.name;
item.checkable = type === 'collectors';
if (
item.collectors &&
Array.isArray(item.collectors) &&
item.collectors.length
) {
item.children = handleData(item.collectors, 'collectors');
}
if (item.points && Array.isArray(item.points) && item.points.length) {
item.children = handleData(item.points, 'points');
}
});
return data as any[];
};
const handleSearch = async () => {
loading.value = true;
const resp = await treeEdgeMap(_props.edgeId);
loading.value = false;
if (resp.status === 200) {
dataSource.value = handleData((resp.result as any[])?.[0], 'channel');
}
};
const onCheck = (keys: string[], e: any) => {
checkedKeys.value = [...keys];
leftList.value = e?.checkedNodes || [];
};
const onRight = () => {
rightList.value = leftList.value;
};
const _delete = (_key: string) => {
const _index = rightList.value.findIndex((i) => i.key === _key);
rightList.value.splice(_index, 1);
checkedKeys.value = rightList.value.map((i) => i.key);
leftList.value = rightList.value;
};
const handleClick = async () => {
if (!rightList.value.length) {
message.warning('请选择采集器');
} else {
const params: any[] = [];
rightList.value.map((item: any) => {
const array = (item.children || []).map((element: any) => ({
channelId: item.parentId,
collectorId: element.collectorId,
pointId: element.id,
metadataType: 'property',
metadataId: (_props.metaData as any[]).find((i: any) => i.name === element.name)
?.metadataId,
provider: dataSource.value.find((it: any) => it.id === item.parentId).provider,
}));
params.push(...array);
});
const filterParms = params.filter((item) => !!item.metadataId);
if (filterParms && filterParms.length !== 0) {
const res = await saveEdgeMap(_props.edgeId, {
deviceId: _props.deviceId,
provider: filterParms[0]?.provider,
requestList: filterParms,
});
if (res.status === 200) {
message.success('操作成功');
_emits('save');
}
} else {
message.error('暂无对应属性的映射');
}
}
};
const handleClose = () => {
_emits('close');
};
onMounted(() => {
if (_props.edgeId) {
handleSearch();
}
});
</script>
<style lang="less" scoped>
.map-tree-content {
margin-top: 20px;
display: flex;
justify-content: space-between;
align-items: center;
.map-tree-content-card {
width: 350px;
height: 400px;
.map-tree-content-card-list {
overflow-y: auto;
height: 300px;
}
}
}
</style>

View File

@ -1,5 +1,5 @@
<template> <template>
<a-spin :spinning="loading"> <a-spin :spinning="loading" v-if="metadata.properties.length">
<a-card> <a-card>
<template #extra> <template #extra>
<a-space> <a-space>
@ -53,6 +53,7 @@
v-model="record[column.dataIndex]" v-model="record[column.dataIndex]"
:id="record.channelId" :id="record.channelId"
type="COLLECTOR" type="COLLECTOR"
:edgeId="instanceStore.current.parentId"
/> />
</a-form-item> </a-form-item>
</template> </template>
@ -70,6 +71,7 @@
v-model="record[column.dataIndex]" v-model="record[column.dataIndex]"
:id="record.collectorId" :id="record.collectorId"
type="POINT" type="POINT"
:edgeId="instanceStore.current.parentId"
/> />
</a-form-item> </a-form-item>
</template> </template>
@ -85,6 +87,7 @@
<a-tooltip title="解绑"> <a-tooltip title="解绑">
<a-popconfirm <a-popconfirm
title="确认解绑" title="确认解绑"
:disabled="!record.id"
@confirm="unbind(record.id)" @confirm="unbind(record.id)"
> >
<a-button type="link" :disabled="!record.id" <a-button type="link" :disabled="!record.id"
@ -97,27 +100,30 @@
</a-table> </a-table>
</a-form> </a-form>
</a-card> </a-card>
<!-- <PatchMapping <PatchMapping
:deviceId="instanceStore.current.id" :deviceId="instanceStore.current.id"
v-if="visible" v-if="visible"
@close="visible = false" @close="visible = false"
@save="onPatchBind" @save="onPatchBind"
:type="provider"
:metaData="modelRef.dataSource" :metaData="modelRef.dataSource"
/> --> :edgeId="instanceStore.current.parentId"
/>
</a-spin> </a-spin>
<a-card v-else>
<JEmpty description='暂无数据,请配置物模型' style="margin: 10% 0" />
</a-card>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useInstanceStore } from '@/store/instance'; import { useInstanceStore } from '@/store/instance';
import { import {
queryMapping, getEdgeMap,
saveMapping, saveEdgeMap,
removeMapping, removeEdgeMap,
queryChannelNoPaging, edgeChannel,
} from '@/api/device/instance'; } from '@/api/device/instance';
import MSelect from '../components/MSelect.vue'; import MSelect from './MSelect.vue';
// import PatchMapping from '../components/PatchMapping.vue'; import PatchMapping from './PatchMapping.vue';
import { message } from 'ant-design-vue/es'; import { message } from 'ant-design-vue/es';
const columns = [ const columns = [
@ -162,13 +168,6 @@ const filterOption = (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0; return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
}; };
const props = defineProps({
provider: {
type: String,
default: 'MODBUS_TCP',
},
});
const instanceStore = useInstanceStore(); const instanceStore = useInstanceStore();
const metadata = JSON.parse(instanceStore.current?.metadata || '{}'); const metadata = JSON.parse(instanceStore.current?.metadata || '{}');
const loading = ref<boolean>(false); const loading = ref<boolean>(false);
@ -182,26 +181,16 @@ const formRef = ref();
const visible = ref<boolean>(false); const visible = ref<boolean>(false);
const getChannel = async () => { const getChannel = async () => {
const resp: any = await queryChannelNoPaging({ if (instanceStore.current?.parentId) {
paging: false, const resp: any = await edgeChannel(instanceStore.current.parentId);
terms: [
{
terms: [
{
column: 'provider',
value: props.provider,
},
],
},
],
});
if (resp.status === 200) { if (resp.status === 200) {
channelList.value = resp.result?.map((item: any) => ({ channelList.value = resp.result?.[0]?.map((item: any) => ({
label: item.name, label: item.name,
value: item.id, value: item.id,
provider: item.provider, provider: item.provider,
})); }));
} }
}
}; };
const handleSearch = async () => { const handleSearch = async () => {
@ -214,12 +203,15 @@ const handleSearch = async () => {
name: item.name, name: item.name,
})); }));
if (_metadata && _metadata.length) { if (_metadata && _metadata.length) {
const resp: any = await queryMapping( const resp: any = await getEdgeMap(instanceStore.current?.parentId || '', {
'device', deviceId: instanceStore.current.id,
instanceStore.current.id, query: {},
); }).catch(() => {
modelRef.dataSource = _metadata;
loading.value = false;
})
if (resp.status === 200) { if (resp.status === 200) {
const array = resp.result.reduce((x: any, y: any) => { const array = resp.result?.[0].reduce((x: any, y: any) => {
const metadataId = _metadata.find( const metadataId = _metadata.find(
(item: any) => item.metadataId === y.metadataId, (item: any) => item.metadataId === y.metadataId,
); );
@ -238,9 +230,10 @@ const handleSearch = async () => {
const unbind = async (id: string) => { const unbind = async (id: string) => {
if (id) { if (id) {
const resp = await removeMapping('device', instanceStore.current.id, [ const resp = await removeEdgeMap(instanceStore.current?.parentId || '', {
id, deviceId: instanceStore.current.id,
]); idList: [id],
});
if (resp.status === 200) { if (resp.status === 200) {
message.success('操作成功!'); message.success('操作成功!');
handleSearch(); handleSearch();
@ -265,11 +258,12 @@ const onSave = () => {
(i: any) => i.channelId, (i: any) => i.channelId,
); );
if (arr && arr.length !== 0) { if (arr && arr.length !== 0) {
const resp = await saveMapping( const submitData = {
instanceStore.current.id, deviceId: instanceStore.current.id,
props.provider, provider: (arr[0] as any)?.provider,
arr, requestList: arr,
); };
const resp = await saveEdgeMap(instanceStore.current.parentId || '', submitData);
if (resp.status === 200) { if (resp.status === 200) {
message.success('操作成功!'); message.success('操作成功!');
handleSearch(); handleSearch();

View File

@ -14,16 +14,21 @@
> >
<a-row :gutter="30"> <a-row :gutter="30">
<a-col :span="15"> <a-col :span="15">
<a-form :ref="`${func.id}Ref`" :model="func">
<a-table <a-table
:columns="columns" :columns="columns"
:data-source="func.table" :data-source="func.table"
:pagination="false" :pagination="false"
rowKey="id" rowKey="id"
> >
<template #bodyCell="{ column, text, record }"> <template #bodyCell="{ column, record, index }">
<template v-if="column.dataIndex === 'type'"> <template
v-if="column.dataIndex === 'type'"
>
<span>{{ record.type }}</span> <span>{{ record.type }}</span>
<a-tooltip v-if="record.type === 'object'"> <a-tooltip
v-if="record.type === 'object'"
>
<template slot="title"> <template slot="title">
请按照json格式输入 请按照json格式输入
</template> </template>
@ -37,10 +42,22 @@
/> />
</a-tooltip> </a-tooltip>
</template> </template>
<template v-if="column.dataIndex === 'value'"> <template
v-if="column.dataIndex === 'value'"
>
<a-form-item
:name="['table', index, 'value']"
:rules="{
required: true,
message: '',
}"
has-feedback
>
<ValueItem <ValueItem
:ref="`valueItemRef${record.id}`" :ref="`valueItemRef${record.id}`"
v-model:modelValue="record.value" v-model:modelValue="
record.value
"
:itemType="record.type" :itemType="record.type"
:options=" :options="
record.type === 'enum' record.type === 'enum'
@ -65,9 +82,11 @@
: undefined : undefined
" "
/> />
</a-form-item>
</template> </template>
</template> </template>
</a-table> </a-table>
</a-form>
<div class="editor-btn"> <div class="editor-btn">
<a-space> <a-space>
<a-button <a-button
@ -180,6 +199,9 @@ const newFunctions = computed(() => {
* 执行 * 执行
*/ */
const handleExecute = async (func: any) => { const handleExecute = async (func: any) => {
proxy?.$refs[`${func.id}Ref`][0]
.validate()
.then(async () => {
const obj = {}; const obj = {};
func.table.forEach((item: any) => { func.table.forEach((item: any) => {
if (item.type === 'object') { if (item.type === 'object') {
@ -197,19 +219,26 @@ const handleExecute = async (func: any) => {
message.success('操作成功'); message.success('操作成功');
func.executeResult = result instanceof Array ? result[0] : result; func.executeResult = result instanceof Array ? result[0] : result;
proxy?.$forceUpdate(); proxy?.$forceUpdate();
})
.catch((err: any) => {
console.log('err: ', err);
});
}; };
/** /**
* 清空 * 清空
*/ */
const handleClear = (func: any) => { const handleClear = (func: any) => {
func.table.forEach((item: any) => { proxy?.$refs[`${func.id}Ref`][0].resetFields();
item.value = undefined;
proxy.$refs[`valueItemRef${item.id}`][0].myValue = undefined;
});
}; };
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
:deep(.ant-table-cell .ant-form-item) {
margin-bottom: 0;
}
:deep(.ant-form-item-with-help .ant-form-item-explain) {
min-height: 0;
}
.wrapper { .wrapper {
.tips { .tips {
margin-bottom: 10px; margin-bottom: 10px;

View File

@ -1,55 +1,118 @@
<template> <template>
<div style="margin-top: 20px" v-if="config.length"> <div style="margin-top: 20px" v-if="config.length">
<div style="display: flex; margin-bottom: 20px; align-items: center;"> <div style="display: flex; margin-bottom: 20px; align-items: center">
<div style="font-size: 16px; font-weight: 700">配置</div> <div style="font-size: 16px; font-weight: 700">配置</div>
<a-space> <a-space>
<a-button type="link" @click="visible = true"><AIcon type="EditOutlined" />编辑</a-button> <PermissionButton
<a-popconfirm title="确认重新应用该配置?" @confirm="deployBtn"> type="link"
<a-button type="link" v-if="instanceStore.detail.current?.value !== 'notActive'"><AIcon type="CheckOutlined" />应用配置<a-tooltip title="修改配置后需重新应用后才能生效。"><AIcon type="QuestionCircleOutlined" /></a-tooltip></a-button> @click="visible = true"
</a-popconfirm> hasPermission="device/Instance:update"
<a-popconfirm title="确认恢复默认配置?" @confirm="resetBtn"> >
<a-button type="link" v-if="instanceStore.detail.aloneConfiguration"><AIcon type="SyncOutlined" />恢复默认<a-tooltip title="该设备单独编辑过配置信息,点击此将恢复成默认的配置信息,请谨慎操作。"><AIcon type="QuestionCircleOutlined" /></a-tooltip></a-button> <template #icon><AIcon type="EditOutlined" /></template>
</a-popconfirm> 编辑
</PermissionButton>
<PermissionButton
type="link"
v-if="instanceStore.detail.current?.value !== 'notActive'"
:popConfirm="{
title: '确认重新应用该配置?',
onConfirm: deployBtn,
}"
hasPermission="device/Instance:update"
>
<AIcon type="CheckOutlined" />应用配置<a-tooltip
title="修改配置后需重新应用后才能生效。"
><AIcon type="QuestionCircleOutlined"
/></a-tooltip>
</PermissionButton>
<PermissionButton
type="link"
v-if="instanceStore.detail.aloneConfiguration"
:popConfirm="{
title: '确认恢复默认配置?',
onConfirm: resetBtn,
}"
hasPermission="device/Instance:update"
>
<AIcon type="SyncOutlined" />恢复默认<a-tooltip
title="该设备单独编辑过配置信息,点击此将恢复成默认的配置信息,请谨慎操作。"
><AIcon type="QuestionCircleOutlined"
/></a-tooltip>
</PermissionButton>
</a-space> </a-space>
</div> </div>
<a-descriptions bordered size="small" v-for="i in config" :key="i.name"> <a-descriptions bordered size="small" v-for="i in config" :key="i.name">
<template #title><h4 style="font-size: 15px">{{i.name}}</h4></template> <template #title
<a-descriptions-item v-for="item in i.properties" :key="item.property"> ><h4 style="font-size: 15px">{{ i.name }}</h4></template
>
<a-descriptions-item
v-for="item in i.properties"
:key="item.property"
>
<template #label> <template #label>
<span style="margin-right: 5px">{{item.name}}</span> <span style="margin-right: 5px">{{ item.name }}</span>
<a-tooltip v-if="item.description" :title="item.description"><AIcon type="QuestionCircleOutlined" /></a-tooltip> <a-tooltip v-if="item.description" :title="item.description"
><AIcon type="QuestionCircleOutlined"
/></a-tooltip>
</template> </template>
<span v-if="item.type.type === 'password' && instanceStore.current?.configuration?.[item.property]?.length > 0">******</span> <span
v-if="
item.type.type === 'password' &&
instanceStore.current?.configuration?.[item.property]
?.length > 0
"
>******</span
>
<span v-else> <span v-else>
<span>{{ instanceStore.current?.configuration?.[item.property] || '' }}</span> <span>{{
<a-tooltip v-if="isExit(item.property)" :title="`有效值:${instanceStore.current?.configuration?.[item.property]}`"><AIcon type="QuestionCircleOutlined" /></a-tooltip> instanceStore.current?.configuration?.[item.property] ||
''
}}</span>
<a-tooltip
v-if="isExit(item.property)"
:title="`有效值:${
instanceStore.current?.configuration?.[
item.property
]
}`"
><AIcon type="QuestionCircleOutlined"
/></a-tooltip>
</span> </span>
</a-descriptions-item> </a-descriptions-item>
</a-descriptions> </a-descriptions>
<Save v-if="visible" @save="saveBtn" @close="visible = false" :config="config" /> <Save
v-if="visible"
@save="saveBtn"
@close="visible = false"
:config="config"
/>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useInstanceStore } from "@/store/instance" import { useInstanceStore } from '@/store/instance';
import { ConfigMetadata } from "@/views/device/Product/typings" import { ConfigMetadata } from '@/views/device/Product/typings';
import { getConfigMetadata, _deploy, configurationReset } from '@/api/device/instance' import {
import { message } from "ant-design-vue" getConfigMetadata,
import Save from './Save.vue' _deploy,
configurationReset,
} from '@/api/device/instance';
import { message } from 'ant-design-vue';
import Save from './Save.vue';
const instanceStore = useInstanceStore() const instanceStore = useInstanceStore();
const visible = ref<boolean>(false) const visible = ref<boolean>(false);
const config = ref<ConfigMetadata[]>([]) const config = ref<ConfigMetadata[]>([]);
watchEffect(() => { watchEffect(() => {
if(instanceStore.current.id){ if (instanceStore.current.id) {
getConfigMetadata(instanceStore.current.id).then(resp => { getConfigMetadata(instanceStore.current.id).then((resp) => {
if(resp.status === 200){ if (resp.status === 200) {
config.value = resp?.result as ConfigMetadata[] config.value = resp?.result as ConfigMetadata[];
} }
}) });
} }
}) });
const isExit = (property: string) => { const isExit = (property: string) => {
return ( return (
@ -59,32 +122,32 @@ const isExit = (property: string) => {
instanceStore.current?.configuration[property] !== instanceStore.current?.configuration[property] !==
instanceStore.current?.cachedConfiguration[property] instanceStore.current?.cachedConfiguration[property]
); );
} };
const deployBtn = async () => { const deployBtn = async () => {
if(instanceStore.current.id){ if (instanceStore.current.id) {
const resp = await _deploy(instanceStore.current.id) const resp = await _deploy(instanceStore.current.id);
if (resp.status === 200) { if (resp.status === 200) {
message.success('操作成功') message.success('操作成功');
instanceStore.refresh(instanceStore.current.id) instanceStore.refresh(instanceStore.current.id);
} }
} }
} };
const resetBtn = async () => { const resetBtn = async () => {
if(instanceStore.current.id){ if (instanceStore.current.id) {
const resp = await configurationReset(instanceStore.current.id) const resp = await configurationReset(instanceStore.current.id);
if (resp.status === 200) { if (resp.status === 200) {
message.success('恢复默认配置成功') message.success('恢复默认配置成功');
instanceStore.refresh(instanceStore.current.id) instanceStore.refresh(instanceStore.current.id);
} }
} }
} };
const saveBtn = () => { const saveBtn = () => {
visible.value = false visible.value = false;
if(instanceStore.current.id){ if (instanceStore.current.id) {
instanceStore.refresh(instanceStore.current.id) instanceStore.refresh(instanceStore.current.id);
} }
} };
</script> </script>

View File

@ -3,31 +3,50 @@
<a-descriptions bordered> <a-descriptions bordered>
<template #title> <template #title>
关系信息 关系信息
<a-button type="link" @click="visible = true"><AIcon type="EditOutlined" />编辑<a-tooltip title="管理设备与其他业务的关联关系,关系来源于关系配置"><AIcon type="QuestionCircleOutlined" /></a-tooltip></a-button> <PermissionButton
type="link"
@click="visible = true"
hasPermission="device/Instance:update"
>
<AIcon type="EditOutlined" />编辑<a-tooltip
title="管理设备与其他业务的关联关系,关系来源于关系配置"
><AIcon type="QuestionCircleOutlined"
/></a-tooltip>
</PermissionButton>
</template> </template>
<a-descriptions-item :span="1" v-for="item in dataSource" :key="item.objectId" :label="item.relationName">{{ item?.related ? (item?.related || []).map(i => i.name).join(',') : '' }}</a-descriptions-item> <a-descriptions-item
:span="1"
v-for="item in dataSource"
:key="item.objectId"
:label="item.relationName"
>{{
item?.related
? (item?.related || []).map((i) => i.name).join(',')
: ''
}}</a-descriptions-item
>
</a-descriptions> </a-descriptions>
<Save v-if="visible" @save="saveBtn" @close="visible = false" /> <Save v-if="visible" @save="saveBtn" @close="visible = false" />
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useInstanceStore } from "@/store/instance" import { useInstanceStore } from '@/store/instance';
import Save from './Save.vue' import Save from './Save.vue';
const instanceStore = useInstanceStore() const instanceStore = useInstanceStore();
const dataSource = ref<Record<any, any>[]>([]) const dataSource = ref<Record<any, any>[]>([]);
const visible = ref<boolean>(false); const visible = ref<boolean>(false);
watchEffect(() => { watchEffect(() => {
const arr = (instanceStore.current?.relations || []).reverse() const arr = (instanceStore.current?.relations || []).reverse();
dataSource.value = arr as Record<any, any>[] dataSource.value = arr as Record<any, any>[];
}) });
const saveBtn = () => { const saveBtn = () => {
visible.value = false visible.value = false;
if(instanceStore.current.id){ if (instanceStore.current.id) {
instanceStore.refresh(instanceStore.current.id) instanceStore.refresh(instanceStore.current.id);
} }
} };
</script> </script>

View File

@ -3,32 +3,44 @@
<a-descriptions bordered> <a-descriptions bordered>
<template #title> <template #title>
标签 标签
<a-button type="link" @click="visible = true"><AIcon type="EditOutlined" />编辑</a-button> <PermissionButton
type="link"
@click="visible = true"
hasPermission="device/Instance:update"
>
<AIcon type="EditOutlined" />编辑
</PermissionButton>
</template> </template>
<a-descriptions-item :span="1" v-for="item in dataSource" :key="item.key" :label="`${item.name}${item.key})`">{{ item?.value }}</a-descriptions-item> <a-descriptions-item
:span="1"
v-for="item in dataSource"
:key="item.key"
:label="`${item.name}${item.key})`"
>{{ item?.value }}</a-descriptions-item
>
</a-descriptions> </a-descriptions>
<Save v-if="visible" @close="visible = false" @save="saveBtn" /> <Save v-if="visible" @close="visible = false" @save="saveBtn" />
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useInstanceStore } from "@/store/instance" import { useInstanceStore } from '@/store/instance';
import Save from './Save.vue' import Save from './Save.vue';
const instanceStore = useInstanceStore() const instanceStore = useInstanceStore();
const dataSource = ref<Record<any, any>[]>([]) const dataSource = ref<Record<any, any>[]>([]);
const visible = ref<boolean>(false) const visible = ref<boolean>(false);
watchEffect(() => { watchEffect(() => {
const arr = (instanceStore.current?.tags || []) const arr = instanceStore.current?.tags || [];
dataSource.value = arr as Record<any, any>[] dataSource.value = arr as Record<any, any>[];
}) });
const saveBtn = () => { const saveBtn = () => {
visible.value = false visible.value = false;
if(instanceStore.current.id){ if (instanceStore.current.id) {
instanceStore.refresh(instanceStore.current.id) instanceStore.refresh(instanceStore.current.id);
} }
} };
</script> </script>

View File

@ -3,43 +3,105 @@
<a-descriptions bordered> <a-descriptions bordered>
<template #title> <template #title>
设备信息 设备信息
<a-button type="link" @click="visible = true"><AIcon type="EditOutlined" />编辑</a-button> <PermissionButton
type="link"
@click="visible = true"
hasPermission="device/Instance:update"
>
<template #icon><AIcon type="EditOutlined" /></template>
编辑
</PermissionButton>
</template> </template>
<a-descriptions-item label="设备ID">{{ instanceStore.current.id }}</a-descriptions-item> <a-descriptions-item label="设备ID">{{
<a-descriptions-item label="产品名称">{{ instanceStore.current.productName }}</a-descriptions-item> instanceStore.current.id
<a-descriptions-item label="产品分类">{{ instanceStore.current.classifiedName }}</a-descriptions-item> }}</a-descriptions-item>
<a-descriptions-item label="设备类型">{{ instanceStore.current.deviceType?.text }}</a-descriptions-item> <a-descriptions-item label="产品名称">{{
<a-descriptions-item label="固件版本">{{ instanceStore.current.firmwareInfo?.version }}</a-descriptions-item> instanceStore.current.productName
<a-descriptions-item label="连接协议">{{ instanceStore.current.protocolName }}</a-descriptions-item> }}</a-descriptions-item>
<a-descriptions-item label="消息协议">{{ instanceStore.current.transport }}</a-descriptions-item> <a-descriptions-item label="产品分类">{{
<a-descriptions-item label="创建时间">{{ instanceStore.current.createTime ? moment(instanceStore.current.createTime).format('YYYY-MM-DD HH:mm:ss') : '' }}</a-descriptions-item> instanceStore.current.classifiedName
<a-descriptions-item label="注册时间">{{ instanceStore.current.registerTime ? moment(instanceStore.current.registerTime).format('YYYY-MM-DD HH:mm:ss') : ''}}</a-descriptions-item> }}</a-descriptions-item>
<a-descriptions-item label="最后上线时间">{{ instanceStore.current.onlineTime ? moment(instanceStore.current.onlineTime).format('YYYY-MM-DD HH:mm:ss') : '' }}</a-descriptions-item> <a-descriptions-item label="设备类型">{{
<a-descriptions-item label="父设备" v-if="instanceStore.current.deviceType?.value === 'childrenDevice'">{{ instanceStore.current.parentId }}</a-descriptions-item> instanceStore.current.deviceType?.text
<a-descriptions-item label="说明">{{ instanceStore.current.description }}</a-descriptions-item> }}</a-descriptions-item>
<a-descriptions-item label="固件版本">{{
instanceStore.current.firmwareInfo?.version
}}</a-descriptions-item>
<a-descriptions-item label="连接协议">{{
instanceStore.current.protocolName
}}</a-descriptions-item>
<a-descriptions-item label="消息协议">{{
instanceStore.current.transport
}}</a-descriptions-item>
<a-descriptions-item label="创建时间">{{
instanceStore.current.createTime
? moment(instanceStore.current.createTime).format(
'YYYY-MM-DD HH:mm:ss',
)
: ''
}}</a-descriptions-item>
<a-descriptions-item label="注册时间">{{
instanceStore.current.registerTime
? moment(instanceStore.current.registerTime).format(
'YYYY-MM-DD HH:mm:ss',
)
: ''
}}</a-descriptions-item>
<a-descriptions-item label="最后上线时间">{{
instanceStore.current.onlineTime
? moment(instanceStore.current.onlineTime).format(
'YYYY-MM-DD HH:mm:ss',
)
: ''
}}</a-descriptions-item>
<a-descriptions-item
label="父设备"
v-if="
instanceStore.current.deviceType?.value === 'childrenDevice'
"
>{{ instanceStore.current.parentId }}</a-descriptions-item
>
<a-descriptions-item label="说明">{{
instanceStore.current.description
}}</a-descriptions-item>
</a-descriptions> </a-descriptions>
<Config /> <Config />
<Tags v-if="instanceStore.current?.tags && instanceStore.current?.tags.length > 0 " /> <Tags
<Relation v-if="instanceStore.current?.relations && instanceStore.current?.relations.length > 0" /> v-if="
<Save v-if="visible" :data="instanceStore.current" @close="visible = false" @save="saveBtn" /> instanceStore.current?.tags &&
instanceStore.current?.tags.length > 0
"
/>
<Relation
v-if="
instanceStore.current?.relations &&
instanceStore.current?.relations.length > 0
"
/>
<Save
v-if="visible"
:data="instanceStore.current"
@close="visible = false"
@save="saveBtn"
/>
</a-card> </a-card>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useInstanceStore } from '@/store/instance' import { useInstanceStore } from '@/store/instance';
import Save from '../../Save/index.vue' import Save from '../../Save/index.vue';
import Config from './components/Config/index.vue' import Config from './components/Config/index.vue';
import Tags from './components/Tags/index.vue' import Tags from './components/Tags/index.vue';
import Relation from './components/Relation/index.vue' import Relation from './components/Relation/index.vue';
import moment from 'moment' import moment from 'moment';
const visible = ref<boolean>(false) const visible = ref<boolean>(false);
const instanceStore = useInstanceStore() const instanceStore = useInstanceStore();
const saveBtn = () => { const saveBtn = () => {
if(instanceStore.current?.id){ if (instanceStore.current?.id) {
instanceStore.refresh(instanceStore.current?.id) instanceStore.refresh(instanceStore.current?.id);
} }
visible.value = false visible.value = false;
} };
</script> </script>

View File

@ -1,5 +1,5 @@
<template> <template>
<a-spin :spinning="loading"> <a-spin :spinning="loading" v-if="metadata.properties.length">
<a-card> <a-card>
<template #extra> <template #extra>
<a-space> <a-space>
@ -85,6 +85,7 @@
<a-tooltip title="解绑"> <a-tooltip title="解绑">
<a-popconfirm <a-popconfirm
title="确认解绑" title="确认解绑"
:disabled="!record.id"
@confirm="unbind(record.id)" @confirm="unbind(record.id)"
> >
<a-button type="link" :disabled="!record.id" <a-button type="link" :disabled="!record.id"
@ -106,6 +107,9 @@
:metaData="modelRef.dataSource" :metaData="modelRef.dataSource"
/> />
</a-spin> </a-spin>
<a-card v-else>
<JEmpty description='暂无数据,请配置物模型' style="margin: 10% 0" />
</a-card>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View File

@ -8,7 +8,8 @@
<template #title> <template #title>
<div> <div>
<div style="display: flex; align-items: center"> <div style="display: flex; align-items: center">
<div>{{ instanceStore.current.name }}</div> <AIcon type="ArrowLeftOutlined" @click="onBack" />
<div style="margin-left: 20px">{{ instanceStore.current.name }}</div>
<a-divider type="vertical" /> <a-divider type="vertical" />
<a-space> <a-space>
<a-badge <a-badge
@ -19,25 +20,35 @@
) )
" "
/> />
<a-popconfirm <PermissionButton
title="确认启用设备"
@confirm="handleAction"
v-if=" v-if="
instanceStore.current.state?.value === instanceStore.current.state?.value ===
'notActive' 'notActive'
" "
type="link"
style="margin-top: -5px; padding: 0 20px"
:popConfirm="{
title: '确认启用设备',
onConfirm: handleAction,
}"
hasPermission="device/Instance:action"
> >
<a-button type="link">启用设备</a-button> 启用设备
</a-popconfirm> </PermissionButton>
<a-popconfirm <PermissionButton
title="确认断开连接"
@confirm="handleDisconnect"
v-if=" v-if="
instanceStore.current.state?.value === 'online' instanceStore.current.state?.value === 'online'
" "
type="link"
style="margin-top: -5px; padding: 0 20px"
:popConfirm="{
title: '确认断开连接?',
onConfirm: handleDisconnect,
}"
hasPermission="device/Instance:action"
> >
<a-button type="link">断开连接</a-button> 断开连接
</a-popconfirm> </PermissionButton>
<a-tooltip <a-tooltip
v-if=" v-if="
instanceStore.current?.accessProvider === instanceStore.current?.accessProvider ===
@ -66,14 +77,14 @@
instanceStore.current.id instanceStore.current.id
}}</a-descriptions-item> }}</a-descriptions-item>
<a-descriptions-item label="所属产品"> <a-descriptions-item label="所属产品">
<a-button <PermissionButton
style="margin-top: -5px; padding: 0"
type="link" type="link"
style="margin-top: -5px; padding: 0"
@click="jumpProduct" @click="jumpProduct"
>{{ hasPermission="device/Product:view"
instanceStore.current.productName
}}</a-button
> >
{{ instanceStore.current.productName }}
</PermissionButton>
</a-descriptions-item> </a-descriptions-item>
</a-descriptions> </a-descriptions>
</div> </div>
@ -109,6 +120,9 @@ import { _deploy, _disconnect } from '@/api/device/instance';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { getImage } from '@/utils/comm'; import { getImage } from '@/utils/comm';
import { getWebSocket } from '@/utils/websocket'; import { getWebSocket } from '@/utils/websocket';
import { useMenuStore } from '@/store/menu';
const menuStory = useMenuStore();
const route = useRoute(); const route = useRoute();
const instanceStore = useInstanceStore(); const instanceStore = useInstanceStore();
@ -180,7 +194,9 @@ watch(
{ immediate: true, deep: true }, { immediate: true, deep: true },
); );
const onBack = () => {}; const onBack = () => {
menuStory.jumpPage('device/Instance');
};
const onTabChange = (e: string) => { const onTabChange = (e: string) => {
instanceStore.tabActiveKey = e; instanceStore.tabActiveKey = e;
@ -214,12 +230,18 @@ const handleRefresh = async () => {
}; };
const jumpProduct = () => { const jumpProduct = () => {
message.warn('暂未开发'); menuStory.jumpPage('device/Product/Detail', {
id: instanceStore.current.productId,
});
}; };
watchEffect(() => { watchEffect(() => {
const keys = list.value.map((i) => i.key); const keys = list.value.map((i) => i.key);
if (instanceStore.current.protocol && !(['modbus-tcp', 'opc-ua'].includes(instanceStore.current.protocol)) && !keys.includes('Diagnose')) { if (
instanceStore.current.protocol &&
!['modbus-tcp', 'opc-ua'].includes(instanceStore.current.protocol) &&
!keys.includes('Diagnose')
) {
list.value.push({ list.value.push({
key: 'Diagnose', key: 'Diagnose',
tab: '设备诊断', tab: '设备诊断',

View File

@ -19,7 +19,14 @@
> >
<template #headerTitle> <template #headerTitle>
<a-space> <a-space>
<a-button type="primary" @click="handleAdd">新增</a-button> <PermissionButton
type="primary"
@click="handleAdd"
hasPermission="device/Instance:add"
>
<template #icon><AIcon type="PlusOutlined" /></template>
新增
</PermissionButton>
<a-dropdown> <a-dropdown>
<a-button <a-button
>批量操作 <AIcon type="DownOutlined" >批量操作 <AIcon type="DownOutlined"
@ -27,77 +34,101 @@
<template #overlay> <template #overlay>
<a-menu> <a-menu>
<a-menu-item> <a-menu-item>
<a-button @click="exportVisible = true" <PermissionButton
><AIcon @click="exportVisible = true"
type="ExportOutlined" hasPermission="device/Instance:export"
/></a-button
> >
<template #icon
><AIcon type="ExportOutlined"
/></template>
批量导出设备
</PermissionButton>
</a-menu-item> </a-menu-item>
<a-menu-item> <a-menu-item>
<a-button @click="importVisible = true" <PermissionButton
><AIcon @click="importVisible = true"
type="ImportOutlined" hasPermission="device/Instance:import"
/></a-button
> >
<template #icon
><AIcon type="ImportOutlined"
/></template>
批量导入设备
</PermissionButton>
</a-menu-item> </a-menu-item>
<a-menu-item> <a-menu-item>
<a-popconfirm <PermissionButton
@confirm="activeAllDevice" ghost
title="确认激活全部设备?"
>
<a-button type="primary" ghost
><AIcon
type="CheckCircleOutlined"
/></a-button
>
</a-popconfirm>
</a-menu-item>
<a-menu-item>
<a-button
@click="syncDeviceStatus"
type="primary" type="primary"
><AIcon :popConfirm="{
type="SyncOutlined" title: '确认激活全部设备?',
/></a-button onConfirm: activeAllDevice,
}"
hasPermission="device/Instance:action"
> >
<template #icon
><AIcon type="CheckCircleOutlined"
/></template>
激活全部设备
</PermissionButton>
</a-menu-item>
<a-menu-item>
<PermissionButton
type="primary"
@click="syncDeviceStatus"
hasPermission="device/Instance:view"
>
<template #icon
><AIcon type="SyncOutlined"
/></template>
同步设备状态
</PermissionButton>
</a-menu-item> </a-menu-item>
<a-menu-item v-if="_selectedRowKeys.length"> <a-menu-item v-if="_selectedRowKeys.length">
<a-popconfirm <PermissionButton
@confirm="delSelectedDevice" type="primary"
title="已启用的设备无法删除,确认删除选中的禁用状态设备?" danger
:popConfirm="{
title: '已启用的设备无法删除,确认删除选中的禁用状态设备?',
onConfirm: delSelectedDevice,
}"
hasPermission="device/Instance:delete"
> >
<a-button type="primary" danger <template #icon
><AIcon ><AIcon type="DeleteOutlined"
type="DeleteOutlined" /></template>
/></a-button 删除选中设备
> </PermissionButton>
</a-popconfirm>
</a-menu-item>
<a-menu-item
v-if="_selectedRowKeys.length"
title="确认激活选中设备?"
>
<a-popconfirm
@confirm="activeSelectedDevice"
>
<a-button type="primary"
><AIcon
type="CheckOutlined"
/></a-button
>
</a-popconfirm>
</a-menu-item> </a-menu-item>
<a-menu-item v-if="_selectedRowKeys.length"> <a-menu-item v-if="_selectedRowKeys.length">
<a-popconfirm <PermissionButton
@confirm="disabledSelectedDevice" type="primary"
title="确认禁用选中设备?" :popConfirm="{
title: '确认激活选中设备',
onConfirm: activeSelectedDevice,
}"
hasPermission="device/Instance:action"
> >
<a-button type="primary" danger <template #icon
><AIcon ><AIcon type="CheckOutlined"
type="StopOutlined" /></template>
/></a-button 激活选中设备
</PermissionButton>
</a-menu-item>
<a-menu-item v-if="_selectedRowKeys.length">
<PermissionButton
type="primary"
danger
:popConfirm="{
title: '确认禁用选中设备?',
onConfirm: disabledSelectedDevice,
}"
hasPermission="device/Instance:action"
> >
</a-popconfirm> <template #icon
><AIcon type="StopOutlined"
/></template>
禁用选中设备
</PermissionButton>
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
</template> </template>
@ -151,30 +182,14 @@
</a-row> </a-row>
</template> </template>
<template #actions="item"> <template #actions="item">
<a-tooltip <PermissionButton
v-bind="item.tooltip"
:title="item.disabled && item.tooltip.title"
>
<a-popconfirm
v-if="item.popConfirm"
v-bind="item.popConfirm"
:disabled="item.disabled"
>
<a-button :disabled="item.disabled">
<AIcon
type="DeleteOutlined"
v-if="item.key === 'delete'"
/>
<template v-else>
<AIcon :type="item.icon" />
<span>{{ item?.text }}</span>
</template>
</a-button>
</a-popconfirm>
<template v-else>
<a-button
:disabled="item.disabled" :disabled="item.disabled"
:popConfirm="item.popConfirm"
:tooltip="{
...item.tooltip,
}"
@click="item.onClick" @click="item.onClick"
:hasPermission="'device/Instance:' + item.key"
> >
<AIcon <AIcon
type="DeleteOutlined" type="DeleteOutlined"
@ -184,9 +199,7 @@
<AIcon :type="item.icon" /> <AIcon :type="item.icon" />
<span>{{ item?.text }}</span> <span>{{ item?.text }}</span>
</template> </template>
</a-button> </PermissionButton>
</template>
</a-tooltip>
</template> </template>
</CardBox> </CardBox>
</template> </template>
@ -197,38 +210,25 @@
/> />
</template> </template>
<template #action="slotProps"> <template #action="slotProps">
<a-space :size="16"> <a-space>
<a-tooltip <template
v-for="i in getActions(slotProps, 'table')" v-for="i in getActions(slotProps, 'table')"
:key="i.key" :key="i.key"
v-bind="i.tooltip"
> >
<a-popconfirm <PermissionButton
v-if="i.popConfirm"
v-bind="i.popConfirm"
:disabled="i.disabled" :disabled="i.disabled"
:popConfirm="i.popConfirm"
:tooltip="{
...i.tooltip,
}"
@click="i.onClick"
type="link"
style="padding: 0px"
:hasPermission="'device/Instance:' + i.key"
> >
<a-button <template #icon><AIcon :type="i.icon" /></template>
:disabled="i.disabled" </PermissionButton>
style="padding: 0" </template>
type="link"
><AIcon :type="i.icon"
/></a-button>
</a-popconfirm>
<a-button
style="padding: 0"
type="link"
v-else
@click="i.onClick && i.onClick(slotProps)"
>
<a-button
:disabled="i.disabled"
style="padding: 0"
type="link"
><AIcon :type="i.icon"
/></a-button>
</a-button>
</a-tooltip>
</a-space> </a-space>
</template> </template>
</JTable> </JTable>
@ -278,6 +278,7 @@ import {
queryOrgThree, queryOrgThree,
} from '@/api/device/product'; } from '@/api/device/product';
import { queryTree } from '@/api/device/category'; import { queryTree } from '@/api/device/category';
import { useMenuStore } from '@/store/menu';
const router = useRouter(); const router = useRouter();
const instanceRef = ref<Record<string, any>>({}); const instanceRef = ref<Record<string, any>>({});
@ -291,6 +292,8 @@ const operationVisible = ref<boolean>(false);
const api = ref<string>(''); const api = ref<string>('');
const type = ref<string>(''); const type = ref<string>('');
const menuStory = useMenuStore()
const statusMap = new Map(); const statusMap = new Map();
statusMap.set('online', 'success'); statusMap.set('online', 'success');
statusMap.set('offline', 'error'); statusMap.set('offline', 'error');
@ -535,7 +538,7 @@ const handleAdd = () => {
* 查看 * 查看
*/ */
const handleView = (id: string) => { const handleView = (id: string) => {
router.push('/iot/device/instance/detail/' + id); menuStory.jumpPage('device/Instance/Detail', {id})
}; };
const getActions = ( const getActions = (
@ -556,7 +559,7 @@ const getActions = (
}, },
}, },
{ {
key: 'edit', key: 'update',
text: '编辑', text: '编辑',
tooltip: { tooltip: {
title: '编辑', title: '编辑',

View File

@ -195,10 +195,11 @@ import { isNoCommunity, downloadObject } from '@/utils/utils';
import { omit } from 'lodash-es'; import { omit } from 'lodash-es';
import { typeOptions } from '@/components/Search/util'; import { typeOptions } from '@/components/Search/util';
import Save from './Save/index.vue'; import Save from './Save/index.vue';
import { useMenuStore } from 'store/menu'
/** /**
* 表格数据 * 表格数据
*/ */
const menuStory = useMenuStore()
const router = useRouter(); const router = useRouter();
const isAdd = ref<number>(0); const isAdd = ref<number>(0);
const title = ref<string>(''); const title = ref<string>('');
@ -426,7 +427,7 @@ const beforeUpload = (file: any) => {
* 查看 * 查看
*/ */
const handleView = (id: string) => { const handleView = (id: string) => {
router.push('/iot/device/product/detail/' + id); menuStory.jumpPage('device/Product/Detail',{id})
}; };
/** /**

View File

@ -348,9 +348,9 @@ import BindDevice from './BindDevice.vue';
import Import from './Import.vue'; import Import from './Import.vue';
import Export from './Export.vue'; import Export from './Export.vue';
import Save from './Save.vue'; import Save from './Save.vue';
import { useMenuStore } from 'store/menu'
const router = useRouter(); const router = useRouter();
const menuStory = useMenuStore()
const cardManageRef = ref<Record<string, any>>({}); const cardManageRef = ref<Record<string, any>>({});
const params = ref<Record<string, any>>({}); const params = ref<Record<string, any>>({});
const _selectedRowKeys = ref<string[]>([]); const _selectedRowKeys = ref<string[]>([]);
@ -536,9 +536,10 @@ const getActions = (
}, },
icon: 'EyeOutlined', icon: 'EyeOutlined',
onClick: () => { onClick: () => {
router.push({ // router.push({
path: `/iot-card/CardManagement/detail/${data.id}`, // path: `/iot-card/CardManagement/detail/${data.id}`,
}); // });
menuStory.jumpPage('iot-card/CardManagement/Detail',{id:data.id})
}, },
}, },
{ {

View File

@ -109,7 +109,7 @@ interface GuideItemProps {
index?: number; index?: number;
auth: boolean; auth: boolean;
} }
const menuStory = useMenuStore();
const menuHasPermission = useMenuStore().hasPermission; const menuHasPermission = useMenuStore().hasPermission;
const btnHasPermission = usePermissionStore().hasPermission; const btnHasPermission = usePermissionStore().hasPermission;
@ -134,14 +134,16 @@ const guideList = [
name: '平台对接', name: '平台对接',
english: 'STEP1', english: 'STEP1',
auth: paltformPermission, auth: paltformPermission,
url: platformUrl, // url: platformUrl,
url: 'iot-card/Platform/Detail',
}, },
{ {
key: 'SCREEN', key: 'SCREEN',
name: '物联卡管理', name: '物联卡管理',
english: 'STEP2', english: 'STEP2',
auth: !!cardPermission, auth: !!cardPermission,
url: cardUrl, // url: cardUrl,
url: 'iot-card/CardManagement',
param: { save: true }, param: { save: true },
}, },
{ {
@ -149,7 +151,8 @@ const guideList = [
name: '操作记录', name: '操作记录',
english: 'STEP3', english: 'STEP3',
auth: !!recordUrl, auth: !!recordUrl,
url: recordUrl, // url: recordUrl,
url: 'iot-card/Record',
}, },
]; ];
@ -177,19 +180,25 @@ const pieChartData = ref<any[]>([
]); ]);
const jumpPage = (data: GuideItemProps) => { const jumpPage = (data: GuideItemProps) => {
if (data.url && data.auth) { // if (data.url && data.auth) {
router.push({ path: `${data.url}`, ...data.param }); // router.push({ path: `${data.url}`, ...data.param });
// } else {
// message.warning('');
// }
if (data.key === 'EQUIPMENT') {
menuStory.jumpPage(data.url, { id: 'add' });
} else { } else {
message.warning('暂无权限,请联系管理员'); menuStory.jumpPage(data.url);
} }
}; };
const jumpDashboard = () => { const jumpDashboard = () => {
if (dashBoardUrl) { // if (dashBoardUrl) {
router.push(`${dashBoardUrl}`); // router.push(`${dashBoardUrl}`);
} else { // } else {
message.warning('暂无权限,请联系管理员'); // message.warning('');
} // }
menuStory.jumpPage('iot-card/Dashboard');
}; };
/** /**

View File

@ -145,9 +145,9 @@ import { getImage } from '@/utils/comm';
import type { ActionsType } from '@/components/Table'; import type { ActionsType } from '@/components/Table';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { queryList, update, del } from '@/api/iot-card/platform'; import { queryList, update, del } from '@/api/iot-card/platform';
import { useMenuStore } from 'store/menu'
const menuStory = useMenuStore()
const router = useRouter() const router = useRouter()
const platformRef = ref<Record<string, any>>({}); const platformRef = ref<Record<string, any>>({});
const params = ref<Record<string, any>>({}); const params = ref<Record<string, any>>({});
@ -225,7 +225,8 @@ const getActions = (
}, },
icon: 'EditOutlined', icon: 'EditOutlined',
onClick: () => { onClick: () => {
router.push(`/iot-card/Platform/detail/${data.id}`); // router.push(`/iot-card/Platform/detail/${data.id}`);
menuStory.jumpPage('iot-card/Platform/Detail',{id:data.id});
}, },
}, },
{ {
@ -298,7 +299,7 @@ const handleSearch = (e: any) => {
* 新增 * 新增
*/ */
const handleAdd = () => { const handleAdd = () => {
router.push(`/iot-card/Platform/detail/:id`) menuStory.jumpPage('iot-card/Platform/Detail',{id:'add'})
}; };
</script> </script>

View File

@ -8,8 +8,12 @@
@cancel="handleCancel" @cancel="handleCancel"
:confirmLoading="btnLoading" :confirmLoading="btnLoading"
> >
<a-form layout="vertical"> <a-form ref="formRef" layout="vertical" :model="formData">
<a-form-item label="通知模版" v-bind="validateInfos.templateId"> <a-form-item
label="通知模版"
name="templateId"
:rules="{ required: true, message: '该字段为必填字段' }"
>
<a-select <a-select
v-model:value="formData.templateId" v-model:value="formData.templateId"
placeholder="请选择通知模版" placeholder="请选择通知模版"
@ -26,32 +30,37 @@
</a-form-item> </a-form-item>
<a-form-item <a-form-item
label="变量" label="变量"
v-bind="validateInfos.variableDefinitions" v-if="
v-if="templateDetailTable && templateDetailTable.length" formData.templateDetailTable &&
> formData.templateDetailTable.length
<a-table
ref="myTable"
class="debug-table"
:columns="columns"
:data-source="templateDetailTable"
:pagination="false"
:rowKey="
(record, index) => {
return record.id;
}
" "
> >
<template #bodyCell="{ column, text, record }"> <a-table
row-key="id"
:columns="columns"
:data-source="formData.templateDetailTable"
:pagination="false"
bordered
>
<template #bodyCell="{ column, record, index }">
<template <template
v-if="['id', 'name'].includes(column.dataIndex)" v-if="['id', 'name'].includes(column.dataIndex)"
> >
<span>{{ record[column.dataIndex] }}</span> <span>{{ record[column.dataIndex] }}</span>
</template> </template>
<template v-else> <template v-else>
<a-form-item
:name="['templateDetailTable', index, 'value']"
:rules="{
required: true,
message: '该字段为必填字段',
}"
>
<ValueItem <ValueItem
v-model:modelValue="record.value" v-model:modelValue="record.value"
:itemType="record.type" :itemType="record.type"
/> />
</a-form-item>
</template> </template>
</template> </template>
</a-table> </a-table>
@ -61,7 +70,6 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Form } from 'ant-design-vue';
import { PropType } from 'vue'; import { PropType } from 'vue';
import ConfigApi from '@/api/notice/config'; import ConfigApi from '@/api/notice/config';
import type { import type {
@ -70,8 +78,6 @@ import type {
} from '@/views/notice/Template/types'; } from '@/views/notice/Template/types';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
const useForm = Form.useForm;
type Emits = { type Emits = {
(e: 'update:visible', data: boolean): void; (e: 'update:visible', data: boolean): void;
}; };
@ -115,15 +121,16 @@ watch(
/** /**
* 获取模板详情 * 获取模板详情
*/ */
const templateDetailTable = ref<IVariableDefinitions[]>();
const getTemplateDetail = async () => { const getTemplateDetail = async () => {
const { result } = await ConfigApi.getTemplateDetail( const { result } = await ConfigApi.getTemplateDetail(
formData.value.templateId, formData.value.templateId,
); );
templateDetailTable.value = result.variableDefinitions.map((m: any) => ({ formData.value.templateDetailTable = result.variableDefinitions.map(
(m: any) => ({
...m, ...m,
value: undefined, value: undefined,
})); }),
);
}; };
const columns = [ const columns = [
@ -146,31 +153,27 @@ const columns = [
]; ];
// //
const formData = ref({ const formData = ref<{
templateId: string;
variableDefinitions: string;
templateDetailTable: IVariableDefinitions[];
}>({
templateId: '', templateId: '',
variableDefinitions: '', variableDefinitions: '',
templateDetailTable: [],
}); });
//
const formRules = ref({
templateId: [{ required: true, message: '请选择通知模板' }],
variableDefinitions: [{ required: false, message: '该字段是必填字段' }],
});
const { resetFields, validate, validateInfos, clearValidate } = useForm(
formData.value,
formRules.value,
);
/** /**
* 提交 * 提交
*/ */
const formRef = ref();
const btnLoading = ref(false); const btnLoading = ref(false);
const handleOk = () => { const handleOk = () => {
validate() formRef.value
.validate()
.then(async () => { .then(async () => {
const params = {}; const params = {};
templateDetailTable.value?.forEach((item) => { formData.value.templateDetailTable?.forEach((item) => {
params[item.id] = item.value; params[item.id] = item.value;
}); });
// console.log('params: ', params); // console.log('params: ', params);
@ -186,16 +189,20 @@ const handleOk = () => {
btnLoading.value = false; btnLoading.value = false;
}); });
}) })
.catch((err) => { .catch((err: any) => {
console.log('err: ', err); console.log('err: ', err);
}); });
}; };
const handleCancel = () => { const handleCancel = () => {
_vis.value = false; _vis.value = false;
templateDetailTable.value = []; formRef.value.resetFields();
resetFields(); formData.value.templateDetailTable = [];
}; };
</script> </script>
<style lang="less" scoped></style> <style lang="less" scoped>
:deep(.ant-table-cell .ant-form-item) {
margin-bottom: 0;
}
</style>

View File

@ -329,8 +329,6 @@ const getActions = (
}, },
icon: 'EditOutlined', icon: 'EditOutlined',
onClick: () => { onClick: () => {
// visible.value = true;
// current.value = data;
menuStory.jumpPage('notice/Config/Detail', { menuStory.jumpPage('notice/Config/Detail', {
id: data.id, id: data.id,
}); });

View File

@ -8,8 +8,12 @@
@cancel="handleCancel" @cancel="handleCancel"
:confirmLoading="btnLoading" :confirmLoading="btnLoading"
> >
<a-form layout="vertical"> <a-form ref="formRef" layout="vertical" :model="formData">
<a-form-item label="通知配置" v-bind="validateInfos.configId"> <a-form-item
label="通知配置"
name="configId"
:rules="{ required: true, message: '该字段为必填字段' }"
>
<a-select <a-select
v-model:value="formData.configId" v-model:value="formData.configId"
placeholder="请选择通知配置" placeholder="请选择通知配置"
@ -25,32 +29,37 @@
</a-form-item> </a-form-item>
<a-form-item <a-form-item
label="变量" label="变量"
v-bind="validateInfos.variableDefinitions" v-if="
v-if="templateDetailTable && templateDetailTable.length" formData.templateDetailTable &&
> formData.templateDetailTable.length
<a-table
ref="myTable"
class="debug-table"
:columns="columns"
:data-source="templateDetailTable"
:pagination="false"
:rowKey="
(record, index) => {
return record.id;
}
" "
> >
<template #bodyCell="{ column, text, record }"> <a-table
row-key="id"
:columns="columns"
:data-source="formData.templateDetailTable"
:pagination="false"
bordered
>
<template #bodyCell="{ column, record, index }">
<template <template
v-if="['id', 'name'].includes(column.dataIndex)" v-if="['id', 'name'].includes(column.dataIndex)"
> >
<span>{{ record[column.dataIndex] }}</span> <span>{{ record[column.dataIndex] }}</span>
</template> </template>
<template v-else> <template v-else>
<a-form-item
:name="['templateDetailTable', index, 'value']"
:rules="{
required: true,
message: '该字段为必填字段',
}"
>
<ValueItem <ValueItem
v-model:modelValue="record.value" v-model:modelValue="record.value"
:itemType="record.type" :itemType="record.type"
/> />
</a-form-item>
</template> </template>
</template> </template>
</a-table> </a-table>
@ -60,7 +69,6 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Form } from 'ant-design-vue';
import { PropType } from 'vue'; import { PropType } from 'vue';
import TemplateApi from '@/api/notice/template'; import TemplateApi from '@/api/notice/template';
import type { import type {
@ -70,7 +78,6 @@ import type {
} from '@/views/notice/Template/types'; } from '@/views/notice/Template/types';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
const useForm = Form.useForm;
type Emits = { type Emits = {
(e: 'update:visible', data: boolean): void; (e: 'update:visible', data: boolean): void;
@ -103,6 +110,7 @@ const getConfigList = async () => {
}; };
const { result } = await TemplateApi.getConfig(params); const { result } = await TemplateApi.getConfig(params);
configList.value = result; configList.value = result;
formData.value.configId = result[0]?.id;
}; };
watch( watch(
@ -118,13 +126,14 @@ watch(
/** /**
* 获取模板详情 * 获取模板详情
*/ */
const templateDetailTable = ref<IVariableDefinitions[]>();
const getTemplateDetail = async () => { const getTemplateDetail = async () => {
const { result } = await TemplateApi.getTemplateDetail(props.data.id); const { result } = await TemplateApi.getTemplateDetail(props.data.id);
templateDetailTable.value = result.variableDefinitions.map((m: any) => ({ formData.value.templateDetailTable = result.variableDefinitions.map(
(m: any) => ({
...m, ...m,
value: undefined, value: undefined,
})); }),
);
}; };
const columns = [ const columns = [
@ -147,31 +156,27 @@ const columns = [
]; ];
// //
const formData = ref({ const formData = ref<{
configId: string;
variableDefinitions: string;
templateDetailTable: IVariableDefinitions[];
}>({
configId: '', configId: '',
variableDefinitions: '', variableDefinitions: '',
templateDetailTable: [],
}); });
//
const formRules = ref({
configId: [{ required: true, message: '请选择通知模板' }],
variableDefinitions: [{ required: false, message: '该字段是必填字段' }],
});
const { resetFields, validate, validateInfos, clearValidate } = useForm(
formData.value,
formRules.value,
);
/** /**
* 提交 * 提交
*/ */
const formRef = ref();
const btnLoading = ref(false); const btnLoading = ref(false);
const handleOk = () => { const handleOk = () => {
validate() formRef.value
.validate()
.then(async () => { .then(async () => {
const params = {}; const params = {};
templateDetailTable.value?.forEach((item) => { formData.value.templateDetailTable?.forEach((item) => {
params[item.id] = item.value; params[item.id] = item.value;
}); });
// console.log('params: ', params); // console.log('params: ', params);
@ -187,16 +192,20 @@ const handleOk = () => {
btnLoading.value = false; btnLoading.value = false;
}); });
}) })
.catch((err) => { .catch((err: any) => {
console.log('err: ', err); console.log('err: ', err);
}); });
}; };
const handleCancel = () => { const handleCancel = () => {
_vis.value = false; _vis.value = false;
templateDetailTable.value = []; formRef.value.resetFields();
resetFields(); formData.value.templateDetailTable = [];
}; };
</script> </script>
<style lang="less" scoped></style> <style lang="less" scoped>
:deep(.ant-table-cell .ant-form-item) {
margin-bottom: 0;
}
</style>

View File

@ -801,7 +801,7 @@ const resetPublicFiles = () => {
formData.value.configId = undefined; formData.value.configId = undefined;
if ( if (
formData.value.type === 'dingTalk' || formData.value.provider === 'dingTalkMessage' ||
formData.value.type === 'weixin' formData.value.type === 'weixin'
) { ) {
formData.value.template.toTag = undefined; formData.value.template.toTag = undefined;
@ -961,7 +961,6 @@ const handleTypeChange = () => {
const handleProviderChange = () => { const handleProviderChange = () => {
formData.value.template = formData.value.template =
TEMPLATE_FIELD_MAP[formData.value.type][formData.value.provider]; TEMPLATE_FIELD_MAP[formData.value.type][formData.value.provider];
console.log('formData.value.template: ', formData.value.template);
getConfigList(); getConfigList();
resetPublicFiles(); resetPublicFiles();
}; };
@ -1023,6 +1022,8 @@ const handleSubmit = () => {
if (formData.value.template.messageType === 'link') if (formData.value.template.messageType === 'link')
delete formData.value.template.markdown; delete formData.value.template.markdown;
// console.log('formData.value: ', formData.value); // console.log('formData.value: ', formData.value);
// , , , :
setTimeout(() => {
validate() validate()
.then(async () => { .then(async () => {
formData.value.template.ttsCode = formData.value.template.ttsCode =
@ -1046,6 +1047,7 @@ const handleSubmit = () => {
.finally(() => { .finally(() => {
btnLoading.value = false; btnLoading.value = false;
}); });
}, 200);
}; };
// test // test

View File

@ -159,9 +159,9 @@
<script setup lang="ts"> <script setup lang="ts">
import TemplateApi from '@/api/notice/template'; import TemplateApi from '@/api/notice/template';
import type { ActionsType } from '@/components/Table/index.vue'; import type { ActionsType } from '@/components/Table/index.vue';
import { getImage, LocalStore } from '@/utils/comm'; // import { getImage, LocalStore } from '@/utils/comm';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable'; // import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
import { NOTICE_METHOD, MSG_TYPE } from '@/views/notice/const'; import { NOTICE_METHOD, MSG_TYPE } from '@/views/notice/const';
@ -301,13 +301,6 @@ const handleExport = () => {
downloadObject(configRef.value.dataSource, `通知配置`); downloadObject(configRef.value.dataSource, `通知配置`);
}; };
/**
* 查看
*/
const handleView = (id: string) => {
message.warn(id + '暂未开发');
};
const syncVis = ref(false); const syncVis = ref(false);
const debugVis = ref(false); const debugVis = ref(false);
const logVis = ref(false); const logVis = ref(false);
@ -326,8 +319,6 @@ const getActions = (
}, },
icon: 'EditOutlined', icon: 'EditOutlined',
onClick: () => { onClick: () => {
// visible.value = true;
// current.value = data;
menuStory.jumpPage('notice/Template/Detail', { menuStory.jumpPage('notice/Template/Detail', {
id: data.id, id: data.id,
}); });

View File

@ -85,9 +85,9 @@ export default defineConfig(({ mode}) => {
// target: 'http://192.168.33.22:8800', // target: 'http://192.168.33.22:8800',
// target: 'http://192.168.32.244:8881', // target: 'http://192.168.32.244:8881',
// target: 'http://47.112.135.104:5096', // opcua // target: 'http://47.112.135.104:5096', // opcua
// target: 'http://120.77.179.54:8844', // 120测试 target: 'http://120.77.179.54:8844', // 120测试
// target: 'http://47.108.63.174:8845', // 测试 // target: 'http://47.108.63.174:8845', // 测试
target: 'http://120.77.179.54:8844', // target: 'http://120.77.179.54:8844',
ws: 'ws://120.77.179.54:8844', ws: 'ws://120.77.179.54:8844',
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '') rewrite: (path) => path.replace(/^\/api/, '')

10268
yarn.lock

File diff suppressed because it is too large Load Diff