feat(iot):增加 alert 模块的代码评审
parent
6740401f6c
commit
58f8b7fb22
|
|
@ -1,8 +1,10 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { AlertConfigApi } from '#/api/iot/alert/config';
|
import type { AlertConfig } from '#/api/iot/alert/config';
|
||||||
|
|
||||||
import { Page, useVbenModal } from '@vben/common-ui';
|
import { Page, useVbenModal } from '@vben/common-ui';
|
||||||
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
|
import { getDictLabel } from '@vben/hooks';
|
||||||
|
|
||||||
import { message, Tag } from 'ant-design-vue';
|
import { message, Tag } from 'ant-design-vue';
|
||||||
|
|
||||||
|
|
@ -25,54 +27,18 @@ function handleRefresh() {
|
||||||
gridApi.query();
|
gridApi.query();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取告警级别文本
|
|
||||||
function getLevelText(level?: number) {
|
|
||||||
const levelMap: Record<number, string> = {
|
|
||||||
1: '提示',
|
|
||||||
2: '一般',
|
|
||||||
3: '警告',
|
|
||||||
4: '严重',
|
|
||||||
5: '紧急',
|
|
||||||
};
|
|
||||||
return level ? levelMap[level] || `级别${level}` : '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取告警级别颜色
|
|
||||||
function getLevelColor(level?: number) {
|
|
||||||
const colorMap: Record<number, string> = {
|
|
||||||
1: 'blue',
|
|
||||||
2: 'green',
|
|
||||||
3: 'orange',
|
|
||||||
4: 'red',
|
|
||||||
5: 'purple',
|
|
||||||
};
|
|
||||||
return level ? colorMap[level] || 'default' : 'default';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取接收类型文本
|
|
||||||
function getReceiveTypeText(type?: number) {
|
|
||||||
const typeMap: Record<number, string> = {
|
|
||||||
1: '站内信',
|
|
||||||
2: '邮箱',
|
|
||||||
3: '短信',
|
|
||||||
4: '微信',
|
|
||||||
5: '钉钉',
|
|
||||||
};
|
|
||||||
return type ? typeMap[type] || `类型${type}` : '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 创建告警配置 */
|
/** 创建告警配置 */
|
||||||
function handleCreate() {
|
function handleCreate() {
|
||||||
formModalApi.setData(null).open();
|
formModalApi.setData(null).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 编辑告警配置 */
|
/** 编辑告警配置 */
|
||||||
function handleEdit(row: AlertConfigApi.AlertConfig) {
|
function handleEdit(row: AlertConfig) {
|
||||||
formModalApi.setData(row).open();
|
formModalApi.setData(row).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除告警配置 */
|
/** 删除告警配置 */
|
||||||
async function handleDelete(row: AlertConfigApi.AlertConfig) {
|
async function handleDelete(row: AlertConfig) {
|
||||||
const hideLoading = message.loading({
|
const hideLoading = message.loading({
|
||||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||||
duration: 0,
|
duration: 0,
|
||||||
|
|
@ -115,7 +81,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
refresh: true,
|
refresh: true,
|
||||||
search: true,
|
search: true,
|
||||||
},
|
},
|
||||||
} as VxeTableGridOptions<AlertConfigApi.AlertConfig>,
|
} as VxeTableGridOptions<AlertConfig>,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -130,24 +96,25 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
label: $t('ui.actionTitle.create', ['告警配置']),
|
label: $t('ui.actionTitle.create', ['告警配置']),
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
icon: ACTION_ICON.ADD,
|
icon: ACTION_ICON.ADD,
|
||||||
|
auth: ['iot:alert-config:create'],
|
||||||
onClick: handleCreate,
|
onClick: handleCreate,
|
||||||
},
|
},
|
||||||
]"
|
]"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- TODO @AI:可以在 data 里渲染么?应该 antd 里有例子的; -->
|
||||||
<!-- 告警级别列 -->
|
<!-- 告警级别列 -->
|
||||||
<template #level="{ row }">
|
<template #level="{ row }">
|
||||||
<Tag :color="getLevelColor(row.level)">
|
<Tag>
|
||||||
{{ getLevelText(row.level) }}
|
{{ getDictLabel(DICT_TYPE.IOT_ALERT_LEVEL, row.level) }}
|
||||||
</Tag>
|
</Tag>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- TODO @AI:可以在 data 里渲染么?应该 antd 里有例子的; -->
|
||||||
<!-- 关联场景联动规则列 -->
|
<!-- 关联场景联动规则列 -->
|
||||||
<template #sceneRules="{ row }">
|
<template #sceneRules="{ row }">
|
||||||
<span>{{ row.sceneRuleIds?.length || 0 }} 条</span>
|
<span>{{ row.sceneRuleIds?.length || 0 }} 条</span>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- TODO @AI:可以在 data 里渲染么?应该 antd 里有例子的; -->
|
||||||
<!-- 接收类型列 -->
|
<!-- 接收类型列 -->
|
||||||
<template #receiveTypes="{ row }">
|
<template #receiveTypes="{ row }">
|
||||||
<Tag
|
<Tag
|
||||||
|
|
@ -155,10 +122,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
:key="index"
|
:key="index"
|
||||||
class="mr-1"
|
class="mr-1"
|
||||||
>
|
>
|
||||||
{{ getReceiveTypeText(type) }}
|
{{ getDictLabel(DICT_TYPE.IOT_ALERT_RECEIVE_TYPE, type) }}
|
||||||
</Tag>
|
</Tag>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 操作列 -->
|
<!-- 操作列 -->
|
||||||
<template #actions="{ row }">
|
<template #actions="{ row }">
|
||||||
<TableAction
|
<TableAction
|
||||||
|
|
@ -167,6 +133,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
label: $t('common.edit'),
|
label: $t('common.edit'),
|
||||||
type: 'link',
|
type: 'link',
|
||||||
icon: ACTION_ICON.EDIT,
|
icon: ACTION_ICON.EDIT,
|
||||||
|
auth: ['iot:alert-config:update'],
|
||||||
onClick: handleEdit.bind(null, row),
|
onClick: handleEdit.bind(null, row),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -174,6 +141,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
type: 'link',
|
type: 'link',
|
||||||
danger: true,
|
danger: true,
|
||||||
icon: ACTION_ICON.DELETE,
|
icon: ACTION_ICON.DELETE,
|
||||||
|
auth: ['iot:alert-config:delete'],
|
||||||
popConfirm: {
|
popConfirm: {
|
||||||
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
title: $t('ui.actionMessage.deleteConfirm', [row.name]),
|
||||||
confirm: handleDelete.bind(null, row),
|
confirm: handleDelete.bind(null, row),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { AlertConfigApi } from '#/api/iot/alert/config';
|
// TODO @AI:是不是要简化成 form.vue?类似 system user form 的情况?
|
||||||
|
// TODO @AI:整体风格,看看是不是要对齐下 system user form 的风格;
|
||||||
|
import type { AlertConfig } from '#/api/iot/alert/config';
|
||||||
|
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
|
|
@ -20,7 +22,7 @@ import { useFormSchema } from '../config/data';
|
||||||
defineOptions({ name: 'IoTAlertConfigForm' });
|
defineOptions({ name: 'IoTAlertConfigForm' });
|
||||||
|
|
||||||
const emit = defineEmits(['success']);
|
const emit = defineEmits(['success']);
|
||||||
const formData = ref<AlertConfigApi.AlertConfig>();
|
const formData = ref<AlertConfig>();
|
||||||
const getTitle = computed(() => {
|
const getTitle = computed(() => {
|
||||||
return formData.value?.id
|
return formData.value?.id
|
||||||
? $t('ui.actionTitle.edit', ['告警配置'])
|
? $t('ui.actionTitle.edit', ['告警配置'])
|
||||||
|
|
@ -47,7 +49,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
}
|
}
|
||||||
modalApi.lock();
|
modalApi.lock();
|
||||||
// 提交表单
|
// 提交表单
|
||||||
const data = (await formApi.getValues()) as AlertConfigApi.AlertConfig;
|
const data = (await formApi.getValues()) as AlertConfig;
|
||||||
try {
|
try {
|
||||||
await (formData.value?.id
|
await (formData.value?.id
|
||||||
? updateAlertConfig(data)
|
? updateAlertConfig(data)
|
||||||
|
|
@ -66,9 +68,10 @@ const [Modal, modalApi] = useVbenModal({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 加载数据
|
// 加载数据
|
||||||
const data = modalApi.getData<AlertConfigApi.AlertConfig>();
|
const data = modalApi.getData<AlertConfig>();
|
||||||
if (!data || !data.id) {
|
if (!data || !data.id) {
|
||||||
// 新增时设置默认值
|
// 新增时设置默认值
|
||||||
|
// TODO @AI:这里是不是不用默认值?status 在 data.ts 里默认就好了?
|
||||||
await formApi.setValues({
|
await formApi.setValues({
|
||||||
status: 0,
|
status: 0,
|
||||||
sceneRuleIds: [],
|
sceneRuleIds: [],
|
||||||
|
|
|
||||||
|
|
@ -1,110 +1,57 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
// TODO @AI:整体风格,是不是对齐现有的 antd 处理的:1)详情独立 modules;2)process 是不是要这样?还是做一个独立的界面出来?
|
||||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||||
import type { AlertRecord } from '#/api/iot/alert/record';
|
import type { AlertRecord } from '#/api/iot/alert/record';
|
||||||
|
|
||||||
import { h, onMounted, ref } from 'vue';
|
import { h, ref } from 'vue';
|
||||||
|
|
||||||
import { Page } from '@vben/common-ui';
|
import { Page } from '@vben/common-ui';
|
||||||
|
import { DICT_TYPE } from '@vben/constants';
|
||||||
|
import { getDictLabel } from '@vben/hooks';
|
||||||
import { IconifyIcon } from '@vben/icons';
|
import { IconifyIcon } from '@vben/icons';
|
||||||
|
|
||||||
import { Button, message, Modal, Popover, Tag } from 'ant-design-vue';
|
import { Button, Input, message, Modal, Popover, Tag } from 'ant-design-vue';
|
||||||
|
|
||||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||||
import { getAlertRecordPage, processAlertRecord } from '#/api/iot/alert/record';
|
import { getAlertRecordPage, processAlertRecord } from '#/api/iot/alert/record';
|
||||||
import { getSimpleDeviceList } from '#/api/iot/device/device';
|
|
||||||
import { getSimpleProductList } from '#/api/iot/product/product';
|
|
||||||
|
|
||||||
import { useGridColumns, useGridFormSchema } from './data';
|
import { useGridColumns, useGridFormSchema } from './data';
|
||||||
|
|
||||||
defineOptions({ name: 'IoTAlertRecord' });
|
defineOptions({ name: 'IoTAlertRecord' });
|
||||||
|
|
||||||
const productList = ref<any[]>([]);
|
|
||||||
const deviceList = ref<any[]>([]);
|
|
||||||
|
|
||||||
/** 刷新表格 */
|
/** 刷新表格 */
|
||||||
function handleRefresh() {
|
function handleRefresh() {
|
||||||
gridApi.query();
|
gridApi.query();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载产品和设备列表
|
/** 处理告警记录 */
|
||||||
async function loadData() {
|
function handleProcess(row: AlertRecord) {
|
||||||
productList.value = await getSimpleProductList();
|
const processRemark = ref('');
|
||||||
deviceList.value = await getSimpleDeviceList();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取告警级别文本
|
|
||||||
function getLevelText(level?: number) {
|
|
||||||
const levelMap: Record<number, string> = {
|
|
||||||
1: '提示',
|
|
||||||
2: '一般',
|
|
||||||
3: '警告',
|
|
||||||
4: '严重',
|
|
||||||
5: '紧急',
|
|
||||||
};
|
|
||||||
return level ? levelMap[level] || `级别${level}` : '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取告警级别颜色
|
|
||||||
function getLevelColor(level?: number) {
|
|
||||||
const colorMap: Record<number, string> = {
|
|
||||||
1: 'blue',
|
|
||||||
2: 'green',
|
|
||||||
3: 'orange',
|
|
||||||
4: 'red',
|
|
||||||
5: 'purple',
|
|
||||||
};
|
|
||||||
return level ? colorMap[level] || 'default' : 'default';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取产品名称
|
|
||||||
function getProductName(productId?: number) {
|
|
||||||
if (!productId) return '-';
|
|
||||||
const product = productList.value.find((p: any) => p.id === productId);
|
|
||||||
return product?.name || '加载中...';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取设备名称
|
|
||||||
function getDeviceName(deviceId?: number) {
|
|
||||||
if (!deviceId) return '-';
|
|
||||||
const device = deviceList.value.find((d: any) => d.id === deviceId);
|
|
||||||
return device?.deviceName || '加载中...';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理告警记录
|
|
||||||
async function handleProcess(row: AlertRecord) {
|
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: '处理告警记录',
|
title: '处理告警记录',
|
||||||
content: h('div', [
|
content: () =>
|
||||||
h('p', '请输入处理原因:'),
|
h('div', { class: 'space-y-2' }, [
|
||||||
h('textarea', {
|
h('p', '请输入处理原因:'),
|
||||||
id: 'processRemark',
|
h(Input.TextArea, {
|
||||||
class: 'ant-input',
|
'value': processRemark.value,
|
||||||
rows: 3,
|
'onUpdate:value': (val: string) => (processRemark.value = val),
|
||||||
placeholder: '请输入处理原因',
|
'rows': 3,
|
||||||
}),
|
'placeholder': '请输入处理原因',
|
||||||
]),
|
}),
|
||||||
|
]),
|
||||||
async onOk() {
|
async onOk() {
|
||||||
const textarea = document.querySelector(
|
if (!processRemark.value) {
|
||||||
'#processRemark',
|
|
||||||
) as HTMLTextAreaElement;
|
|
||||||
const processRemark = textarea?.value || '';
|
|
||||||
|
|
||||||
if (!processRemark) {
|
|
||||||
message.warning('请输入处理原因');
|
message.warning('请输入处理原因');
|
||||||
throw new Error('请输入处理原因');
|
throw new Error('请输入处理原因');
|
||||||
}
|
}
|
||||||
|
|
||||||
const hideLoading = message.loading({
|
const hideLoading = message.loading({
|
||||||
content: '正在处理...',
|
content: '正在处理...',
|
||||||
duration: 0,
|
duration: 0,
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await processAlertRecord(row.id as number, processRemark);
|
await processAlertRecord(row.id as number, processRemark.value);
|
||||||
message.success('处理成功');
|
message.success('处理成功');
|
||||||
handleRefresh();
|
handleRefresh();
|
||||||
} catch (error) {
|
|
||||||
console.error('处理失败:', error);
|
|
||||||
throw error;
|
|
||||||
} finally {
|
} finally {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
}
|
}
|
||||||
|
|
@ -112,8 +59,12 @@ async function handleProcess(row: AlertRecord) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看告警记录详情
|
/** 查看告警记录详情 */
|
||||||
function handleView(row: AlertRecord) {
|
function handleView(row: AlertRecord) {
|
||||||
|
const deviceMessageText =
|
||||||
|
row.deviceMessage && typeof row.deviceMessage === 'object'
|
||||||
|
? JSON.stringify(row.deviceMessage, null, 2)
|
||||||
|
: row.deviceMessage || '-';
|
||||||
Modal.info({
|
Modal.info({
|
||||||
title: '告警记录详情',
|
title: '告警记录详情',
|
||||||
width: 600,
|
width: 600,
|
||||||
|
|
@ -124,14 +75,17 @@ function handleView(row: AlertRecord) {
|
||||||
]),
|
]),
|
||||||
h('div', [
|
h('div', [
|
||||||
h('span', { class: 'font-semibold' }, '告警级别:'),
|
h('span', { class: 'font-semibold' }, '告警级别:'),
|
||||||
h('span', getLevelText(row.configLevel)),
|
h(
|
||||||
|
'span',
|
||||||
|
getDictLabel(DICT_TYPE.IOT_ALERT_LEVEL, row.configLevel) || '-',
|
||||||
|
),
|
||||||
]),
|
]),
|
||||||
h('div', [
|
h('div', [
|
||||||
h('span', { class: 'font-semibold' }, '设备消息:'),
|
h('span', { class: 'font-semibold' }, '设备消息:'),
|
||||||
h(
|
h(
|
||||||
'pre',
|
'pre',
|
||||||
{ class: 'mt-1 text-xs bg-gray-50 p-2 rounded' },
|
{ class: 'mt-1 text-xs bg-gray-50 p-2 rounded' },
|
||||||
row.deviceMessage || '-',
|
deviceMessageText,
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
h('div', [
|
h('div', [
|
||||||
|
|
@ -181,32 +135,29 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||||
} as VxeTableGridOptions<AlertRecord>,
|
} as VxeTableGridOptions<AlertRecord>,
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
/** 把设备消息序列化成可读字符串 */
|
||||||
loadData();
|
function stringifyDeviceMessage(deviceMessage: any) {
|
||||||
});
|
if (!deviceMessage) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return typeof deviceMessage === 'object'
|
||||||
|
? JSON.stringify(deviceMessage, null, 2)
|
||||||
|
: String(deviceMessage);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page auto-content-height>
|
<Page auto-content-height>
|
||||||
<Grid table-title="告警记录列表">
|
<Grid table-title="告警记录列表">
|
||||||
<!-- 告警级别列 -->
|
<!-- 告警级别列 -->
|
||||||
|
<!-- TODO @AI:可以在 data 里渲染么?应该 antd 里有例子的; -->
|
||||||
<template #configLevel="{ row }">
|
<template #configLevel="{ row }">
|
||||||
<Tag :color="getLevelColor(row.configLevel)">
|
<Tag>
|
||||||
{{ getLevelText(row.configLevel) }}
|
{{ getDictLabel(DICT_TYPE.IOT_ALERT_LEVEL, row.configLevel) }}
|
||||||
</Tag>
|
</Tag>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 产品名称列 -->
|
|
||||||
<template #product="{ row }">
|
|
||||||
<span>{{ getProductName(row.productId) }}</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- 设备名称列 -->
|
|
||||||
<template #device="{ row }">
|
|
||||||
<span>{{ getDeviceName(row.deviceId) }}</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- 设备消息列 -->
|
<!-- 设备消息列 -->
|
||||||
|
<!-- TODO @AI:可以在 data 里渲染么?应该 antd 里有例子的; -->
|
||||||
<template #deviceMessage="{ row }">
|
<template #deviceMessage="{ row }">
|
||||||
<Popover
|
<Popover
|
||||||
v-if="row.deviceMessage"
|
v-if="row.deviceMessage"
|
||||||
|
|
@ -215,7 +166,7 @@ onMounted(() => {
|
||||||
:overlay-style="{ maxWidth: '600px' }"
|
:overlay-style="{ maxWidth: '600px' }"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<pre class="text-xs">{{ row.deviceMessage }}</pre>
|
<pre class="text-xs">{{ stringifyDeviceMessage(row.deviceMessage) }}</pre>
|
||||||
</template>
|
</template>
|
||||||
<Button size="small" type="link">
|
<Button size="small" type="link">
|
||||||
<IconifyIcon icon="ant-design:eye-outlined" class="mr-1" />
|
<IconifyIcon icon="ant-design:eye-outlined" class="mr-1" />
|
||||||
|
|
@ -224,7 +175,6 @@ onMounted(() => {
|
||||||
</Popover>
|
</Popover>
|
||||||
<span v-else class="text-gray-400">-</span>
|
<span v-else class="text-gray-400">-</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 操作列 -->
|
<!-- 操作列 -->
|
||||||
<template #actions="{ row }">
|
<template #actions="{ row }">
|
||||||
<TableAction
|
<TableAction
|
||||||
|
|
@ -233,6 +183,7 @@ onMounted(() => {
|
||||||
label: '处理',
|
label: '处理',
|
||||||
type: 'link',
|
type: 'link',
|
||||||
icon: ACTION_ICON.EDIT,
|
icon: ACTION_ICON.EDIT,
|
||||||
|
auth: ['iot:alert-record:process'],
|
||||||
onClick: handleProcess.bind(null, row),
|
onClick: handleProcess.bind(null, row),
|
||||||
ifShow: !row.processStatus,
|
ifShow: !row.processStatus,
|
||||||
},
|
},
|
||||||
|
|
@ -240,6 +191,7 @@ onMounted(() => {
|
||||||
label: '查看',
|
label: '查看',
|
||||||
type: 'link',
|
type: 'link',
|
||||||
icon: ACTION_ICON.VIEW,
|
icon: ACTION_ICON.VIEW,
|
||||||
|
auth: ['iot:alert-record:query'],
|
||||||
onClick: handleView.bind(null, row),
|
onClick: handleView.bind(null, row),
|
||||||
ifShow: row.processStatus,
|
ifShow: row.processStatus,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue