fix: 修改阿里云新增和卡片

This commit is contained in:
100011797 2023-02-23 15:47:34 +08:00
parent b239bfc8b2
commit 34d870f1ec
13 changed files with 639 additions and 482 deletions

View File

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

View File

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

View File

@ -154,6 +154,7 @@ const JTable = defineComponent<JTableProps>({
const pageSize = ref<number>(6)
const total = ref<number>(0)
const loading = ref<boolean>(true)
const loading1 = ref<boolean>(true)
const _columns = computed(() => props.columns.filter(i => !(i?.hideInTable)))
@ -244,10 +245,6 @@ const JTable = defineComponent<JTableProps>({
window.onresize = null
})
watchEffect(() => {
// console.log(props.bodyStyle)
})
/**
*
* @param _params
@ -292,92 +289,94 @@ const JTable = defineComponent<JTableProps>({
</div>
</div>
{/* content */}
<div class={styles['jtable-content']}>
{
props.alertRender && props?.rowSelection && props?.rowSelection?.selectedRowKeys && props.rowSelection.selectedRowKeys?.length ?
<div class={styles['jtable-alert']}>
<Alert
message={'已选择' + props?.rowSelection?.selectedRowKeys?.length + '项'}
type="info"
onClose={() => {
emit('cancelSelect')
}}
closeText={<a-button type="link"></a-button>}
/>
</div> : null
}
{
_model.value === ModelEnum.CARD ?
<div class={styles['jtable-card']}>
{
_dataSource.value.length ?
<div
class={styles['jtable-card-items']}
style={{ gridTemplateColumns: `repeat(${column.value}, 1fr)` }}
>
{
_dataSource.value.map(item => slots.card ?
<div class={[styles['jtable-card-item'], props.cardBodyClass]}>
{slots.card(item)}
</div> : null
)
}
</div> :
<div><JEmpty style="margin: 10% 0" /></div>
}
</div> :
<div>
<Table
dataSource={_dataSource.value}
columns={_columns.value}
pagination={false}
rowKey="id"
rowSelection={props.rowSelection}
scroll={{ x: 1366 }}
v-slots={{
bodyCell: (dt: Record<string, any>) => {
const { column, record } = dt;
if ((column?.key || column?.dataIndex) && column?.scopedSlots && (slots?.[column?.dataIndex] || slots?.[column?.key])) {
const _key = column?.key || column?.dataIndex
return slots?.[_key]!(record)
} else {
return record?.[column?.dataIndex] || ''
}
},
emptyText: () => <JEmpty style="margin: 10% 0" />
}}
/>
</div>
}
</div>
{
!loading.value ? <div class={styles['jtable-content']}>
{
props.alertRender && props?.rowSelection && props?.rowSelection?.selectedRowKeys && props.rowSelection.selectedRowKeys?.length ?
<div class={styles['jtable-alert']}>
<Alert
message={'已选择' + props?.rowSelection?.selectedRowKeys?.length + '项'}
type="info"
onClose={() => {
emit('cancelSelect')
}}
closeText={<a-button type="link"></a-button>}
/>
</div> : null
}
{
_model.value === ModelEnum.CARD ?
<div class={styles['jtable-card']}>
{
_dataSource.value.length ?
<div
class={styles['jtable-card-items']}
style={{ gridTemplateColumns: `repeat(${column.value}, 1fr)` }}
>
{
_dataSource.value.map(item => slots.card ?
<div class={[styles['jtable-card-item'], props.cardBodyClass]}>
{slots.card(item)}
</div> : null
)
}
</div> :
<div><JEmpty style="margin: 10% 0" /></div>
}
</div> :
<div>
<Table
dataSource={_dataSource.value}
columns={_columns.value}
pagination={false}
rowKey="id"
rowSelection={props.rowSelection}
scroll={{ x: 1366 }}
v-slots={{
bodyCell: (dt: Record<string, any>) => {
const { column, record } = dt;
if ((column?.key || column?.dataIndex) && column?.scopedSlots && (slots?.[column?.dataIndex] || slots?.[column?.key])) {
const _key = column?.key || column?.dataIndex
return slots?.[_key]!(record)
} else {
return record?.[column?.dataIndex] || ''
}
},
emptyText: () => <JEmpty style="margin: 10% 0" />
}}
/>
</div>
}
</div> : <div style="width: 100%; height: 400px"></div>
}
{/* 分页 */}
{
(!!_dataSource.value.length) && !props.noPagination && props.type === 'PAGE' &&
<div class={styles['jtable-pagination']}>
{
slots?.paginationRender ?
slots.paginationRender() :
<Pagination
size="small"
total={total.value}
showQuickJumper={false}
showSizeChanger={true}
current={pageIndex.value + 1}
pageSize={pageSize.value}
pageSizeOptions={['12', '24', '48', '60', '100']}
showTotal={(num) => {
const minSize = pageIndex.value * pageSize.value + 1;
const MaxSize = (pageIndex.value + 1) * pageSize.value;
return `${minSize} - ${MaxSize > num ? num : MaxSize} 条/总共 ${num}`;
}}
onChange={(page, size) => {
handleSearch({
...props.params,
pageSize: size,
pageIndex: pageSize.value === size ? (page ? page - 1 : 0) : 0
})
}}
/>
slots.paginationRender() :
<Pagination
size="small"
total={total.value}
showQuickJumper={false}
showSizeChanger={true}
current={pageIndex.value + 1}
pageSize={pageSize.value}
pageSizeOptions={['12', '24', '48', '60', '100']}
showTotal={(num) => {
const minSize = pageIndex.value * pageSize.value + 1;
const MaxSize = (pageIndex.value + 1) * pageSize.value;
return `${minSize} - ${MaxSize > num ? num : MaxSize} 条/总共 ${num}`;
}}
onChange={(page, size) => {
handleSearch({
...props.params,
pageSize: size,
pageIndex: pageSize.value === size ? (page ? page - 1 : 0) : 0
})
}}
/>
}
</div>
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,90 +1,153 @@
<template>
<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>
<a-space>
<a-button type="link" @click="visible = true"><AIcon type="EditOutlined" />编辑</a-button>
<a-popconfirm title="确认重新应用该配置?" @confirm="deployBtn">
<a-button type="link" v-if="instanceStore.detail.current?.value !== 'notActive'"><AIcon type="CheckOutlined" />应用配置<a-tooltip title="修改配置后需重新应用后才能生效。"><AIcon type="QuestionCircleOutlined" /></a-tooltip></a-button>
</a-popconfirm>
<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>
</a-popconfirm>
<PermissionButton
type="link"
@click="visible = true"
hasPermission="device/Instance:update"
>
<template #icon><AIcon type="EditOutlined" /></template>
编辑
</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>
</div>
<a-descriptions bordered size="small" v-for="i in config" :key="i.name">
<template #title><h4 style="font-size: 15px">{{i.name}}</h4></template>
<a-descriptions-item v-for="item in i.properties" :key="item.property">
<template #title
><h4 style="font-size: 15px">{{ i.name }}</h4></template
>
<a-descriptions-item
v-for="item in i.properties"
:key="item.property"
>
<template #label>
<span style="margin-right: 5px">{{item.name}}</span>
<a-tooltip v-if="item.description" :title="item.description"><AIcon type="QuestionCircleOutlined" /></a-tooltip>
<span style="margin-right: 5px">{{ item.name }}</span>
<a-tooltip v-if="item.description" :title="item.description"
><AIcon type="QuestionCircleOutlined"
/></a-tooltip>
</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>{{ 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>{{
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>
</a-descriptions-item>
</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>
</template>
<script lang="ts" setup>
import { useInstanceStore } from "@/store/instance"
import { ConfigMetadata } from "@/views/device/Product/typings"
import { getConfigMetadata, _deploy, configurationReset } from '@/api/device/instance'
import { message } from "ant-design-vue"
import Save from './Save.vue'
import { useInstanceStore } from '@/store/instance';
import { ConfigMetadata } from '@/views/device/Product/typings';
import {
getConfigMetadata,
_deploy,
configurationReset,
} from '@/api/device/instance';
import { message } from 'ant-design-vue';
import Save from './Save.vue';
const instanceStore = useInstanceStore()
const visible = ref<boolean>(false)
const config = ref<ConfigMetadata[]>([])
const instanceStore = useInstanceStore();
const visible = ref<boolean>(false);
const config = ref<ConfigMetadata[]>([]);
watchEffect(() => {
if(instanceStore.current.id){
getConfigMetadata(instanceStore.current.id).then(resp => {
if(resp.status === 200){
config.value = resp?.result as ConfigMetadata[]
if (instanceStore.current.id) {
getConfigMetadata(instanceStore.current.id).then((resp) => {
if (resp.status === 200) {
config.value = resp?.result as ConfigMetadata[];
}
})
});
}
})
});
const isExit = (property: string) => {
return (
instanceStore.current?.cachedConfiguration &&
instanceStore.current?.cachedConfiguration[property] !== undefined &&
instanceStore.current?.configuration &&
instanceStore.current?.configuration[property] !==
instanceStore.current?.cachedConfiguration[property]
instanceStore.current?.cachedConfiguration &&
instanceStore.current?.cachedConfiguration[property] !== undefined &&
instanceStore.current?.configuration &&
instanceStore.current?.configuration[property] !==
instanceStore.current?.cachedConfiguration[property]
);
}
};
const deployBtn = async () => {
if(instanceStore.current.id){
const resp = await _deploy(instanceStore.current.id)
if (instanceStore.current.id) {
const resp = await _deploy(instanceStore.current.id);
if (resp.status === 200) {
message.success('操作成功')
instanceStore.refresh(instanceStore.current.id)
message.success('操作成功');
instanceStore.refresh(instanceStore.current.id);
}
}
}
};
const resetBtn = async () => {
if(instanceStore.current.id){
const resp = await configurationReset(instanceStore.current.id)
if (instanceStore.current.id) {
const resp = await configurationReset(instanceStore.current.id);
if (resp.status === 200) {
message.success('恢复默认配置成功')
instanceStore.refresh(instanceStore.current.id)
message.success('恢复默认配置成功');
instanceStore.refresh(instanceStore.current.id);
}
}
}
};
const saveBtn = () => {
visible.value = false
if(instanceStore.current.id){
instanceStore.refresh(instanceStore.current.id)
visible.value = false;
if (instanceStore.current.id) {
instanceStore.refresh(instanceStore.current.id);
}
}
};
</script>

View File

@ -3,31 +3,50 @@
<a-descriptions bordered>
<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>
<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>
<Save v-if="visible" @save="saveBtn" @close="visible = false" />
</div>
</template>
<script lang="ts" setup>
import { useInstanceStore } from "@/store/instance"
import Save from './Save.vue'
const instanceStore = useInstanceStore()
import { useInstanceStore } from '@/store/instance';
import Save from './Save.vue';
const instanceStore = useInstanceStore();
const dataSource = ref<Record<any, any>[]>([])
const dataSource = ref<Record<any, any>[]>([]);
const visible = ref<boolean>(false);
watchEffect(() => {
const arr = (instanceStore.current?.relations || []).reverse()
dataSource.value = arr as Record<any, any>[]
})
const arr = (instanceStore.current?.relations || []).reverse();
dataSource.value = arr as Record<any, any>[];
});
const saveBtn = () => {
visible.value = false
if(instanceStore.current.id){
instanceStore.refresh(instanceStore.current.id)
visible.value = false;
if (instanceStore.current.id) {
instanceStore.refresh(instanceStore.current.id);
}
}
};
</script>

View File

@ -3,32 +3,44 @@
<a-descriptions bordered>
<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>
<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>
<Save v-if="visible" @close="visible = false" @save="saveBtn" />
</div>
</template>
<script lang="ts" setup>
import { useInstanceStore } from "@/store/instance"
import Save from './Save.vue'
import { useInstanceStore } from '@/store/instance';
import Save from './Save.vue';
const instanceStore = useInstanceStore()
const instanceStore = useInstanceStore();
const dataSource = ref<Record<any, any>[]>([])
const visible = ref<boolean>(false)
const dataSource = ref<Record<any, any>[]>([]);
const visible = ref<boolean>(false);
watchEffect(() => {
const arr = (instanceStore.current?.tags || [])
dataSource.value = arr as Record<any, any>[]
})
const arr = instanceStore.current?.tags || [];
dataSource.value = arr as Record<any, any>[];
});
const saveBtn = () => {
visible.value = false
if(instanceStore.current.id){
instanceStore.refresh(instanceStore.current.id)
visible.value = false;
if (instanceStore.current.id) {
instanceStore.refresh(instanceStore.current.id);
}
}
};
</script>

View File

@ -3,43 +3,105 @@
<a-descriptions bordered>
<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>
<a-descriptions-item label="设备ID">{{ instanceStore.current.id }}</a-descriptions-item>
<a-descriptions-item label="产品名称">{{ instanceStore.current.productName }}</a-descriptions-item>
<a-descriptions-item label="产品分类">{{ instanceStore.current.classifiedName }}</a-descriptions-item>
<a-descriptions-item label="设备类型">{{ instanceStore.current.deviceType?.text }}</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-item label="设备ID">{{
instanceStore.current.id
}}</a-descriptions-item>
<a-descriptions-item label="产品名称">{{
instanceStore.current.productName
}}</a-descriptions-item>
<a-descriptions-item label="产品分类">{{
instanceStore.current.classifiedName
}}</a-descriptions-item>
<a-descriptions-item label="设备类型">{{
instanceStore.current.deviceType?.text
}}</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>
<Config />
<Tags v-if="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" />
<Tags
v-if="
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>
</template>
<script lang="ts" setup>
import { useInstanceStore } from '@/store/instance'
import Save from '../../Save/index.vue'
import Config from './components/Config/index.vue'
import Tags from './components/Tags/index.vue'
import Relation from './components/Relation/index.vue'
import moment from 'moment'
import { useInstanceStore } from '@/store/instance';
import Save from '../../Save/index.vue';
import Config from './components/Config/index.vue';
import Tags from './components/Tags/index.vue';
import Relation from './components/Relation/index.vue';
import moment from 'moment';
const visible = ref<boolean>(false)
const instanceStore = useInstanceStore()
const visible = ref<boolean>(false);
const instanceStore = useInstanceStore();
const saveBtn = () => {
if(instanceStore.current?.id){
instanceStore.refresh(instanceStore.current?.id)
if (instanceStore.current?.id) {
instanceStore.refresh(instanceStore.current?.id);
}
visible.value = false
}
visible.value = false;
};
</script>

View File

@ -8,7 +8,8 @@
<template #title>
<div>
<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-space>
<a-badge
@ -19,25 +20,35 @@
)
"
/>
<a-popconfirm
title="确认启用设备"
@confirm="handleAction"
<PermissionButton
v-if="
instanceStore.current.state?.value ===
'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>
<a-popconfirm
title="确认断开连接"
@confirm="handleDisconnect"
启用设备
</PermissionButton>
<PermissionButton
v-if="
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
v-if="
instanceStore.current?.accessProvider ===
@ -66,14 +77,14 @@
instanceStore.current.id
}}</a-descriptions-item>
<a-descriptions-item label="所属产品">
<a-button
style="margin-top: -5px; padding: 0"
<PermissionButton
type="link"
style="margin-top: -5px; padding: 0"
@click="jumpProduct"
>{{
instanceStore.current.productName
}}</a-button
hasPermission="device/Product:view"
>
{{ instanceStore.current.productName }}
</PermissionButton>
</a-descriptions-item>
</a-descriptions>
</div>
@ -109,6 +120,9 @@ import { _deploy, _disconnect } from '@/api/device/instance';
import { message } from 'ant-design-vue';
import { getImage } from '@/utils/comm';
import { getWebSocket } from '@/utils/websocket';
import { useMenuStore } from '@/store/menu';
const menuStory = useMenuStore();
const route = useRoute();
const instanceStore = useInstanceStore();
@ -180,7 +194,9 @@ watch(
{ immediate: true, deep: true },
);
const onBack = () => {};
const onBack = () => {
menuStory.jumpPage('device/Instance');
};
const onTabChange = (e: string) => {
instanceStore.tabActiveKey = e;
@ -214,12 +230,18 @@ const handleRefresh = async () => {
};
const jumpProduct = () => {
message.warn('暂未开发');
menuStory.jumpPage('device/Product/Detail', {
id: instanceStore.current.productId,
});
};
watchEffect(() => {
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({
key: 'Diagnose',
tab: '设备诊断',

View File

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