Compare commits

...

2 Commits

  1. 2
      .vscode/settings.json
  2. 2
      src/api/alert/model/models.ts
  3. 124
      src/views/model/list/step/Step3.vue
  4. 355
      src/views/model/train/index.vue

2
.vscode/settings.json

@ -120,7 +120,7 @@
"source.organizeImports": "never",
"source.fixAll.stylelint": "explicit"
},
"editor.defaultFormatter": "stylelint.vscode-stylelint"
"editor.defaultFormatter": "octref.vetur"
},
"i18n-ally.localesPaths": ["src/locales/lang"],
"i18n-ally.keystyle": "nested",

2
src/api/alert/model/models.ts

@ -9,6 +9,7 @@ enum Api {
CALCULATE_BACK = '/alert/model/data/calculate/',
OPTIMISTIC = '/alert/optimistic',
TRAIN_MODEL = '/alert/model/train',
TEST_MODEL = '/alert/model/test',
}
export function modelCardListApi(params?: ModelQueryParams) {
return defHttp.get<ModelCardItem[]>({ url: Api.MODEL_CARD_LIST, params })
@ -34,3 +35,4 @@ export function calculateBackApi(id: any, params: any) {
export const getOptimisticApi = (params: any) => defHttp.get<any>({ url: Api.OPTIMISTIC, params })
export const trainModelApi = (params: any) => defHttp.post<any>({ url: Api.TRAIN_MODEL, data: params })
export const testModelApi = (params: any) => defHttp.post<any>({ url: Api.TEST_MODEL, data: params })

124
src/views/model/list/step/Step3.vue

@ -1,14 +1,14 @@
<script lang="ts">
import type { PropType } from 'vue'
import { computed, defineComponent, ref, toRaw, unref } from 'vue'
import { Alert, Descriptions, Divider } from 'ant-design-vue'
import type { Recordable } from '@vben/types'
import { useDebounceFn } from '@vueuse/core'
import { step3Schemas } from './data'
import { ApiSelect, BasicForm, useForm } from '@/components/Form'
import { Button } from '@/components/Button'
import { pointListApi } from '@/api/alert/model/select'
import { modelSaveApi } from '@/api/alert/model/models'
import type { PropType } from "vue";
import { computed, defineComponent, ref, toRaw, unref } from "vue";
import { Alert, Descriptions, Divider } from "ant-design-vue";
import type { Recordable } from "@vben/types";
import { useDebounceFn } from "@vueuse/core";
import { step3Schemas } from "./data";
import { ApiSelect, BasicForm, useForm } from "@/components/Form";
import { Button } from "@/components/Button";
import { pointListApi } from "@/api/alert/model/select";
import { modelSaveApi } from "@/api/alert/model/models";
export default defineComponent({
components: {
@ -28,114 +28,118 @@ export default defineComponent({
type: Number,
},
},
emits: ['next', 'prev'],
emits: ["next", "prev"],
setup(props, { emit }) {
const [register, { appendSchemaByField, removeSchemaByField, validate, setProps }] = useForm({
const [
register,
{ appendSchemaByField, removeSchemaByField, validate, setProps },
] = useForm({
labelWidth: 100,
schemas: step3Schemas,
actionColOptions: {
span: 14,
},
resetButtonOptions: {
text: '上一步',
text: "上一步",
},
submitButtonOptions: {
text: '创建模型',
text: "创建模型",
},
resetFunc: customResetFunc,
submitFunc: customSubmitFunc,
})
const n = ref(4)
});
const n = ref(4);
function add() {
appendSchemaByField(
[
{
field: `point${n.value}`,
component: 'Input',
component: "Input",
label: `参数${n.value}`,
required: true,
slot: 'remoteSearch',
slot: "remoteSearch",
colProps: {
span: 23,
},
},
{
field: `${n.value}`,
component: 'Input',
label: ' ',
slot: 'add',
component: "Input",
label: " ",
slot: "add",
colProps: {
span: 1,
},
},
],
'',
)
n.value++
""
);
n.value++;
}
function del(field) {
removeSchemaByField([`point${field}`, `${field}`])
n.value--
removeSchemaByField([`point${field}`, `${field}`]);
n.value--;
}
async function customResetFunc() {
emit('prev')
emit("prev");
}
async function customSubmitFunc() {
try {
const values = await validate()
const pointInfo = []
const values = await validate();
const pointInfo = [];
for (const key in values) {
if (key.startsWith('point')) {
const point = values[key].split('|')
if (key.startsWith("point")) {
const point = values[key].split("|");
const p = {
description: point[0],
pointId: point[1],
unit: point[2],
Lower: point[3],
Upper: point[4],
}
pointInfo.push(p)
dead: true,
limit: false,
};
pointInfo.push(p);
}
}
const modelInfo = toRaw(props.beforeData)
modelInfo.pointInfo = pointInfo
const modelId = await modelSaveApi(modelInfo)
const modelInfo = toRaw(props.beforeData);
modelInfo.pointInfo = pointInfo;
const modelId = await modelSaveApi(modelInfo);
setProps({
submitButtonOptions: {
loading: true,
},
})
});
setTimeout(() => {
setProps({
submitButtonOptions: {
loading: false,
},
})
emit('next', modelId)
}, 1500)
}
catch (error) {
console.error(error)
});
emit("next", modelId);
}, 1500);
} catch (error) {
console.error(error);
}
}
const keyword = ref<string>('')
const keyword = ref<string>("");
const searchParams = computed<Recordable>(() => {
return { keyword: unref(keyword) }
})
return { keyword: unref(keyword) };
});
function onSearch(value: string) {
keyword.value = value
keyword.value = value;
}
async function searchPoint(params) {
if (params.keyword) {
const data = await pointListApi(params)
return data
const data = await pointListApi(params);
return data;
}
}
@ -147,11 +151,11 @@ export default defineComponent({
searchParams,
searchPoint,
handleReset: () => {
keyword.value = ''
keyword.value = "";
},
}
};
},
})
});
</script>
<template>
@ -190,20 +194,16 @@ export default defineComponent({
/>
</template>
<template #add="{ field }">
<Button v-if="Number(field) === 0" @click="add">
+
</Button>
<Button v-if="field > 0" @click="del(field)">
-
</Button>
<Button v-if="Number(field) === 0" @click="add"> + </Button>
<Button v-if="field > 0" @click="del(field)"> - </Button>
</template>
</BasicForm>
</div>
</template>
<style lang="less" scoped>
.step3 {
width: 450px;
margin: 0 auto;
}
.step3 {
width: 450px;
margin: 0 auto;
}
</style>

355
src/views/model/train/index.vue

@ -3,7 +3,7 @@ import type { ComponentPublicInstance } from 'vue'
import type { Dayjs } from 'dayjs'
import { debounce } from 'lodash-es'
import dayjs from 'dayjs'
import { computed, defineComponent, onMounted, ref, watch } from 'vue'
import { computed, defineComponent, onMounted, ref, toRaw, watch } from 'vue'
import { useRoute } from 'vue-router'
import {
Button,
@ -17,17 +17,23 @@ import {
InputNumber,
Modal,
RangePicker,
Spin,
Steps,
Tabs,
} from 'ant-design-vue'
import VueECharts from 'vue-echarts'
import { func } from 'vue-types'
import { pointTableSchema, sampleInfoTableSchema } from './data'
import { BasicTable, useTable } from '@/components/Table'
import { PageWrapper } from '@/components/Page'
import { modelInfoApi, trainModelApi, updateModelInfo } from '@/api/alert/model/models'
import {
modelInfoApi,
testModelApi,
trainModelApi,
updateModelInfo,
} from '@/api/alert/model/models'
import { getExaHistorys } from '@/api/alert/exa/index'
import { useECharts } from '@/hooks/web/useECharts'
import { useMessage } from '@/hooks/web/useMessage'
export default defineComponent({
components: {
@ -50,13 +56,15 @@ export default defineComponent({
ACheckbox: Checkbox,
AInputNumber: InputNumber,
AButton: Button,
ASpin: Spin,
},
setup() {
const { createMessage } = useMessage()
const route = useRoute()
const id = route.params.id
const model = ref(null)
const brushActivated = ref<Set<number>>(new Set())
const spinning = ref(false)
const fetchModelInfo = async () => {
const modelInfo = await modelInfoApi(id)
model.value = modelInfo
@ -83,7 +91,10 @@ export default defineComponent({
type RangeValue = [Dayjs, Dayjs]
const currentDate: Dayjs = dayjs()
const lastMonthDate: Dayjs = currentDate.subtract(1, 'day')
const rangeValue: RangeValue = [dayjs('2023-10-28 00:00:00'), dayjs('2023-10-28 23:59:59')]
const rangeValue: RangeValue = [
dayjs('2025-2-24 00:00:00'),
dayjs('2025-2-24 23:59:59'),
]
const historyTime = ref<RangeValue>(rangeValue)
const historyList = ref<any[]>([])
const echartsRefs = ref<any[]>([])
@ -91,33 +102,105 @@ export default defineComponent({
async function getHistory() {
if (!historyTime.value)
return
spinning.value = true
if (model.value.para) {
getTestData()
}
else {
const params = {
startTime: historyTime.value[0].format('YYYY-MM-DD HH:mm:ss'),
endTime: historyTime.value[1].format('YYYY-MM-DD HH:mm:ss'),
itemName: model.value?.pointInfo
.map(item => item.pointId)
.join(','),
interval: model.value.sampling,
}
console.log(params)
const history = await getExaHistorys(params)
historyList.value = history.map((item, index) => {
const point = model.value?.pointInfo[index]
return {
data: [item],
name: `${index + 1}.${point?.description}(${point?.pointId})`,
}
})
echartsRefs.value = Array.from({ length: historyList.value.length })
brushActivated.value = new Set()
}
spinning.value = false
}
async function getTestData() {
const params = {
startTime: historyTime.value[0].format('YYYY-MM-DD HH:mm:ss'),
endTime: historyTime.value[1].format('YYYY-MM-DD HH:mm:ss'),
itemName: model.value?.pointInfo.map(item => item.pointId).join(','),
Model_id: 118,
version: model.value?.Cur_Version ? model.value?.Cur_Version : 'v-test',
Test_Data: {
time: historyTime.value
.map(t => dayjs(t).format('YYYY-MM-DD HH:mm:ss'))
.join(','),
points: model.value.pointInfo.map(t => t.pointId).join(','),
interval: model.value.sampling * 1000,
},
}
console.log(params)
const history = await getExaHistorys(params)
historyList.value = history.map((item, index) => {
const result = await testModelApi(params)
const sampleData = result.sampleData
const reconData = result.reconData
const xData = generateTimeList(
historyTime.value,
model.value.sampling * 1000,
)
historyList.value = sampleData.map((item, index) => {
const point = model.value?.pointInfo[index]
return {
data: item,
name: `${index}.${point?.description}(${point?.pointId})`,
data: [
item.map((t, i) => {
return [xData[i], t]
}),
reconData[index].map((t, i) => {
return [xData[i], t]
}),
],
name: `${index + 1}.${point?.description}(${point?.pointId})`,
}
})
echartsRefs.value = Array.from({ length: historyList.value.length })
console.log('echartsRefs', echartsRefs.value)
brushActivated.value = new Set()
console.log(historyList.value)
}
function generateTimeList(time: RangeValue, intervalMs: number) {
const [t1, t2] = time
const count = Math.floor(t2.diff(t1, 'millisecond') / intervalMs) + 1
return Array.from({ length: count }, (_, i) =>
t1.add(i * intervalMs, 'millisecond').valueOf())
}
function getOption(item: any) {
console.log('getOption', item)
return {
const name = ['测量值', '模型值', '']
const color = ['blue', '#ff6f00', 'red']
const yIndex = [0, 0, 1]
const option = {
xAxis: {
type: 'time',
axisLabel: {
formatter(value) {
const date = new Date(value)
return date.toLocaleString()
},
},
},
yAxis: { type: 'value' },
series: [{ data: item.data, type: 'line', smooth: true, symbol: 'none' }],
yAxis: [{ type: 'value' }, { type: 'value', max: 10, show: false }],
series: item.data.map((item, index) => {
return {
data: item,
type: 'line',
smooth: true,
symbol: 'none',
name: name[index],
color: color[index],
yAxisIndex: yIndex[index],
}
}),
legend: {},
dataZoom: [{}],
brush: {
toolbox: ['lineX'],
@ -133,6 +216,8 @@ export default defineComponent({
},
],
}
console.log('option', option)
return option
}
function setChartRef(
@ -161,7 +246,6 @@ export default defineComponent({
const chart = echartsRefs.value[index]
if (!chart)
return
console.log('chart', index, chart)
chart.dispatchAction({
type: 'takeGlobalCursor',
key: 'brush',
@ -173,7 +257,10 @@ export default defineComponent({
const areas = (model.value?.trainTime || []).map(row => ({
brushType: 'lineX',
xAxisIndex: 0,
coordRange: [dayjs(row.st).valueOf(), dayjs(row.et).valueOf()],
coordRange: [
dayjs(row.st).format('YYYY-MM-DD HH:mm:ss'),
dayjs(row.et).format('YYYY-MM-DD HH:mm:ss'),
],
}))
chart.dispatchAction({
type: 'brush',
@ -191,7 +278,6 @@ export default defineComponent({
isInitBrush.value = false
return
}
console.log('brush selected:', params.batch)
const selected = params.batch[0].selected
if (selected.length > 0) {
const areas = mergeAreas(params.batch[0].areas).map(area => ({
@ -201,6 +287,7 @@ export default defineComponent({
}))
const trainTime = areas.map((area) => {
const [st, et] = area.coordRange
console.log('Selected area:', { st, et }, area)
return {
st: dayjs(st).format('YYYY-MM-DD HH:mm:ss'),
et: dayjs(et).format('YYYY-MM-DD HH:mm:ss'),
@ -220,6 +307,7 @@ export default defineComponent({
isInitBrush.value = true
}
})
updateModelInfoDebounced()
}
}, 300)
@ -257,23 +345,12 @@ export default defineComponent({
}
//
const updateModelInfoDebounced = debounce((val) => {
const updateModelInfoDebounced = debounce(() => {
const val = toRaw(model.value)
if (val && val.id)
updateModelInfo(val)
}, 500)
// model
watch(
model,
(newVal, oldVal) => {
console.log('model changed:', newVal, oldVal)
if (oldVal === null)
return
updateModelInfoDebounced(newVal)
},
{ deep: true },
)
function handleDeleteTrainTime(index: number) {
if (!model.value?.trainTime)
return
@ -297,6 +374,12 @@ export default defineComponent({
isInitBrush.value = true
}
})
updateModelInfoDebounced()
}
async function clearModel() {
model.value.para = null
updateModelInfoDebounced()
await getHistory()
}
async function trainModel() {
@ -311,24 +394,46 @@ export default defineComponent({
return
}
const params = {
condition: '1==1',
conditon: modelInfo.alarmmodelset?.alarmcondition || '1==1',
Hyper_para: {
percent: modelInfo.rate,
},
Train_Data: {
points: pointInfo.map(item => item.pointId).join(','),
dead: pointInfo.map(item => item.dead ? '1' : '0').join(','),
limit: pointInfo.map(item => item.limit ? '1' : '0').join(','),
uplow: pointInfo.map(item => `${item.Upper},${item.Lower}`).join(';'),
dead: pointInfo.map(item => (item.dead ? '1' : '0')).join(','),
limit: pointInfo.map(item => (item.limit ? '1' : '0')).join(','),
uplow: pointInfo
.map(
item =>
`${item.Upper ? item.Upper : null},${
item.Lower ? item.Lower : null
}`,
)
.join(';'),
interval: modelInfo.sampling * 1000,
time: modelInfo.trainTime.map(item => `${item.st},${item.et}`).join(';'),
time: modelInfo.trainTime
.map(item => `${item.st},${item.et}`)
.join(';'),
},
type: 'PCA',
smote_config: '[]',
smote_config: [],
smote: true,
}
const response = await trainModelApi(params)
console.log('模型训练结果:', response)
spinning.value = true
try {
const response = await trainModelApi(params)
model.value.para = response
model.value.principal = response.Model_info.K
updateModelInfoDebounced()
getTestData()
createMessage.success('模型训练成功')
}
catch (error) {
console.error('模型训练失败:', error)
createMessage.error('模型训练失败')
}
spinning.value = false
}
const editForm = ref({
index: -1,
@ -336,14 +441,13 @@ export default defineComponent({
Lower: '',
lowerBound: '',
upperBound: '',
dead: false,
limit: true,
dead: true,
limit: false,
})
const openEditPointModal = ref<boolean>(false)
let pointEditRecord = null
function editPoint() {
//
console.log('编辑点')
model.value.pointInfo[editForm.value.index] = {
...model.value.pointInfo[editForm.value.index],
Upper: editForm.value.Upper,
@ -353,6 +457,7 @@ export default defineComponent({
dead: editForm.value.dead,
limit: editForm.value.limit,
}
updateModelInfoDebounced()
pointEditRecord.Upper = editForm.value.Upper
pointEditRecord.Lower = editForm.value.Lower
pointEditRecord.lowerBound = editForm.value.lowerBound
@ -377,7 +482,7 @@ export default defineComponent({
}
const mode = ref({
alarmcondition: '1=1',
alarmcondition: '1==1',
alarmname: '全工况运行',
})
const openEditModeModal = ref<boolean>(false)
@ -385,7 +490,7 @@ export default defineComponent({
function openEditMode() {
openEditModeModal.value = true
mode.value = {
alarmcondition: model.value?.alarmmodelset?.alarmcondition || '1=1',
alarmcondition: model.value?.alarmmodelset?.alarmcondition || '1==1',
alarmname: model.value?.alarmmodelset?.alarmname || '全工况运行',
}
}
@ -394,10 +499,11 @@ export default defineComponent({
}
function handleEditMode() {
//
console.log('编辑模式')
model.value.alarmmodelset = mode.value
updateModelInfoDebounced()
closeEditMode()
}
return {
pointTable,
model,
@ -423,6 +529,8 @@ export default defineComponent({
closeEditMode,
handleEditMode,
mode,
spinning,
clearModel,
}
},
})
@ -443,10 +551,10 @@ export default defineComponent({
{{ model?.name }}
</a-descriptions-item>
<a-descriptions-item label="描述">
{{ model?.description || '暂无描述' }}
{{ model?.description || "暂无描述" }}
</a-descriptions-item>
<a-descriptions-item label="版本">
{{ model?.Cur_Version || 'v-test' }}
{{ model?.Cur_Version || "v-test" }}
</a-descriptions-item>
<a-descriptions-item label="评估报告">
暂无
@ -458,22 +566,22 @@ export default defineComponent({
{{ model?.creatTime }}
</a-descriptions-item>
<a-descriptions-item label="最近修改人">
{{ model?.Modifier || '暂无' }}
{{ model?.Modifier || "暂无" }}
</a-descriptions-item>
<a-descriptions-item label="最近修改时间">
{{ model?.modifiedTime || '暂无' }}
{{ model?.modifiedTime || "暂无" }}
</a-descriptions-item>
</a-descriptions>
<a-divider />
<a-descriptions size="small" :column="4" bordered>
<a-descriptions-item label="算法">
{{ model?.algorithm || 'PCA' }}
{{ model?.algorithm || "PCA" }}
</a-descriptions-item>
<a-descriptions-item label="训练采样间隔">
{{ model?.sampling }}
</a-descriptions-item>
<a-descriptions-item label="参数个数">
{{ model?.pointInfo.length || '暂无' }}
{{ model?.pointInfo.length || "暂无" }}
</a-descriptions-item>
<a-descriptions-item label="最小主元贡献率">
{{ model?.rate }}
@ -485,14 +593,25 @@ export default defineComponent({
{{ model?.precision }}
</a-descriptions-item>
<a-descriptions-item label="训练总时长(h)">
{{ (model?.trainTime.reduce((total, item) => total + item.duration, 0) / 3600).toFixed(2) || '暂无' }}
{{
(
model?.trainTime.reduce(
(total, item) => total + item.duration,
0,
) / 3600
).toFixed(2) || "暂无"
}}
</a-descriptions-item>
<a-descriptions-item label="有效样本数">
{{ model?.principal }}
</a-descriptions-item>
</a-descriptions>
</a-card>
<a-card title="模式" :bordered="false" style="margin-top: 16px;margin-bottom: -20px;">
<a-card
title="模式"
:bordered="false"
style="margin-top: 16px; margin-bottom: -20px"
>
<a-button size="large" @click="openEditMode">
{{ model?.alarmmodelset.alarmname }}
</a-button>
@ -503,7 +622,11 @@ export default defineComponent({
<a-tab-pane key="1" tab="训练采样时间">
<BasicTable @register="trainTimeTable">
<template #action="{ record, index }">
<a-button type="link" danger @click="handleDeleteTrainTime(index)">
<a-button
type="link"
danger
@click="handleDeleteTrainTime(index)"
>
删除
</a-button>
</template>
@ -522,46 +645,91 @@ export default defineComponent({
</a-card>
<a-card title="智能训练" :bordered="false">
<div style="display: flex; align-items: center;">
<a-form layout="inline" style="flex: 1;">
<div style="display: flex; align-items: center">
<a-form layout="inline" style="flex: 1">
<a-form-item label="模型预览时间范围">
<a-range-picker v-model:value="historyTime" show-time @change="getHistory" />
<a-range-picker
v-model:value="historyTime"
show-time
@change="getHistory"
/>
</a-form-item>
</a-form>
<a-button type="primary" style="margin-left: auto;" @click="trainModel">
<a-button
type="primary"
style="margin-left: auto"
@click="trainModel"
>
模型训练
</a-button>
<a-button
type="primary"
style="margin-left: 10px"
@click="clearModel"
>
清除训练结果
</a-button>
</div>
<a-divider />
<div v-for="(item, index) in historyList" :key="index" class="echart-box">
<a-card :bordered="false" style="margin-bottom: 16px;">
<template #title>
<span style="font-size: 20px;">{{ item.name }}</span>
</template>
<VueECharts
:ref="(el) => setEchartsRef(el, index)" :option="getOption(item)" autoresize
style="width: 100%; height: 300px" @finished="() => onChartFinished(index)"
@brush-selected="onBrushSelected"
/>
</a-card>
</div>
<a-spin :spinning="spinning" size="large">
<div
v-for="(item, index) in historyList"
:key="index"
class="echart-box"
style="width: 100%"
>
<a-card :bordered="false" style="margin-bottom: 16px">
<template #title>
<span style="font-size: 20px">{{ item.name }}</span>
</template>
<VueECharts
:ref="(el) => setEchartsRef(el, index)"
:option="getOption(item)"
autoresize
style="width: 100%; height: 200px"
@finished="() => onChartFinished(index)"
@brush-selected="onBrushSelected"
/>
</a-card>
</div>
</a-spin>
</a-card>
</div>
<a-modal v-model:open="openEditPointModal" title="更改上下限" @ok="editPoint">
<a-form :model="editForm" :label-col="{ span: 7 }" :wrapper-col="{ span: 15 }">
<a-modal
v-model:open="openEditPointModal"
title="更改上下限"
@ok="editPoint"
>
<a-form
:model="editForm"
:label-col="{ span: 7 }"
:wrapper-col="{ span: 15 }"
>
<a-form-item label="上限">
<a-input-number v-model:value="editForm.Upper" placeholder="请输入上限" />
<a-input-number
v-model:value="editForm.Upper"
placeholder="请输入上限"
/>
</a-form-item>
<a-form-item label="下限">
<a-input-number v-model:value="editForm.Lower" placeholder="请输入下限" />
<a-input-number
v-model:value="editForm.Lower"
placeholder="请输入下限"
/>
</a-form-item>
<a-form-item label="残差上限">
<a-input-number v-model:value="editForm.upperBound" placeholder="请输入残差上限" />
<a-input-number
v-model:value="editForm.upperBound"
placeholder="请输入残差上限"
/>
</a-form-item>
<a-form-item label="残差下限">
<a-input-number v-model:value="editForm.lowerBound" placeholder="请输入残差下限" />
<a-input-number
v-model:value="editForm.lowerBound"
placeholder="请输入残差下限"
/>
</a-form-item>
<a-form-item label="清洗方式" style="margin-top: 16px;">
<a-form-item label="清洗方式" style="margin-top: 16px">
<a-checkbox v-model:checked="editForm.dead">
死区清洗
</a-checkbox>
@ -571,13 +739,28 @@ export default defineComponent({
</a-form-item>
</a-form>
</a-modal>
<a-modal v-model:open="openEditModeModal" title="编辑模式" @ok="handleEditMode" @cancel="closeEditMode">
<a-form :model="mode" :label-col="{ span: 7 }" :wrapper-col="{ span: 15 }">
<a-modal
v-model:open="openEditModeModal"
title="编辑模式"
@ok="handleEditMode"
@cancel="closeEditMode"
>
<a-form
:model="mode"
:label-col="{ span: 7 }"
:wrapper-col="{ span: 15 }"
>
<a-form-item label="模式名称">
<a-input v-model:value="mode.alarmname" placeholder="请输入模式名称" />
<a-input
v-model:value="mode.alarmname"
placeholder="请输入模式名称"
/>
</a-form-item>
<a-form-item label="报警条件">
<a-input v-model:value="mode.alarmcondition" placeholder="请输入报警条件" />
<a-input
v-model:value="mode.alarmcondition"
placeholder="请输入报警条件"
/>
</a-form-item>
</a-form>
</a-modal>

Loading…
Cancel
Save