21 changed files with 9611 additions and 7313 deletions
@ -0,0 +1,11 @@ |
|||
{ |
|||
"name": "My Server", |
|||
"host": "120.26.116.243", |
|||
"protocol": "sftp", |
|||
"port": 22, |
|||
"username": "root", |
|||
"remotePath": "/", |
|||
"uploadOnSave": false, |
|||
"useTempFile": false, |
|||
"openSsh": false |
|||
} |
File diff suppressed because it is too large
@ -0,0 +1,75 @@ |
|||
import { integer } from 'vue-types' |
|||
import { defHttp } from '@/utils/http/axios' |
|||
|
|||
export interface EXAPageReqVO extends PageParam { |
|||
condition?: string |
|||
|
|||
} |
|||
|
|||
export interface EXAHistoryReqVO { |
|||
itemName?: string |
|||
startTime?: Date |
|||
endTime?: Date |
|||
|
|||
} |
|||
|
|||
export interface EXANowReqVO { |
|||
itemName?: string |
|||
|
|||
} |
|||
|
|||
export interface EXAPoint { |
|||
AssetCode?: string |
|||
ItemName?: string |
|||
GroupName?: string |
|||
ItemType?: number |
|||
Descriptor?: string |
|||
EngUnits?: string |
|||
Source?: string |
|||
AutoSave?: number |
|||
UpperBound?: number |
|||
LowerBound?: number |
|||
UpperLimit?: number |
|||
LowerLimit?: number |
|||
UpperUpperLimit?: number |
|||
LowerLowerLimit?: number |
|||
Comment?: string |
|||
Note?: string |
|||
} |
|||
// 查询测点列表
|
|||
export function getEXAPage(params: EXAPageReqVO) { |
|||
return defHttp.get({ url: '/alert/exa/page', params }) |
|||
} |
|||
|
|||
// 查询实时值列表
|
|||
export function getExaNowList(itemName: string) { |
|||
return defHttp.get({ url: `/alert/exa/nowList?itemName=${itemName}` }) |
|||
} |
|||
|
|||
export function getExaNow(itemName: string) { |
|||
return defHttp.get({ url: `/alert/exa/now?itemName=${itemName}` }) |
|||
} |
|||
|
|||
// 查询历史值-单点
|
|||
export function getExaHistory(params: EXAHistoryReqVO) { |
|||
return defHttp.get({ url: '/alert/exa/history', params }) |
|||
} |
|||
|
|||
// 查询历史值-多点
|
|||
export function getExaHistorys(params: EXAHistoryReqVO) { |
|||
return defHttp.get({ url: '/alert/exa/historys', params }) |
|||
} |
|||
|
|||
// 新增点
|
|||
export function createEXAPoint(data: EXAPoint) { |
|||
return defHttp.post({ url: '/alert/exa/create', data }) |
|||
} |
|||
|
|||
// 获取全部组名
|
|||
export function getGroup() { |
|||
return defHttp.get({ url: '/alert/exa/group' }) |
|||
} |
|||
// 删除点
|
|||
export function deletePoint(ItemName: string) { |
|||
return defHttp.delete({ url: `/alert/exa/delete?ItemName=${ItemName}` }) |
|||
} |
@ -0,0 +1,2 @@ |
|||
<template> |
|||
</template> |
@ -0,0 +1,114 @@ |
|||
<script lang="ts" setup> |
|||
import type { Ref } from 'vue' |
|||
import { onMounted, ref, watch } from 'vue' |
|||
import type { YAXisOption } from 'echarts/types/dist/shared' |
|||
import { useECharts } from '@/hooks/web/useECharts' |
|||
import { propTypes } from '@/utils/propTypes' |
|||
|
|||
const props = defineProps({ |
|||
height: { |
|||
type: String, |
|||
default: propTypes.string.def('500px'), |
|||
}, |
|||
name: { |
|||
type: Array<string>, |
|||
default: [], |
|||
}, |
|||
data: { |
|||
type: Array<Array<number>>, |
|||
default: () => [[]], |
|||
}, |
|||
width: propTypes.string.def('100%'), |
|||
// height: propTypes.string.def('70vh'), |
|||
}) |
|||
const chartRef = ref<HTMLDivElement | null>(null) |
|||
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>) |
|||
onMounted(() => { |
|||
|
|||
}) |
|||
|
|||
// const yAxis = reactive<yAxisForm>({ |
|||
// type: 'value', |
|||
// scale: true, // 开启自适应刻度 |
|||
|
|||
// }) |
|||
watch( |
|||
() => props.data, |
|||
() => { |
|||
console.log(props.data) |
|||
const yAxis = ref<YAXisOption[]>([]) |
|||
const series = ref<any[]>([]) |
|||
|
|||
for (let i = 0; i < props.data.length; i++) { |
|||
yAxis.value.push({ |
|||
type: 'value', |
|||
show: false, |
|||
scale: true, // 开启自适应刻度 |
|||
}) |
|||
series.value.push({ |
|||
name: props.name[i], |
|||
type: 'line', |
|||
smooth: true, |
|||
itemStyle: { |
|||
normal: { |
|||
lineStyle: { |
|||
width: 3, // 0.1的线条是非常细的了 |
|||
}, |
|||
}, |
|||
}, |
|||
showSymbol: false, |
|||
data: props.data[i], |
|||
yAxisIndex: i, |
|||
}) |
|||
} |
|||
|
|||
setOptions({ |
|||
tooltip: { |
|||
trigger: 'axis', |
|||
position(pt) { |
|||
return [pt[0], '10%'] |
|||
}, |
|||
}, |
|||
title: { |
|||
left: 'center', |
|||
text: 'Large Ara Chart', |
|||
show: false, |
|||
}, |
|||
toolbox: { |
|||
feature: { |
|||
dataZoom: { |
|||
yAxisIndex: 'none', |
|||
}, |
|||
restore: {}, |
|||
saveAsImage: {}, |
|||
}, |
|||
}, |
|||
xAxis: { |
|||
type: 'time', |
|||
// boundaryGap: false, |
|||
}, |
|||
yAxis: yAxis.value, |
|||
legend: { |
|||
|
|||
}, |
|||
dataZoom: [ |
|||
{ |
|||
type: 'inside', |
|||
start: 0, |
|||
end: 100, |
|||
}, |
|||
{ |
|||
start: 0, |
|||
end: 100, |
|||
}, |
|||
], |
|||
series: series.value, |
|||
}) |
|||
}, |
|||
{ immediate: true }, |
|||
) |
|||
</script> |
|||
|
|||
<template> |
|||
<div ref="chartRef" :style="{ width, height }" /> |
|||
</template> |
@ -0,0 +1,60 @@ |
|||
<script lang="ts" setup> |
|||
import { onBeforeMount, onMounted, ref, unref } from 'vue' |
|||
import { UploadDragger, message } from 'ant-design-vue' |
|||
import type { UploadChangeParam } from 'ant-design-vue' |
|||
import { InboxOutlined } from '@ant-design/icons-vue' |
|||
|
|||
import { useI18n } from '@/hooks/web/useI18n' |
|||
import { useMessage } from '@/hooks/web/useMessage' |
|||
import { BasicModal, useModalInner } from '@/components/Modal' |
|||
import { createEXAPoint, getGroup } from '@/api/alert/exa' |
|||
|
|||
const emit = defineEmits(['success', 'register']) |
|||
const { t } = useI18n() |
|||
const { createMessage } = useMessage() |
|||
|
|||
const [registerCreateBatchModal, { setModalProps, closeModal }] = useModalInner(async () => { |
|||
|
|||
}) |
|||
const fileList = ref([]) |
|||
function handleChange(info: UploadChangeParam) { |
|||
const status = info.file.status |
|||
if (status !== 'uploading') |
|||
console.log(info.file, info.fileList) |
|||
|
|||
if (status === 'done') |
|||
message.success(`${info.file.name} file uploaded successfully.`) |
|||
else if (status === 'error') |
|||
message.error(`${info.file.name} file upload failed.`) |
|||
} |
|||
function handleDrop(e: DragEvent) { |
|||
console.log(e) |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<BasicModal |
|||
v-bind="$attrs" :min-height="200" :title="t('action.createBatch')" @register="registerCreateBatchModal" |
|||
@ok="handleSubmit" |
|||
> |
|||
<UploadDragger |
|||
v-model:fileList="fileList" |
|||
name="file" |
|||
:multiple="true" |
|||
action="/user/import/" |
|||
@change="handleChange" |
|||
@drop="handleDrop" |
|||
> |
|||
<p class="ant-upload-drag-icon"> |
|||
<inbox-outlined /> |
|||
</p> |
|||
<p class="ant-upload-text"> |
|||
Click or drag file to this area to upload |
|||
</p> |
|||
<p class="ant-upload-hint"> |
|||
Support for a single or bulk upload. Strictly prohibit from uploading company data or other |
|||
band files |
|||
</p> |
|||
</UploadDragger> |
|||
</BasicModal> |
|||
</template> |
@ -0,0 +1,95 @@ |
|||
<script lang="ts" setup> |
|||
import { onBeforeMount, onMounted, ref, unref } from 'vue' |
|||
import { formSchema } from '../exa.data' |
|||
import { useI18n } from '@/hooks/web/useI18n' |
|||
import { useMessage } from '@/hooks/web/useMessage' |
|||
import { BasicForm, useForm } from '@/components/Form' |
|||
import { BasicModal, useModalInner } from '@/components/Modal' |
|||
import { createEXAPoint, getGroup } from '@/api/alert/exa' |
|||
|
|||
const emit = defineEmits(['success', 'register']) |
|||
const { t } = useI18n() |
|||
const { createMessage } = useMessage() |
|||
|
|||
// 组名 |
|||
const groupList = ref() |
|||
|
|||
// onMounted(() => { |
|||
// getGroupListSelect() |
|||
// }) |
|||
const [registerForm, { resetFields, validate, updateSchema }] = useForm({ |
|||
labelWidth: 120, |
|||
baseColProps: { span: 24 }, |
|||
schemas: formSchema, |
|||
showActionButtonGroup: false, |
|||
actionColOptions: { span: 23 }, |
|||
|
|||
}) |
|||
|
|||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async () => { |
|||
resetFields() |
|||
setModalProps({ confirmLoading: false }) |
|||
getGroupListSelect() |
|||
|
|||
// isUpdate.value = !!data?.isUpdate |
|||
// if (unref(isUpdate)) { |
|||
// const res = await getRole(data.record.id) |
|||
// setFieldsValue({ ...res }) |
|||
// } |
|||
}) |
|||
|
|||
async function getGroupListSelect() { |
|||
// 从后端接口获取值(后端返回数据必须是{label:'',value:''}形式) |
|||
const res = await getGroup() |
|||
const list = [] as any |
|||
// // 组名下拉框问题 |
|||
res.forEach((item) => { |
|||
list.push({ label: item.GroupName, value: item.GroupName }) |
|||
}) |
|||
|
|||
groupList.value = list |
|||
console.log(groupList.value) |
|||
// 将数据放入下拉框中 |
|||
updateSchema({ |
|||
field: 'GroupName', |
|||
componentProps: { |
|||
options: unref(groupList.value), |
|||
}, |
|||
}) |
|||
} |
|||
async function handleSubmit() { |
|||
try { |
|||
const values = await validate() |
|||
setModalProps({ confirmLoading: true }) |
|||
// const mergedObject = Object.assign({}, values, { AutoSave: 1 }) |
|||
const createResult = await createEXAPoint(values) |
|||
|
|||
closeModal() |
|||
if (createResult === '1') { |
|||
createMessage.success(t('common.successText')) |
|||
emit('success') |
|||
} |
|||
|
|||
else if (createResult === '-20') { |
|||
createMessage.info('点号已存在') |
|||
} |
|||
|
|||
else { |
|||
createMessage.error(t('common.errorText')) |
|||
} |
|||
} |
|||
|
|||
finally { |
|||
setModalProps({ confirmLoading: false }) |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<BasicModal |
|||
v-bind="$attrs" :min-height="200" :title="t('action.create')" @register="registerModal" |
|||
@ok="handleSubmit" |
|||
> |
|||
<BasicForm @register="registerForm" /> |
|||
</BasicModal> |
|||
</template> |
@ -0,0 +1,77 @@ |
|||
<script lang="ts" setup> |
|||
import { ref } from 'vue' |
|||
import moment from 'moment' |
|||
|
|||
import { Card } from 'ant-design-vue' |
|||
import { formHistory } from '../exa.data' |
|||
import EXAHistoryLine from '../EXAHistoryLine.vue' |
|||
|
|||
import { BasicModal, useModalInner } from '@/components/Modal' |
|||
import { BasicForm, useForm } from '@/components/Form' |
|||
|
|||
import { getExaHistorys } from '@/api/alert/exa' |
|||
|
|||
const loading = ref(true) |
|||
|
|||
const itemName = ref('') |
|||
|
|||
const [registerForm, { getFieldsValue }] = useForm({ |
|||
labelWidth: 100, |
|||
// baseColProps: { span: 24 }, |
|||
schemas: formHistory, |
|||
showResetButton: false, |
|||
layout: 'horizontal', |
|||
model: { time: [moment().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')] }, |
|||
fieldMapToTime: [ |
|||
// data为时间组件在表单内的字段,startTime,endTime为转化后的开始时间于结束时间 |
|||
['time', ['startTime', 'endTime'], 'YYYY-MM-DD HH:mm:ss'], |
|||
], |
|||
actionColOptions: { span: 2 }, |
|||
}) |
|||
const historyData = ref() |
|||
const legendName = ref<any[]>([]) |
|||
|
|||
const [registerHistoryModal, { setModalProps }] = useModalInner(async (data) => { |
|||
console.log(543) |
|||
setModalProps({ confirmLoading: false, showCancelBtn: false, showOkBtn: false }) |
|||
itemName.value = data.record.itemName |
|||
legendName.value = [] |
|||
legendName.value.push(`${data.record.descriptor}-${data.record.itemName}`) |
|||
handleSubmitR() |
|||
// 利用点号去获取历史数据,从而生成曲线 |
|||
// setFieldsValue({ time: [moment().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')] }) |
|||
// console.log(startTime, endTime, itemName) |
|||
// const historyData = getExaHistory({ |
|||
// startTime, |
|||
// endTime, |
|||
// itemName: itemName.value, |
|||
// }) |
|||
// isUpdate.value = !!data?.isUpdate |
|||
// if (unref(isUpdate)) { |
|||
// const res = await getUser(data.record.id) |
|||
// setFieldsValue({ ...res }) |
|||
// } |
|||
}) |
|||
|
|||
async function handleSubmitR() { |
|||
const serachFormData = getFieldsValue() |
|||
loading.value = true |
|||
const exaHistoryReqVO = { |
|||
startTime: serachFormData.startTime, |
|||
endTime: serachFormData.endTime, |
|||
itemName: itemName.value, |
|||
} |
|||
historyData.value = await getExaHistorys(exaHistoryReqVO) |
|||
|
|||
loading.value = false |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<BasicModal v-bind="$attrs" title="历史曲线" width="80%" @register="registerHistoryModal"> |
|||
<BasicForm @register="registerForm" @submit="handleSubmitR" /> |
|||
<Card :loading="loading"> |
|||
<EXAHistoryLine :data="historyData" :name="legendName" height="500px" /> |
|||
</Card> |
|||
</BasicModal> |
|||
</template> |
@ -0,0 +1,154 @@ |
|||
<script lang="ts" setup> |
|||
import { onMounted, onUnmounted } from 'vue' |
|||
import { columns, searchFormSchema } from '../exa.data' |
|||
import EXAModal from './EXAModal.vue' |
|||
import CreateBatchModal from './CreateBatchModal.vue' |
|||
|
|||
import HistoryModal from './HistoryModal.vue' |
|||
import { useI18n } from '@/hooks/web/useI18n' |
|||
import { useMessage } from '@/hooks/web/useMessage' |
|||
import { useModal } from '@/components/Modal' |
|||
import { IconEnum } from '@/enums/appEnum' |
|||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
|||
import type { RoleExportReqVO } from '@/api/system/role' |
|||
import { deletePoint, getEXAPage, getExaNow } from '@/api/alert/exa' |
|||
|
|||
defineOptions({ name: 'EXAConfig' }) |
|||
|
|||
const { t } = useI18n() |
|||
const { createConfirm, createMessage } = useMessage() |
|||
const [registerModal, { openModal }] = useModal() |
|||
const [registerCreateBatchModal, { openModal: openCreateBatchModal }] = useModal() |
|||
|
|||
const [registerHistoryModal, { openModal: openHistoryModal }] = useModal() |
|||
|
|||
const [registerTable, { getForm, reload, getDataSource, updateTableDataRecord }] = useTable({ |
|||
title: '测点列表', |
|||
api: getEXAPage, |
|||
rowKey: 'serialNumber', |
|||
immediate: false, |
|||
columns, |
|||
formConfig: { |
|||
labelWidth: 150, |
|||
schemas: searchFormSchema, |
|||
showResetButton: false, |
|||
actionColOptions: { |
|||
span: 2, |
|||
}, |
|||
}, |
|||
useSearchForm: true, |
|||
showTableSetting: true, |
|||
showIndexColumn: false, |
|||
actionColumn: { |
|||
width: 140, |
|||
title: t('common.action'), |
|||
dataIndex: 'action', |
|||
fixed: 'right', |
|||
}, |
|||
}) |
|||
// function handleQuery() { |
|||
// getForm().setFieldsValue({ condition: '锅炉' }) |
|||
// reload() |
|||
// } |
|||
|
|||
async function getNow() { |
|||
const params = getDataSource() |
|||
params.forEach((item: any) => { |
|||
// 对数组中的每个元素执行异步操作 |
|||
getExaNow(item.itemName).then((result) => { |
|||
updateTableDataRecord(item.serialNumber, Object.assign(item, { value: result })) |
|||
}) |
|||
}) |
|||
} |
|||
|
|||
let timerId |
|||
onMounted(() => { |
|||
// 启动定时器,每5秒刷新一次 |
|||
timerId = setInterval(() => { |
|||
getNow() |
|||
}, 5000) // 每5秒执行一次[^1] |
|||
}) |
|||
|
|||
onUnmounted(() => { |
|||
// 组件销毁时清除定时器 |
|||
clearInterval(timerId) |
|||
}) |
|||
function handleCreate() { |
|||
openModal(true, {}) |
|||
} |
|||
function handleCreateBatch() { |
|||
openCreateBatchModal(true, {}) |
|||
} |
|||
function handleHistory(record: Recordable) { |
|||
openHistoryModal(true, { record }) |
|||
} |
|||
async function handleExport() { |
|||
createConfirm({ |
|||
title: t('common.exportTitle'), |
|||
iconType: 'warning', |
|||
content: t('common.exportMessage'), |
|||
async onOk() { |
|||
await exportRole(getForm().getFieldsValue() as RoleExportReqVO) |
|||
createMessage.success(t('common.exportSuccessText')) |
|||
}, |
|||
}) |
|||
} |
|||
|
|||
async function handleDelete(record: Recordable) { |
|||
await deletePoint(record.itemName) |
|||
createMessage.success(t('common.delSuccessText')) |
|||
reload() |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<div> |
|||
<BasicTable @register="registerTable"> |
|||
<template #value="{ record }"> |
|||
<a class="click-status" @click="handleHistory(record)"> |
|||
{{ record.value }} |
|||
</a> |
|||
<!-- <SlidersOutlined class="click-status" /> --> |
|||
</template> |
|||
<template #toolbar> |
|||
<a-button v-auth="['system:role:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreate"> |
|||
{{ t('action.create') }} |
|||
</a-button> |
|||
<a-button v-auth="['system:role:create']" type="primary" :pre-icon="IconEnum.ADD" @click="handleCreateBatch"> |
|||
{{ t('action.createBatch') }} |
|||
</a-button> |
|||
<!-- <a-button v-auth="['system:role:create']" :pre-icon="IconEnum.EXPORT" @click="handleExport"> |
|||
{{ t('action.export') }} |
|||
</a-button> --> |
|||
</template> |
|||
<template #bodyCell="{ column, record }"> |
|||
<template v-if="column.key === 'action'"> |
|||
<TableAction |
|||
:actions="[{ |
|||
icon: IconEnum.DELETE, |
|||
danger: true, |
|||
label: t('action.delete'), |
|||
auth: 'alert:exa:delete', |
|||
popConfirm: { |
|||
title: t('common.delMessage'), |
|||
placement: 'left', |
|||
confirm: handleDelete.bind(null, record), |
|||
}, |
|||
}, |
|||
]" |
|||
/> |
|||
</template> |
|||
</template> |
|||
</BasicTable> |
|||
<EXAModal @register="registerModal" @success="reload" /> |
|||
<CreateBatchModal @register="registerCreateBatchModal" /> |
|||
<HistoryModal @register="registerHistoryModal" /> |
|||
</div> |
|||
</template> |
|||
|
|||
<style lang="less" scoped> |
|||
:deep(.value) { |
|||
font-weight:bold; |
|||
color: #0B55A4 |
|||
} |
|||
</style> |
@ -0,0 +1,128 @@ |
|||
import moment from 'moment' |
|||
import type { BasicColumn, FormSchema } from '@/components/Table' |
|||
import { DICT_TYPE, getDictOptions } from '@/utils/dict' |
|||
|
|||
export const columns: BasicColumn[] = [ |
|||
{ |
|||
title: '编号', |
|||
dataIndex: 'serialNumber', |
|||
width: 80, |
|||
}, |
|||
{ |
|||
title: '测点编码', |
|||
dataIndex: 'itemName', |
|||
width: 150, |
|||
}, |
|||
{ |
|||
title: '测点名称', |
|||
dataIndex: 'descriptor', |
|||
width: 200, |
|||
}, |
|||
{ |
|||
title: '实时值', |
|||
dataIndex: 'value', |
|||
width: 90, |
|||
className: 'value', |
|||
slots: { customRender: 'value' }, |
|||
|
|||
}, |
|||
{ |
|||
title: '单位', |
|||
dataIndex: 'engUnits', |
|||
width: 30, |
|||
}, |
|||
{ |
|||
title: '组别', |
|||
dataIndex: 'groupName', |
|||
width: 50, |
|||
}, |
|||
|
|||
] |
|||
|
|||
export const searchFormSchema: FormSchema[] = [ |
|||
{ |
|||
label: '测点名称或测点描述', |
|||
field: 'condition', |
|||
component: 'Input', |
|||
defaultValue: '', |
|||
required: true, |
|||
colProps: { span: 8 }, |
|||
}, |
|||
] |
|||
|
|||
export const formSchema: FormSchema[] = [ |
|||
|
|||
{ |
|||
label: '测点编码', |
|||
field: 'ItemName', |
|||
required: true, |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: '测点描述', |
|||
field: 'Descriptor', |
|||
required: true, |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: '单位', |
|||
field: 'EngUnits', |
|||
required: true, |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: '组名', |
|||
field: 'GroupName', |
|||
component: 'Select', |
|||
componentProps: { |
|||
options: getDictOptions(DICT_TYPE.COMMON_STATUS), |
|||
}, |
|||
required: true, |
|||
|
|||
}, |
|||
{ |
|||
label: '类型', |
|||
field: 'ItemType', |
|||
component: 'Select', |
|||
componentProps: { |
|||
options: [{ value: 1, label: 1 }, { value: 2, label: 2 }, { value: 3, label: 3 }, { value: 4, label: 4 }, { value: 5, label: 5 }], |
|||
}, |
|||
required: true, |
|||
|
|||
}, |
|||
{ |
|||
label: '自动保存', |
|||
field: 'AutoSave', |
|||
component: 'Select', |
|||
componentProps: { |
|||
options: [{ value: 1, label: '是' }, { value: 2, label: '否' }], |
|||
}, |
|||
required: true, |
|||
|
|||
}, |
|||
{ |
|||
label: '数据来源', |
|||
field: 'Source', |
|||
component: 'Input', |
|||
}, |
|||
] |
|||
|
|||
export const formHistory: FormSchema[] = [ |
|||
{ |
|||
label: '时间', |
|||
field: 'time', |
|||
show: true, |
|||
component: 'RangePicker', |
|||
componentProps: { |
|||
placeholder: ['开始时间', '结束时间'], |
|||
defaultValue: [moment().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')], |
|||
valueFormat: 'YYYY-MM-DD HH:mm:ss', |
|||
showTime: { |
|||
defaultValue: [moment().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')], |
|||
}, |
|||
onChange: (e: any) => { |
|||
console.log(e) |
|||
}, |
|||
}, |
|||
}, |
|||
] |
@ -0,0 +1,240 @@ |
|||
<script lang="ts" setup> |
|||
import { computed, reactive, ref, watch } from 'vue' |
|||
import { columns, searchFormSchema } from '../exa.data' |
|||
import type { TableActionType } from '@/components/Table' |
|||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
|||
import { getEXAPage } from '@/api/alert/exa' |
|||
import { useI18n } from '@/hooks/web/useI18n' |
|||
import { IconEnum } from '@/enums/appEnum' |
|||
import { BasicModal, useModalInner } from '@/components/Modal' |
|||
|
|||
const props = defineProps({ |
|||
selectedData: { type: Array<Recordable>, default: [] }, |
|||
selectedRowKeys: { type: Array<number>, default: [] }, |
|||
}) |
|||
const emit = defineEmits(['success', 'register']) |
|||
const [registerPointModal, { setModalProps, closeModal }] = useModalInner(async (data) => { |
|||
setModalProps({ confirmLoading: false }) |
|||
}) |
|||
const { t } = useI18n() |
|||
|
|||
const selectedData = ref<Recordable[]>(props.selectedData) |
|||
|
|||
interface RowKeys { |
|||
selectedRowKeys: number[] |
|||
} |
|||
const state = reactive<RowKeys>({ |
|||
selectedRowKeys: props.selectedRowKeys, |
|||
|
|||
}) |
|||
|
|||
const tableRef = ref<Nullable<TableActionType>>(null) |
|||
const tableRef1 = ref<Nullable<TableActionType>>(null) |
|||
|
|||
const [registerTable] = useTable({ |
|||
title: '测点列表', |
|||
api: getEXAPage, |
|||
immediate: false, |
|||
showTableSetting: false, |
|||
tableSetting: { |
|||
// 是否显示刷新按钮 |
|||
redo: false, |
|||
// 是否显示尺寸调整按钮 |
|||
size: false, |
|||
// 是否显示字段调整按钮 |
|||
setting: false, |
|||
// 是否显示全屏按 |
|||
fullScreen: false, |
|||
}, |
|||
columns, |
|||
formConfig: { |
|||
labelWidth: 150, |
|||
schemas: searchFormSchema, |
|||
showResetButton: false, |
|||
actionColOptions: { |
|||
span: 2, |
|||
}, |
|||
}, |
|||
rowKey: 'serialNumber', |
|||
useSearchForm: true, |
|||
showIndexColumn: false, |
|||
|
|||
}) |
|||
|
|||
// 这个必须写在computed计算属性里,心酸啊,弄一下午! |
|||
const rowSelection = computed(() => { |
|||
return { |
|||
preserveSelectedRowKeys: true, // 2.加这一行 |
|||
type: 'checkbox', |
|||
selectedRowKeys: state.selectedRowKeys, |
|||
onChange: onSelectChange, |
|||
|
|||
} |
|||
}) |
|||
|
|||
// defaultChecked: record.serialNumber === 93, |
|||
watch( |
|||
() => props.selectedRowKeys, |
|||
() => { |
|||
state.selectedRowKeys = props.selectedRowKeys |
|||
selectedData.value = props.selectedData |
|||
}, |
|||
) |
|||
// onBeforeMount(() => { |
|||
// console.log(props.selectedRowKeys) |
|||
// console.log(props.selectedData) |
|||
// state.selectedRowKeys = props.selectedRowKeys |
|||
// selectedData.value = props.selectedData |
|||
// console.log(state.selectedRowKeys) |
|||
// console.log(selectedData.value) |
|||
|
|||
// emit('success') |
|||
// }) |
|||
|
|||
// const historyData = ref() |
|||
// const legendName = ref<any[]>([]) |
|||
|
|||
// async function getHistoryChart() { |
|||
// const pointCodeList = selectedData.value.map(item => (item.itemName)) |
|||
// loading.value = true |
|||
|
|||
// if (pointCodeList.length !== 0) { |
|||
// const pointCode = selectedData.value.map(item => (item.itemName)).join(',') |
|||
// const pointDesc: any[] = selectedData.value.map(item => (item.descriptor)) |
|||
|
|||
// const exaHistoryReqVO = { |
|||
// startTime: searchForm.publishTime[0], |
|||
// endTime: searchForm.publishTime[1], |
|||
// itemName: pointCode, |
|||
// } |
|||
// historyData.value = await getExaHistorys(exaHistoryReqVO) |
|||
// legendName.value = pointDesc |
|||
// } |
|||
// else { |
|||
// message.info('暂无测点') |
|||
// } |
|||
// loading.value = false |
|||
// } |
|||
|
|||
const [registerSelectedTable] = useTable({ |
|||
title: '已选中测点列表', |
|||
showTableSetting: false, |
|||
tableSetting: { |
|||
// 是否显示刷新按钮 |
|||
redo: false, |
|||
// 是否显示尺寸调整按钮 |
|||
size: false, |
|||
// 是否显示字段调整按钮 |
|||
setting: false, |
|||
// 是否显示全屏按 |
|||
fullScreen: false, |
|||
}, |
|||
columns, |
|||
formConfig: { |
|||
labelWidth: 150, |
|||
schemas: searchFormSchema, |
|||
showResetButton: false, |
|||
actionColOptions: { |
|||
span: 2, |
|||
}, |
|||
}, |
|||
rowKey: 'serialNumber', |
|||
useSearchForm: false, |
|||
showIndexColumn: false, |
|||
actionColumn: { |
|||
width: 140, |
|||
title: t('common.action'), |
|||
dataIndex: 'action', |
|||
fixed: 'right', |
|||
}, |
|||
}) |
|||
|
|||
function onSelectChange(selectedRowKeys, selectedRows) { |
|||
console.log(selectedRowKeys, selectedRows) |
|||
state.selectedRowKeys = selectedRowKeys |
|||
let selectionRows: any[] = [] |
|||
console.log('3', selectedData.value) |
|||
|
|||
selectionRows = selectedData.value.concat(selectedRows) |
|||
console.log('2', selectionRows) |
|||
|
|||
selectionRows = selectionRows.filter(item => selectedRowKeys.includes(item.serialNumber)) |
|||
console.log('1', selectionRows) |
|||
// 数组对象去重 |
|||
const map = new Map() |
|||
for (const item of selectionRows) { |
|||
if (!map.has(item.serialNumber)) |
|||
map.set(item.serialNumber, item) |
|||
}; |
|||
selectedData.value = [...map.values()] |
|||
} |
|||
|
|||
function handleOk() { |
|||
console.log('onOk:') |
|||
localStorage.setItem('pointInfo', JSON.stringify(state.selectedRowKeys)) |
|||
localStorage.setItem('pointInfoList', JSON.stringify(selectedData.value)) |
|||
closeModal() |
|||
|
|||
emit('success') |
|||
} |
|||
async function handleDelete(record: Recordable) { |
|||
const newArr = state.selectedRowKeys.filter((value) => { |
|||
return value !== record.serialNumber |
|||
}) |
|||
state.selectedRowKeys = newArr |
|||
localStorage.setItem('pointInfo', JSON.stringify(state.selectedRowKeys)) |
|||
|
|||
const newArrObj = selectedData.value.filter((value) => { |
|||
return value.serialNumber !== record.serialNumber |
|||
}) |
|||
selectedData.value = newArrObj |
|||
} |
|||
function handleClearAndClose() { |
|||
state.selectedRowKeys = [] |
|||
selectedData.value = [] |
|||
localStorage.setItem('pointInfo', JSON.stringify(state.selectedRowKeys)) |
|||
localStorage.setItem('pointInfoList', JSON.stringify(selectedData.value)) |
|||
closeModal() |
|||
emit('success') |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<BasicModal v-bind="$attrs" title="测点配置" width="1000px" @register="registerPointModal" @ok="handleOk"> |
|||
<BasicTable |
|||
ref="tableRef" :can-resize="false" title="RefTable示例" title-help-message="使用Ref调用表格内方法" |
|||
:row-selection="rowSelection" size="small" @register="registerTable" |
|||
/> |
|||
<BasicTable |
|||
ref="tableRef1" :pagination="false" size="small" :data-source="selectedData" :can-resize="false" |
|||
title="已选中测点" title-help-message="使用Ref调用表格内方法" @register="registerSelectedTable" |
|||
> |
|||
<template #bodyCell="{ column, record }"> |
|||
<template v-if="column.key === 'action'"> |
|||
<TableAction |
|||
:actions="[{ |
|||
icon: IconEnum.DELETE, |
|||
danger: true, |
|||
label: t('action.delete'), |
|||
auth: 'system:role:delete', |
|||
popConfirm: { |
|||
title: t('common.delMessage'), |
|||
placement: 'left', |
|||
confirm: handleDelete.bind(null, record), |
|||
}, |
|||
}, |
|||
]" |
|||
/> |
|||
</template> |
|||
</template> |
|||
<template #toolbar> |
|||
<a-button v-auth="['system:role:create']" type="primary" :pre-icon="IconEnum.DELETE" @click="handleClearAndClose"> |
|||
{{ t('action.clearAllandClose') }} |
|||
</a-button> |
|||
<!-- <a-button v-auth="['system:role:create']" :pre-icon="IconEnum.EXPORT" @click="handleExport"> |
|||
{{ t('action.export') }} |
|||
</a-button> --> |
|||
</template> |
|||
</BasicTable> |
|||
</BasicModal> |
|||
</template> |
@ -0,0 +1,260 @@ |
|||
<script lang="ts" setup> |
|||
import { computed, onMounted, reactive, ref } from 'vue' |
|||
import type { Dayjs } from 'dayjs' |
|||
import moment from 'moment' |
|||
import { Button, Card, Form, FormItem, Modal, RangePicker, Space, message } from 'ant-design-vue' |
|||
import { columns, searchFormSchema } from '../exa.data' |
|||
import EXAHistoryLine from '../EXAHistoryLine.vue' |
|||
import type { TableActionType } from '@/components/Table' |
|||
import { BasicTable, TableAction, useTable } from '@/components/Table' |
|||
import { getEXAPage, getExaHistorys } from '@/api/alert/exa' |
|||
import { useI18n } from '@/hooks/web/useI18n' |
|||
import { IconEnum } from '@/enums/appEnum' |
|||
|
|||
const { t } = useI18n() |
|||
interface FormState { |
|||
publishTime: any[] |
|||
} |
|||
const searchForm = reactive<FormState>({ |
|||
publishTime: [], |
|||
}) |
|||
const loading = ref(true) |
|||
|
|||
function onRangeChange(value: [Dayjs, Dayjs], dateString: [string, string]) { |
|||
console.log('Selected Time: ', value) |
|||
console.log('Formatted Selected Time: ', dateString) |
|||
} |
|||
|
|||
function onRangeOk(value: [Dayjs, Dayjs]) { |
|||
console.log('onOk: ', value) |
|||
console.log(searchForm.publishTime) |
|||
searchForm.publishTime = value |
|||
} |
|||
|
|||
const open = ref<boolean>(false) |
|||
const selectedData = ref<Recordable[]>([]) |
|||
|
|||
interface RowKeys { |
|||
selectedRowKeys: number[] |
|||
} |
|||
const state = reactive<RowKeys>({ |
|||
selectedRowKeys: [], |
|||
|
|||
}) |
|||
|
|||
const tableRef = ref<Nullable<TableActionType>>(null) |
|||
const tableRef1 = ref<Nullable<TableActionType>>(null) |
|||
|
|||
const [registerTable] = useTable({ |
|||
title: '测点列表', |
|||
api: getEXAPage, |
|||
immediate: false, |
|||
showTableSetting: false, |
|||
tableSetting: { |
|||
// 是否显示刷新按钮 |
|||
redo: false, |
|||
// 是否显示尺寸调整按钮 |
|||
size: false, |
|||
// 是否显示字段调整按钮 |
|||
setting: false, |
|||
// 是否显示全屏按 |
|||
fullScreen: false, |
|||
}, |
|||
columns, |
|||
formConfig: { |
|||
labelWidth: 150, |
|||
schemas: searchFormSchema, |
|||
showResetButton: false, |
|||
actionColOptions: { |
|||
span: 2, |
|||
}, |
|||
}, |
|||
rowKey: 'serialNumber', |
|||
useSearchForm: true, |
|||
showIndexColumn: false, |
|||
|
|||
}) |
|||
|
|||
// 这个必须写在computed计算属性里,心酸啊,弄一下午! |
|||
const rowSelection = computed(() => { |
|||
return { |
|||
preserveSelectedRowKeys: true, // 2.加这一行 |
|||
type: 'checkbox', |
|||
selectedRowKeys: state.selectedRowKeys, |
|||
onChange: onSelectChange, |
|||
|
|||
} |
|||
}) |
|||
|
|||
// defaultChecked: record.serialNumber === 93, |
|||
|
|||
onMounted(() => { |
|||
state.selectedRowKeys = localStorage.getItem('pointInfo') ? JSON.parse(localStorage.getItem('pointInfo') || '') : [] |
|||
selectedData.value = localStorage.getItem('pointInfoList') ? JSON.parse(localStorage.getItem('pointInfoList') || '') : [] |
|||
searchForm.publishTime = [moment().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')] |
|||
getHistoryChart() |
|||
}) |
|||
|
|||
const historyData = ref() |
|||
const legendName = ref<any[]>([]) |
|||
|
|||
async function getHistoryChart() { |
|||
const pointCodeList = selectedData.value.map(item => (item.itemName)) |
|||
loading.value = true |
|||
|
|||
if (pointCodeList.length !== 0) { |
|||
const pointCode = selectedData.value.map(item => (item.itemName)).join(',') |
|||
const pointDesc: any[] = selectedData.value.map(item => (item.descriptor)) |
|||
|
|||
const exaHistoryReqVO = { |
|||
startTime: searchForm.publishTime[0], |
|||
endTime: searchForm.publishTime[1], |
|||
itemName: pointCode, |
|||
} |
|||
historyData.value = await getExaHistorys(exaHistoryReqVO) |
|||
legendName.value = pointDesc |
|||
} |
|||
else { |
|||
message.info('暂无测点') |
|||
} |
|||
loading.value = false |
|||
} |
|||
|
|||
const [registerSelectedTable] = useTable({ |
|||
title: '已选中测点列表', |
|||
showTableSetting: false, |
|||
tableSetting: { |
|||
// 是否显示刷新按钮 |
|||
redo: false, |
|||
// 是否显示尺寸调整按钮 |
|||
size: false, |
|||
// 是否显示字段调整按钮 |
|||
setting: false, |
|||
// 是否显示全屏按 |
|||
fullScreen: false, |
|||
}, |
|||
columns, |
|||
formConfig: { |
|||
labelWidth: 150, |
|||
schemas: searchFormSchema, |
|||
showResetButton: false, |
|||
actionColOptions: { |
|||
span: 2, |
|||
}, |
|||
}, |
|||
rowKey: 'serialNumber', |
|||
useSearchForm: false, |
|||
showIndexColumn: false, |
|||
actionColumn: { |
|||
width: 140, |
|||
title: t('common.action'), |
|||
dataIndex: 'action', |
|||
fixed: 'right', |
|||
}, |
|||
}) |
|||
function openModal() { |
|||
open.value = true |
|||
console.log(24) |
|||
console.log(selectedData.value) |
|||
} |
|||
function onSelectChange(selectedRowKeys, selectedRows) { |
|||
console.log(selectedRowKeys, selectedRows) |
|||
state.selectedRowKeys = selectedRowKeys |
|||
let selectionRows: any[] = [] |
|||
console.log('3', selectedData.value) |
|||
|
|||
selectionRows = selectedData.value.concat(selectedRows) |
|||
console.log('2', selectionRows) |
|||
|
|||
selectionRows = selectionRows.filter(item => selectedRowKeys.includes(item.serialNumber)) |
|||
console.log('1', selectionRows) |
|||
// 数组对象去重 |
|||
const map = new Map() |
|||
for (const item of selectionRows) { |
|||
if (!map.has(item.serialNumber)) |
|||
map.set(item.serialNumber, item) |
|||
}; |
|||
selectedData.value = [...map.values()] |
|||
} |
|||
|
|||
function handleOk() { |
|||
console.log('onOk:') |
|||
localStorage.setItem('pointInfo', JSON.stringify(state.selectedRowKeys)) |
|||
localStorage.setItem('pointInfoList', JSON.stringify(selectedData.value)) |
|||
open.value = false |
|||
getHistoryChart() |
|||
} |
|||
async function handleDelete(record: Recordable) { |
|||
const newArr = state.selectedRowKeys.filter((value) => { |
|||
return value !== record.serialNumber |
|||
}) |
|||
state.selectedRowKeys = newArr |
|||
localStorage.setItem('pointInfo', JSON.stringify(state.selectedRowKeys)) |
|||
|
|||
const newArrObj = selectedData.value.filter((value) => { |
|||
return value.serialNumber !== record.serialNumber |
|||
}) |
|||
selectedData.value = newArrObj |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<div> |
|||
<Card> |
|||
<Form layout="inline" :model="searchForm"> |
|||
<FormItem label="时间范围"> |
|||
<RangePicker |
|||
:model="searchForm.publishTime" |
|||
:show-time="{ format: 'HH:mm:ss' }" |
|||
:placeholder="['开始时间', '结束时间']" :default-value="[moment().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')]" |
|||
value-format="YYYY-MM-DD HH:mm:ss" |
|||
@change="onRangeChange" |
|||
@ok="onRangeOk" |
|||
/> |
|||
</FormItem> |
|||
<FormItem> |
|||
<Space wrap> |
|||
<Button type="primary" @click="getHistoryChart"> |
|||
查看趋势 |
|||
</Button> |
|||
<Button @click="openModal()"> |
|||
测点配置 |
|||
</Button> |
|||
</Space> |
|||
</FormItem> |
|||
</Form> |
|||
</Card> |
|||
<Card style="margin:5px" :loading="loading"> |
|||
<EXAHistoryLine :data="historyData" :name="legendName" height="70vh" /> |
|||
</Card> |
|||
<Modal v-model:open="open" :destroy-on-close="false" width="1000px" title="测点配置" @ok="handleOk"> |
|||
<BasicTable |
|||
ref="tableRef" :can-resize="false" title="RefTable示例" title-help-message="使用Ref调用表格内方法" |
|||
:row-selection="rowSelection" size="small" @register="registerTable" |
|||
/> |
|||
<BasicTable |
|||
ref="tableRef1" :pagination="false" size="small" :data-source="selectedData" :can-resize="false" |
|||
title="已选中测点" title-help-message="使用Ref调用表格内方法" @register="registerSelectedTable" |
|||
> |
|||
<template #bodyCell="{ column, record }"> |
|||
<template v-if="column.key === 'action'"> |
|||
<TableAction |
|||
:actions="[{ |
|||
icon: IconEnum.DELETE, |
|||
danger: true, |
|||
label: t('action.delete'), |
|||
auth: 'system:role:delete', |
|||
popConfirm: { |
|||
title: t('common.delMessage'), |
|||
placement: 'left', |
|||
confirm: handleDelete.bind(null, record), |
|||
}, |
|||
}, |
|||
]" |
|||
/> |
|||
</template> |
|||
</template> |
|||
</BasicTable> |
|||
</Modal> |
|||
</div> |
|||
</template> |
@ -0,0 +1,105 @@ |
|||
<script lang="ts" setup> |
|||
import { onMounted, reactive, ref } from 'vue' |
|||
import type { Dayjs } from 'dayjs' |
|||
import moment from 'moment' |
|||
import { Button, Card, Form, FormItem, RangePicker, Space, message } from 'ant-design-vue' |
|||
import EXAHistoryLine from '../EXAHistoryLine.vue' |
|||
import PointModal from './PointModal.vue' |
|||
import { getExaHistorys } from '@/api/alert/exa' |
|||
import { useModal } from '@/components/Modal' |
|||
|
|||
interface FormState { |
|||
publishTime: any[] |
|||
} |
|||
const searchForm = reactive<FormState>({ |
|||
publishTime: [], |
|||
}) |
|||
const loading = ref(true) |
|||
|
|||
function onRangeChange(value: [Dayjs, Dayjs], dateString: [string, string]) { |
|||
console.log('Selected Time: ', value) |
|||
console.log('Formatted Selected Time: ', dateString) |
|||
} |
|||
|
|||
function onRangeOk(value: [Dayjs, Dayjs]) { |
|||
console.log('onOk: ', value) |
|||
console.log(searchForm.publishTime) |
|||
searchForm.publishTime = value |
|||
} |
|||
|
|||
const selectedData = ref<Recordable[]>([]) |
|||
interface RowKeys { |
|||
selectedRowKeys: number[] |
|||
} |
|||
const state = reactive<RowKeys>({ |
|||
selectedRowKeys: [], |
|||
}) |
|||
onMounted(() => { |
|||
getHistoryChart() |
|||
}) |
|||
|
|||
const historyData = ref() |
|||
const legendName = ref<any[]>([]) |
|||
|
|||
async function getHistoryChart() { |
|||
state.selectedRowKeys = localStorage.getItem('pointInfo') ? JSON.parse(localStorage.getItem('pointInfo') || '') : [] |
|||
selectedData.value = localStorage.getItem('pointInfoList') ? JSON.parse(localStorage.getItem('pointInfoList') || '') : [] |
|||
searchForm.publishTime = [moment().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')] |
|||
|
|||
const pointCodeList = selectedData.value.map(item => (item.itemName)) |
|||
loading.value = true |
|||
|
|||
if (pointCodeList.length === 0) |
|||
message.info('暂无测点') |
|||
|
|||
const pointCode = selectedData.value.map(item => (item.itemName)).join(',') |
|||
const pointDesc: any[] = selectedData.value.map(item => (item.descriptor)) |
|||
|
|||
const exaHistoryReqVO = { |
|||
startTime: searchForm.publishTime[0], |
|||
endTime: searchForm.publishTime[1], |
|||
itemName: pointCode, |
|||
} |
|||
historyData.value = await getExaHistorys(exaHistoryReqVO) |
|||
legendName.value = pointDesc |
|||
loading.value = false |
|||
} |
|||
|
|||
const [registerPointModal, { openModal: openPointModal }] = useModal() |
|||
function handlePointModal() { |
|||
openPointModal(true) |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<div> |
|||
<Card> |
|||
<Form layout="inline" :model="searchForm"> |
|||
<FormItem label="时间范围"> |
|||
<RangePicker |
|||
:model="searchForm.publishTime" |
|||
:show-time="{ format: 'HH:mm:ss' }" |
|||
:placeholder="['开始时间', '结束时间']" :default-value="[moment().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss')]" |
|||
value-format="YYYY-MM-DD HH:mm:ss" |
|||
@change="onRangeChange" |
|||
@ok="onRangeOk" |
|||
/> |
|||
</FormItem> |
|||
<FormItem> |
|||
<Space wrap> |
|||
<Button type="primary" @click="getHistoryChart"> |
|||
查看趋势 |
|||
</Button> |
|||
<Button @click="handlePointModal"> |
|||
测点配置 |
|||
</Button> |
|||
</Space> |
|||
</FormItem> |
|||
</Form> |
|||
</Card> |
|||
<Card style="margin:5px" :loading="loading"> |
|||
<EXAHistoryLine :data="historyData" :name="legendName" height="70vh" /> |
|||
</Card> |
|||
<PointModal :selected-row-keys="state.selectedRowKeys" :selected-data="selectedData" @register="registerPointModal" @success="getHistoryChart" /> |
|||
</div> |
|||
</template> |
Loading…
Reference in new issue