Sfoglia il codice sorgente

业务菜单重新配置,同文件也更改

like 6 mesi fa
parent
commit
ae30cc0a44

+ 454 - 0
snowy-admin-web/src/views/basicset/mem/form copy.vue

@@ -0,0 +1,454 @@
+<template>
+	<xn-form-container
+		:title="formData.id ? '编辑采集器' : '新增采集器'"
+		:width="'60%'"
+		:visible="visible"
+		:destroy-on-close="true"
+		@close="onClose"
+	>
+		<a-form
+			ref="formRef"
+			:model="formData"
+			:rules="formRules"
+			layout="inline"
+			:label-col="{ style: { width: '100px', justifyContent: 'end' } }"
+		>
+			<a-tabs v-model:activeKey="activeKey" type="editable-card" @edit="onEdit">
+				<a-tab-pane tab="对象信息" key="object" :closable="false">
+					<a-row :gutter="10">
+						<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+							<a-form-item label="采集器名称" name="name">
+								<a-input v-model:value="formData.name" placeholder="请输入采集器名称" allow-clear />
+							</a-form-item>
+						</a-col>
+
+						<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+							<a-form-item label="采集器编号" name="code">
+								<a-input v-model:value="formData.code" placeholder="请输入采集器编号" allow-clear />
+							</a-form-item>
+						</a-col>
+						<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+							<a-form-item label="采集器条码" name="barcode">
+								<a-input v-model:value="formData.barcode" placeholder="请输入采集器条码" allow-clear />
+							</a-form-item>
+						</a-col>
+						<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+							<a-form-item label="品牌" name="brand">
+								<a-input v-model:value="formData.brand" placeholder="请输入品牌" allow-clear />
+							</a-form-item>
+						</a-col>
+
+						<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+							<a-form-item label="状态" name="state">
+								<a-select ref="select" v-model:value="formData.state">
+									<a-select-option value="1">正常</a-select-option>
+									<a-select-option value="2">维修</a-select-option>
+									<a-select-option value="3">闲置</a-select-option>
+									<a-select-option value="4">报废</a-select-option>
+								</a-select>
+							</a-form-item>
+						</a-col>
+						<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+							<a-form-item label="所属部门">
+								<a-input v-model:value="formData.division" placeholder="请输入所属部门" allow-clear />
+							</a-form-item>
+						</a-col>
+						<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="12">
+							<a-form-item label="采集时间">
+								<a-time-range-picker v-model:value="formData.collectionTime" style="width: 100%" />
+							</a-form-item>
+						</a-col>
+						<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+							<a-form-item label="位置">
+								<a-input v-model:value="formData.location" placeholder="请输入位置" allow-clear />
+							</a-form-item>
+						</a-col>
+						<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+							<a-form-item label="备注">
+								<a-textarea
+									v-model:value="formData.remarks"
+									placeholder="请输入备注"
+									allow-clear
+									:maxlength="200"
+									:rows="4"
+								/>
+							</a-form-item>
+						</a-col>
+					</a-row>
+				</a-tab-pane>
+				<a-tab-pane tab="报警信息" key="report" :closable="false">
+					<a-form-item :label-col="{ style: { width: '200px' } }">
+						<template #label>
+							<a-tooltip>
+								<template #title>
+									重复通知间隔:同一设备同一故障类型出现报警进行过一次报警通知后,进行下一次报普通知的间隔时间,如果在这个时间间隔内该设备同一故障类型
+									还在产生报警则不再进行通知。
+									最多报警次数:同一设备同一故障类型连续报警最多报警通知次数,在达到最多报警次数后如果报警还未及时处理,系统也不再进行报警通知。
+									延时时间:为屏蔽误报,系统报警在达到延时时间之后才进行报警通知。
+								</template>
+								<question-circle-outlined />
+							</a-tooltip>
+							重复发送通知:
+						</template>
+
+						<a-radio-group v-model:value="formData.repeat">
+							<a-radio value="1">1次</a-radio>
+							<a-radio value="2">多次</a-radio>
+						</a-radio-group>
+					</a-form-item>
+					<a-form-item label="延时报警" :label-col="{ style: { width: '200px' } }">
+						<a-radio-group v-model:value="formData.delayed">
+							<a-radio value="1">是</a-radio>
+							<a-radio value="0">否</a-radio>
+						</a-radio-group>
+					</a-form-item>
+					<a-form-item label="报警关闭发送通知" :label-col="{ style: { width: '200px' } }">
+						<a-radio-group v-model:value="formData.close">
+							<a-radio value="1">是</a-radio>
+							<a-radio value="0">否</a-radio>
+						</a-radio-group>
+					</a-form-item>
+					<a-form-item label="多级报警" :label-col="{ style: { width: '200px' } }">
+						<a-radio-group v-model:value="formData.multistage">
+							<a-radio value="1">是</a-radio>
+							<a-radio value="0">否</a-radio>
+						</a-radio-group>
+					</a-form-item>
+
+					<a-form-item label="报警通知方式" :label-col="{ style: { width: '200px' } }">
+						<a-checkbox-group v-model:value="formData.noticeway" style="width: 100%">
+							<a-checkbox value="1">短信</a-checkbox>
+							<a-checkbox value="2">电话</a-checkbox>
+							<a-checkbox value="3">微信</a-checkbox>
+							<a-checkbox value="4">电子邮件</a-checkbox>
+							<a-checkbox value="5">弹出</a-checkbox>
+							<a-checkbox value="6">音乐</a-checkbox>
+						</a-checkbox-group>
+					</a-form-item>
+
+					<!-- 表格区域 -->
+					<div>
+						<a-button type="primary" style="margin-left: 90%; margin-bottom: 5px" @click="tableAdd">
+							<template #icon><plus-outlined /></template>新增
+						</a-button>
+						<a-table
+							ref="tableRef"
+							bordered
+							:columns="columns"
+							:data-source="formData.loadData"
+							:row-key="(record) => record.startTime"
+							:pagination="false"
+						>
+							<template #headerCell="{ column }">
+								<template v-if="column.dataIndex === 'startTime'">
+									<text style="color: #e74032"> * </text>开始时间
+								</template>
+								<template v-if="column.dataIndex === 'endTime'">
+									<text style="color: #e74032"> * </text>结束时间
+								</template>
+								<template v-if="column.dataIndex === 'name'"> <text style="color: #e74032"> * </text>姓名 </template>
+								<template v-if="column.dataIndex === 'remindType'">
+									<text style="color: #e74032"> * </text>提醒方式
+								</template>
+								<template v-if="column.dataIndex === 'level'"> <text style="color: #e74032"> * </text>级别 </template>
+								<template v-if="column.dataIndex === 'phone'">
+									<text style="color: #e74032"> * </text>电话号码
+								</template>
+							</template>
+							<template #bodyCell="{ column, record, index }">
+								<!-- 表格内容 -->
+								<template v-if="column.dataIndex === 'startTime'">
+									<a-time-picker v-model:value="record.startTime" value-format="HH:mm:ss" style="width: 100%" />
+								</template>
+
+								<template v-if="column.dataIndex === 'endTime'">
+									<a-time-picker v-model:value="record.endTime" value-format="HH:mm:ss" style="width: 100%" />
+								</template>
+
+								<template v-if="column.dataIndex === 'name'">
+									<a-input v-model:value="record.name" placeholder="请输入姓名" />
+								</template>
+
+								<template v-if="column.dataIndex === 'remindType'">
+									<a-select v-model:value="record.remindType" placeholder="请选择提醒方式" style="width: 100%">
+										<a-select-option value="1">短信报警</a-select-option>
+										<a-select-option value="2">电话报警</a-select-option>
+										<a-select-option value="3">微信公众号报警</a-select-option>
+										<a-select-option value="4">邮件报警</a-select-option>
+										<a-select-option value="5">弹出报警</a-select-option>
+										<a-select-option value="6">音乐报警</a-select-option>
+									</a-select>
+								</template>
+
+								<template v-if="column.dataIndex === 'level'">
+									<a-select v-model:value="record.level" placeholder="请选择级别" style="width: 100px">
+										<a-select-option value="1">1</a-select-option>
+										<a-select-option value="2">2</a-select-option>
+										<a-select-option value="3">3</a-select-option>
+										<a-select-option value="4">4</a-select-option>
+										<a-select-option value="5">5</a-select-option>
+									</a-select>
+								</template>
+
+								<template v-if="column.dataIndex === 'phone'">
+									<a-input v-model:value="record.phone" placeholder="请输入电话号码" />
+								</template>
+
+								<template v-if="column.dataIndex === 'action'">
+									<a-popconfirm title="确定要删除吗?" @confirm="tableDelete(record, index)">
+										<a-button type="link" danger size="small">删除</a-button>
+									</a-popconfirm>
+								</template>
+							</template>
+						</a-table>
+					</div>
+				</a-tab-pane>
+
+				<a-tab-pane v-for="pane in panes" :key="pane.key" :tab="pane.title" :closable="pane.closable">
+					<!-- 新增路数-->
+					<div v-for="(item, index) in formData.list" :key="index">
+						<a-row :gutter="10" v-if="activeKey == item.key">
+							<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="12">
+								<a-form-item label="传感器路数">
+									<a-input v-model:value="item.number" placeholder="请输入传感器路数" allow-clear />
+								</a-form-item>
+							</a-col>
+
+							<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="12">
+								<a-form-item label="传感器类型">
+									<a-select ref="select" v-model:value="item.sensorType" placeholder="请选择传感器类型">
+										<a-select-option value="1">温度</a-select-option>
+										<a-select-option value="2">温湿度</a-select-option>
+									</a-select>
+								</a-form-item>
+							</a-col>
+
+							<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+								<a-form-item label="备注">
+									<a-textarea
+										v-model:value="item.numberRemarks"
+										placeholder="请输入备注"
+										allow-clear
+										:maxlength="200"
+										:rows="4"
+									/>
+								</a-form-item>
+							</a-col>
+						</a-row>
+					</div>
+				</a-tab-pane>
+			</a-tabs>
+		</a-form>
+
+		<template #footer>
+			<a-button class="xn-mr8" @click="onClose">退出</a-button>
+			<a-button type="primary" @click="onSubmit">保存</a-button>
+		</template>
+	</xn-form-container>
+</template>
+
+<script setup>
+	import { message } from 'ant-design-vue'
+	import { required } from '@/utils/formRules'
+	import moduleApi from '@/api/sys/resource/moduleApi'
+	// 默认是关闭状态
+	const visible = ref(false)
+	const emit = defineEmits({ successful: null })
+	const formRef = ref()
+
+	const tableRef = ref()
+
+	const columns = [
+		{
+			title: '开始时间',
+			dataIndex: 'startTime',
+			align: 'center'
+		},
+		{
+			title: '结束时间',
+			dataIndex: 'endTime',
+			align: 'center'
+		},
+		{
+			title: '姓名',
+			dataIndex: 'name',
+			align: 'center'
+		},
+		{
+			title: '提醒方式',
+			dataIndex: 'remindType',
+			align: 'center'
+		},
+		{
+			title: '级别',
+			dataIndex: 'level',
+			align: 'center'
+		},
+		{
+			title: '电话号码',
+			dataIndex: 'phone',
+			align: 'center'
+		},
+		{
+			title: '操作',
+			dataIndex: 'action',
+			align: 'center',
+			scopedSlots: { customRender: 'action' }
+		}
+	]
+	// 表单数据
+	const formData = ref({
+		repeat: '1',
+		delayed: '0',
+		close: '1',
+		multistage: '0',
+		noticeway: ['1'],
+		loadData: [],
+		list: []
+	})
+
+	// 打开抽屉
+	const onOpen = (record) => {
+		visible.value = true
+		activeKey.value = 'object'
+		panes.value = []
+		newTabIndex.value = 1
+		if (record) {
+			formData.value = Object.assign({}, record)
+		} else {
+			formData.value = {
+				repeat: '1',
+				delayed: '0',
+				close: '1',
+				multistage: '0',
+				noticeway: ['1'],
+				loadData: [],
+				list: []
+			}
+		}
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		formRef.value.resetFields()
+		visible.value = false
+	}
+
+	// 默认要校验的
+	const formRules = {
+		name: [required('请输入采集器名称')],
+		code: [required('请输入采集器编号')]
+	}
+
+	// 表格添加一行空数据
+	const tableAdd = () => {
+		const newRow = {
+			startTime: '00:00:00',
+			endTime: '23:59:59',
+			name: '',
+			level: '',
+			phone: ''
+		}
+		formData.value.loadData.push(newRow)
+	}
+
+	// 删除某一行
+	const tableDelete = (record, index) => {
+		formData.value.loadData.splice(index, 1)
+	}
+
+	// 验证并提交数据
+	const onSubmit = () => {
+		formRef.value
+			.validate()
+			.then(() => {
+				// 校验表格信息里面的表单是否有空数据
+				for (let i = 0; i < formData.value.loadData.length; i++) {
+					if (
+						!formData.value.loadData[i].startTime ||
+						!formData.value.loadData[i].endTime ||
+						!formData.value.loadData[i].name ||
+						!formData.value.loadData[i].remindType ||
+						!formData.value.loadData[i].level ||
+						!formData.value.loadData[i].phone
+					) {
+						console.log(i, 'xxxxxx')
+
+						message.warning(`请完善表格中的必填项`)
+					} else {
+						console.log(formData.value.list, '提交数据')
+						// moduleApi.submitForm(formData.value, formData.value.id).then(() => {
+						// 	onClose()
+						// 	emit('successful')
+						// })
+					}
+				}
+			})
+			.catch((error) => {
+				console.log(error)
+			})
+	}
+
+	// 新增或删除路数
+	const onEdit = (targetKey, action) => {
+		if (action === 'add') {
+			add()
+		} else {
+			remove(targetKey)
+		}
+	}
+	const activeKey = ref('object') //选中的tabs
+	const panes = ref([]) //tabs数组
+	const newTabIndex = ref(1)
+	// 添加tabs
+	const add = () => {
+		const newIndex = newTabIndex.value++
+		if (panes.value.length >= 5) return //最多只能5路
+		panes.value.push({
+			title: `${newIndex}路`,
+			key: newIndex
+		})
+
+		formData.value.list.push({
+			title: `${newIndex}路`,
+			key: newIndex
+		})
+	}
+
+	// 删除tabs
+	const remove = (targetKey) => {
+		let lastIndex = 0
+		panes.value.forEach((pane, i) => {
+			if (pane.key === targetKey) {
+				lastIndex = i - 1
+			}
+		})
+		panes.value = panes.value.filter((pane) => pane.key !== targetKey)
+		if (panes.value.length && activeKey.value === targetKey) {
+			if (lastIndex >= 0) {
+				activeKey.value = panes.value[lastIndex].key
+			} else {
+				activeKey.value = panes.value[0].key
+			}
+		}
+
+		if (panes.value.length == 0) {
+			activeKey.value = 'object'
+		}
+
+		formData.value.list = formData.value.list.filter((item) => item.key !== targetKey) //删除表单
+	}
+
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		onOpen
+	})
+</script>
+
+<style lang="less" scoped>
+	.ant-tabs {
+		width: 100%;
+	}
+	:deep(.ant-col) {
+		margin-bottom: 10px;
+	}
+</style>

+ 474 - 0
snowy-admin-web/src/views/basicset/monitor/form copy.vue

@@ -0,0 +1,474 @@
+<template>
+	<xn-form-container
+		:title="formData.id ? '编辑监控对象' : '新增监控对象'"
+		:width="'60%'"
+		:visible="visible"
+		:destroy-on-close="true"
+		@close="onClose"
+	>
+		<a-form
+			ref="formRef"
+			:model="formData"
+			:rules="formRules"
+			layout="inline"
+			:label-col="{ style: { width: '100px', justifyContent: 'end' } }"
+		>
+			<a-tabs v-model:activeKey="activeKey" type="editable-card" @edit="onEdit">
+				<a-tab-pane tab="对象信息" key="object" :closable="false">
+					<a-row :gutter="10">
+						<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+							<a-form-item label="对象名称" name="name">
+								<a-input v-model:value="formData.name" placeholder="请输入对象名称" allow-clear />
+							</a-form-item>
+						</a-col>
+
+						<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+							<a-form-item label="对象编号">
+								<a-input v-model:value="formData.code" placeholder="请输入对象编号" allow-clear />
+							</a-form-item>
+						</a-col>
+						<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+							<a-form-item label="对象ID">
+								<a-input v-model:value="formData.barcode" placeholder="请输入对象ID" allow-clear />
+							</a-form-item>
+						</a-col>
+						<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+							<a-form-item label="对象类型" name="type">
+								<a-select ref="select" v-model:value="formData.type">
+									<a-select-option value="1">新装</a-select-option>
+									<a-select-option value="2">液氮罐</a-select-option>
+									<a-select-option value="3">转运箱</a-select-option>
+								</a-select>
+							</a-form-item>
+						</a-col>
+						<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+							<a-form-item label="品牌" name="brand">
+								<a-input v-model:value="formData.brand" placeholder="请输入品牌" allow-clear />
+							</a-form-item>
+						</a-col>
+						<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+							<a-form-item label="型号" name="xh">
+								<a-input v-model:value="formData.xh" placeholder="请输入型号" allow-clear />
+							</a-form-item>
+						</a-col>
+
+						<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+							<a-form-item label="对象状态" name="state">
+								<a-select ref="select" v-model:value="formData.state" placeholder="请选择对象状态">
+									<a-select-option value="1">正常</a-select-option>
+									<a-select-option value="2">停用</a-select-option>
+								</a-select>
+							</a-form-item>
+						</a-col>
+						<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+							<a-form-item label="所属部门" name="division">
+								<a-input v-model:value="formData.division" placeholder="请输入所属部门" allow-clear />
+							</a-form-item>
+						</a-col>
+						<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+							<a-form-item label="使用日期">
+								<a-date-picker
+									v-model:value="formData.collectionTime"
+									placeholder="请选择使用日期"
+									format="YYYY-MM-DD"
+									style="width: 100%"
+								/>
+							</a-form-item>
+						</a-col>
+						<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+							<a-form-item label="对象位置">
+								<a-input v-model:value="formData.location" placeholder="请输入对象位置" allow-clear />
+							</a-form-item>
+						</a-col>
+					</a-row>
+				</a-tab-pane>
+				<a-tab-pane tab="报警信息" key="report" :closable="false">
+					<a-form-item :label-col="{ style: { width: '200px' } }">
+						<template #label>
+							<a-tooltip>
+								<template #title>
+									重复通知间隔:同一设备同一故障类型出现报警进行过一次报警通知后,进行下一次报普通知的间隔时间,如果在这个时间间隔内该设备同一故障类型
+									还在产生报警则不再进行通知。
+									最多报警次数:同一设备同一故障类型连续报警最多报警通知次数,在达到最多报警次数后如果报警还未及时处理,系统也不再进行报警通知。
+									延时时间:为屏蔽误报,系统报警在达到延时时间之后才进行报警通知。
+								</template>
+								<question-circle-outlined />
+							</a-tooltip>
+							重复发送通知:
+						</template>
+
+						<a-radio-group v-model:value="formData.repeat">
+							<a-radio value="1">1次</a-radio>
+							<a-radio value="2">多次</a-radio>
+						</a-radio-group>
+					</a-form-item>
+					<a-form-item label="延时报警" :label-col="{ style: { width: '200px' } }">
+						<a-radio-group v-model:value="formData.delayed">
+							<a-radio value="1">是</a-radio>
+							<a-radio value="0">否</a-radio>
+						</a-radio-group>
+					</a-form-item>
+					<a-form-item label="报警关闭发送通知" :label-col="{ style: { width: '200px' } }">
+						<a-radio-group v-model:value="formData.close">
+							<a-radio value="1">是</a-radio>
+							<a-radio value="0">否</a-radio>
+						</a-radio-group>
+					</a-form-item>
+					<a-form-item label="多级报警" :label-col="{ style: { width: '200px' } }">
+						<a-radio-group v-model:value="formData.multistage">
+							<a-radio value="1">是</a-radio>
+							<a-radio value="0">否</a-radio>
+						</a-radio-group>
+					</a-form-item>
+
+					<a-form-item label="报警通知方式" :label-col="{ style: { width: '200px' } }">
+						<a-checkbox-group v-model:value="formData.noticeway" style="width: 100%">
+							<a-checkbox value="1">短信</a-checkbox>
+							<a-checkbox value="2">电话</a-checkbox>
+							<a-checkbox value="3">微信</a-checkbox>
+							<a-checkbox value="4">电子邮件</a-checkbox>
+							<a-checkbox value="5">弹出</a-checkbox>
+							<a-checkbox value="6">音乐</a-checkbox>
+						</a-checkbox-group>
+					</a-form-item>
+
+					<!-- 表格区域 -->
+					<div>
+						<a-button type="primary" style="margin-left: 90%; margin-bottom: 5px" @click="tableAdd">
+							<template #icon><plus-outlined /></template>新增
+						</a-button>
+						<a-table
+							ref="tableRef"
+							bordered
+							:columns="columns"
+							:data-source="formData.loadData"
+							:row-key="(record) => record.startTime"
+							:pagination="false"
+						>
+							<template #headerCell="{ column }">
+								<template v-if="column.dataIndex === 'startTime'">
+									<text style="color: #e74032"> * </text>开始时间
+								</template>
+								<template v-if="column.dataIndex === 'endTime'">
+									<text style="color: #e74032"> * </text>结束时间
+								</template>
+								<template v-if="column.dataIndex === 'name'"> <text style="color: #e74032"> * </text>姓名 </template>
+								<template v-if="column.dataIndex === 'remindType'">
+									<text style="color: #e74032"> * </text>提醒方式
+								</template>
+								<template v-if="column.dataIndex === 'level'"> <text style="color: #e74032"> * </text>级别 </template>
+								<template v-if="column.dataIndex === 'phone'">
+									<text style="color: #e74032"> * </text>电话号码
+								</template>
+							</template>
+							<template #bodyCell="{ column, record, index }">
+								<!-- 表格内容 -->
+								<template v-if="column.dataIndex === 'startTime'">
+									<a-time-picker v-model:value="record.startTime" value-format="HH:mm:ss" style="width: 100%" />
+								</template>
+
+								<template v-if="column.dataIndex === 'endTime'">
+									<a-time-picker v-model:value="record.endTime" value-format="HH:mm:ss" style="width: 100%" />
+								</template>
+
+								<template v-if="column.dataIndex === 'name'">
+									<a-input v-model:value="record.name" placeholder="请输入姓名" />
+								</template>
+
+								<template v-if="column.dataIndex === 'remindType'">
+									<a-select v-model:value="record.remindType" placeholder="请选择提醒方式" style="width: 100%">
+										<a-select-option value="1">短信报警</a-select-option>
+										<a-select-option value="2">电话报警</a-select-option>
+										<a-select-option value="3">微信公众号报警</a-select-option>
+										<a-select-option value="4">邮件报警</a-select-option>
+										<a-select-option value="5">弹出报警</a-select-option>
+										<a-select-option value="6">音乐报警</a-select-option>
+									</a-select>
+								</template>
+
+								<template v-if="column.dataIndex === 'level'">
+									<a-select v-model:value="record.level" placeholder="请选择级别" style="width: 100px">
+										<a-select-option value="1">1</a-select-option>
+										<a-select-option value="2">2</a-select-option>
+										<a-select-option value="3">3</a-select-option>
+										<a-select-option value="4">4</a-select-option>
+										<a-select-option value="5">5</a-select-option>
+									</a-select>
+								</template>
+
+								<template v-if="column.dataIndex === 'phone'">
+									<a-input v-model:value="record.phone" placeholder="请输入电话号码" />
+								</template>
+
+								<template v-if="column.dataIndex === 'action'">
+									<a-popconfirm title="确定要删除吗?" @confirm="tableDelete(record, index)">
+										<a-button type="link" danger size="small">删除</a-button>
+									</a-popconfirm>
+								</template>
+							</template>
+						</a-table>
+					</div>
+				</a-tab-pane>
+
+				<a-tab-pane v-for="pane in panes" :key="pane.key" :tab="pane.title" :closable="pane.closable">
+					<!-- 新增监控点-->
+					<div v-for="(item, index) in formData.list" :key="index">
+						<a-row :gutter="10" v-if="activeKey == item.key">
+							<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+								<a-form-item label="采集设备">
+									<a-input v-model:value="item.device" placeholder="请输入采集设备" allow-clear />
+								</a-form-item>
+							</a-col>
+							<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+								<a-form-item label="传感器路数">
+									<a-input v-model:value="item.number" placeholder="请输入传感器路数" allow-clear />
+								</a-form-item>
+							</a-col>
+							<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+								<a-form-item label="监控点名称">
+									<a-input v-model:value="item.paneName" placeholder="请输入监控点名称" allow-clear />
+								</a-form-item>
+							</a-col>
+							<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+								<a-form-item label="采集值1上限">
+									<a-input v-model:value="item.upper" placeholder="请输入采集值1上限" allow-clear />
+								</a-form-item>
+							</a-col>
+							<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+								<a-form-item label="采集值1下限">
+									<a-input v-model:value="item.lower" placeholder="请输入采集值1下限" allow-clear />
+								</a-form-item>
+							</a-col>
+							<a-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
+								<a-form-item
+									label="正常采集间隔(分钟)"
+									:label-col="{ style: { width: '130px', justifyContent: 'end' } }"
+								>
+									<a-input v-model:value="item.interval" placeholder="请输入正常采集间隔" allow-clear />
+								</a-form-item>
+							</a-col>
+						</a-row>
+					</div>
+				</a-tab-pane>
+			</a-tabs>
+		</a-form>
+
+		<template #footer>
+			<a-button class="xn-mr8" @click="onClose">退出</a-button>
+			<a-button type="primary" @click="onSubmit">保存</a-button>
+		</template>
+	</xn-form-container>
+</template>
+
+<script setup>
+	import { message } from 'ant-design-vue'
+	import { required } from '@/utils/formRules'
+	import setupApi from '@/api/basicset/setupApi'
+	// 默认是关闭状态
+	const visible = ref(false)
+	const emit = defineEmits({ successful: null })
+	const formRef = ref()
+
+	const tableRef = ref()
+
+	const columns = [
+		{
+			title: '开始时间',
+			dataIndex: 'startTime',
+			align: 'center'
+		},
+		{
+			title: '结束时间',
+			dataIndex: 'endTime',
+			align: 'center'
+		},
+		{
+			title: '姓名',
+			dataIndex: 'name',
+			align: 'center'
+		},
+		{
+			title: '提醒方式',
+			dataIndex: 'remindType',
+			align: 'center'
+		},
+		{
+			title: '级别',
+			dataIndex: 'level',
+			align: 'center'
+		},
+		{
+			title: '电话号码',
+			dataIndex: 'phone',
+			align: 'center'
+		},
+		{
+			title: '操作',
+			dataIndex: 'action',
+			align: 'center',
+			scopedSlots: { customRender: 'action' }
+		}
+	]
+	// 表单数据
+	const formData = ref({
+		repeat: '1',
+		delayed: '0',
+		close: '0',
+		multistage: '0',
+		noticeway: ['1'],
+		loadData: [],
+		list: []
+	})
+	// 默认要校验的
+	const formRules = {
+		name: [required('请输入对象名称')],
+		type: [required('请选择对象类型')],
+		brand: [required('请输入品牌')],
+		xh: [required('请输入型号')],
+		division: [required('请输入所属部门')],
+		state: [required('请选择对象状态')]
+	}
+
+	// 打开抽屉
+	const onOpen = (record) => {
+		visible.value = true
+		activeKey.value = 'object'
+		panes.value = []
+		newTabIndex.value = 1
+		if (record) {
+			formData.value = Object.assign({}, record)
+		} else {
+			formData.value = {
+				repeat: '1',
+				delayed: '0',
+				close: '0',
+				multistage: '0',
+				noticeway: ['1'],
+				loadData: [],
+				list: []
+			}
+		}
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		formRef.value.resetFields()
+		visible.value = false
+	}
+
+	// 表格添加一行空数据
+	const tableAdd = () => {
+		const newRow = {
+			startTime: '00:00:00',
+			endTime: '23:59:59',
+			name: '',
+			level: '',
+			phone: ''
+		}
+		formData.value.loadData.push(newRow)
+	}
+
+	// 删除某一行
+	const tableDelete = (record, index) => {
+		formData.value.loadData.splice(index, 1)
+	}
+
+	// 验证并提交数据
+	const onSubmit = () => {
+		formRef.value
+			.validate()
+			.then(() => {
+				// 校验表格信息里面的表单是否有空数据
+				for (let i = 0; i < formData.value.loadData.length; i++) {
+					if (
+						!formData.value.loadData[i].startTime ||
+						!formData.value.loadData[i].endTime ||
+						!formData.value.loadData[i].name ||
+						!formData.value.loadData[i].remindType ||
+						!formData.value.loadData[i].level ||
+						!formData.value.loadData[i].phone
+					) {
+						console.log(i, 'xxxxxx')
+
+						message.warning(`请完善表格中的必填项`)
+					} else {
+						console.log(formData.value.list, '提交数据')
+						// setupApi.submitForm(formData.value, formData.value.id).then(() => {
+						// 	onClose()
+						// 	emit('successful')
+						// })
+					}
+				}
+			})
+			.catch((error) => {
+				console.log(error)
+			})
+	}
+
+	// 新增或删除监控点
+	const onEdit = (targetKey, action) => {
+		if (action === 'add') {
+			add()
+		} else {
+			remove(targetKey)
+		}
+	}
+	const activeKey = ref('object') //选中的tabs
+	const panes = ref([]) //tabs数组
+	const newTabIndex = ref(1)
+	// 添加tabs
+	const add = () => {
+		const newIndex = newTabIndex.value++
+		if (panes.value.length >= 5) return //最多只能5个监控点
+		panes.value.push({
+			title: `监控点${newIndex}`,
+			key: newIndex
+		})
+
+		formData.value.list.push({
+			title: `监控点${newIndex}`,
+			paneName: `监控点${newIndex}`,
+			key: newIndex,
+			interval: 10,
+			upper: 100,
+			lower: -100
+		})
+	}
+
+	// 删除tabs
+	const remove = (targetKey) => {
+		let lastIndex = 0
+		panes.value.forEach((pane, i) => {
+			if (pane.key === targetKey) {
+				lastIndex = i - 1
+			}
+		})
+		panes.value = panes.value.filter((pane) => pane.key !== targetKey)
+		if (panes.value.length && activeKey.value === targetKey) {
+			if (lastIndex >= 0) {
+				activeKey.value = panes.value[lastIndex].key
+			} else {
+				activeKey.value = panes.value[0].key
+			}
+		}
+
+		if (panes.value.length == 0) {
+			activeKey.value = 'object'
+		}
+
+		formData.value.list = formData.value.list.filter((item) => item.key !== targetKey) //删除表单
+	}
+
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		onOpen
+	})
+</script>
+
+<style lang="less" scoped>
+	.ant-tabs {
+		width: 100%;
+	}
+	:deep(.ant-col) {
+		margin-bottom: 10px;
+	}
+</style>

+ 265 - 0
snowy-admin-web/src/views/biz/monitor/devicetype/form.vue

@@ -0,0 +1,265 @@
+<template>
+	<xn-form-container
+		:title="formData.id ? '编辑设备型号' : '新增设备型号'"
+		:width="'800px'"
+		:visible="visible"
+		:destroy-on-close="true"
+		@close="onClose"
+	>
+		<a-form
+			ref="formRef"
+			:model="formData"
+			:rules="formRules"
+			layout="inline"
+			:label-col="{ style: { width: '105px', justifyContent: 'end' } }"
+		>
+			<a-row :gutter="10">
+				<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+					<a-form-item label="型号名称" name="name">
+						<a-input v-model:value="formData.name" placeholder="请输入型号名称" allow-clear />
+					</a-form-item>
+				</a-col>
+				<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+					<a-form-item label="传感器类型" name="sensorType">
+						<a-select
+							ref="select"
+							v-model:value="formData.sensorType"
+							:options="sensorOptions"
+							placeholder="请选择传感器类型"
+						/>
+					</a-form-item>
+				</a-col>
+				<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+					<a-form-item label="型号编号" name="code">
+						<a-input v-model:value="formData.code" placeholder="请输入型号编号" allow-clear :maxlength="10" />
+					</a-form-item>
+				</a-col>
+				<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+					<a-form-item label="传感器总路数" name="sensorCount">
+						<a-input-number
+							v-model:value="formData.sensorCount"
+							placeholder="请输入传感器总路数"
+							allow-clear
+							:min="1"
+							:max="4"
+							style="width: 100%"
+							:precision="0"
+						/>
+					</a-form-item>
+				</a-col>
+
+				<div
+					class="form_item"
+					v-if="formData.sensorType == 'W' || formData.sensorType == 'WS' || formData.sensorType == 'WSC'"
+				>
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="温度上限" name="temperatureUp">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.temperatureUp"
+								placeholder="请输入温度上限"
+								allow-clear
+								style="width: 100%"
+							/>
+						</a-form-item>
+					</a-col>
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="温度下限" name="temperatureDown">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.temperatureDown"
+								placeholder="请输入温度下限"
+								allow-clear
+								style="width: 100%"
+							/>
+						</a-form-item>
+					</a-col>
+				</div>
+				<div class="form_item" v-if="formData.sensorType == 'WS' || formData.sensorType == 'WSC'">
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="湿度上限" name="humidityUp">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.humidityUp"
+								placeholder="请输入湿度上限"
+								allow-clear
+								style="width: 100%"
+							/>
+						</a-form-item>
+					</a-col>
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="湿度下限" name="humidityDown">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.humidityDown"
+								placeholder="请输入湿度下限"
+								allow-clear
+								style="width: 100%"
+							/>
+						</a-form-item>
+					</a-col>
+				</div>
+				<div class="form_item" v-if="formData.sensorType == 'WSC'">
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="CO2上限" name="co2Up">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.co2Up"
+								placeholder="请输入CO2上限"
+								allow-clear
+								style="width: 100%"
+							/>
+						</a-form-item>
+					</a-col>
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="CO2下限" name="co2Down">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.co2Down"
+								placeholder="请输入CO2下限"
+								allow-clear
+								style="width: 100%"
+							/>
+						</a-form-item>
+					</a-col>
+				</div>
+
+				<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+					<a-form-item label="排序:" name="sortCode">
+						<a-input-number class="xn-wd" v-model:value="formData.sortCode" :max="100" />
+					</a-form-item>
+				</a-col>
+			</a-row>
+		</a-form>
+
+		<template #footer>
+			<a-button class="xn-mr8" @click="onClose">退出</a-button>
+			<a-button type="primary" @click="onSubmit">保存</a-button>
+		</template>
+	</xn-form-container>
+</template>
+
+<script setup>
+	import tool from '@/utils/tool'
+
+	import { required, rules } from '@/utils/formRules'
+	import devicetypeApi from '@/api/basicset/devicetypeApi'
+	const emit = defineEmits({ successful: null })
+	const visible = ref(false) // 默认是关闭状态
+	const formRef = ref()
+	const formData = ref({ sortCode: 99 }) // 表单数据
+	const sensorOptions = tool.dictList('SENSORTYPE') // 传感器类型
+
+	// 默认要校验的
+	const formRules = {
+		name: [required('请输入型号名称')],
+		sensorType: [required('请选择传感器类型')],
+		code: [required('请输入型号编号'), rules.number],
+		sensorCount: [required('请输入传感器总路数')],
+		temperatureUp: [required('请输入温度上限'), { validator: validateTemperature, trigger: ['change', 'blur'] }],
+		temperatureDown: [required('请输入温度下限'), { validator: validateTemperature, trigger: ['change', 'blur'] }],
+		humidityUp: [required('请输入湿度上限'), { validator: validateHumidity, trigger: ['change', 'blur'] }],
+		humidityDown: [required('请输入湿度下限'), { validator: validateHumidity, trigger: ['change', 'blur'] }],
+		co2Up: [required('请输入CO2上限'), { validator: validateCo2, trigger: ['change', 'blur'] }],
+		co2Down: [required('请输入CO2下限'), { validator: validateCo2, trigger: ['change', 'blur'] }]
+	}
+	// 温度上下限校验
+	function validateTemperature(rule, value, callback) {
+		if (rule.field == 'temperatureDown') {
+			if (formData.value.temperatureUp !== null && value >= formData.value.temperatureUp) {
+				callback(new Error('温度下限不能大于或等于温度上限'))
+			} else {
+				callback()
+			}
+		} else if (rule.field == 'temperatureUp') {
+			if (formData.value.temperatureDown !== null && value <= formData.value.temperatureDown) {
+				callback(new Error('温度上限不能小于或等于温度下限'))
+			} else {
+				callback()
+			}
+		}
+	}
+
+	// 湿度上下限校验
+	function validateHumidity(rule, value, callback) {
+		if (rule.field == 'humidityDown') {
+			if (formData.value.humidityUp !== null && value >= formData.value.humidityUp) {
+				callback(new Error('湿度下限不能大于或等于湿度上限'))
+			} else {
+				callback()
+			}
+		} else if (rule.field == 'humidityUp') {
+			if (formData.value.humidityDown !== null && value <= formData.value.humidityDown) {
+				callback(new Error('湿度上限不能小于或等于湿度下限'))
+			} else {
+				callback()
+			}
+		}
+	}
+
+	// CO2上下限校验
+	function validateCo2(rule, value, callback) {
+		if (rule.field == 'co2Down') {
+			if (formData.value.co2Up !== null && value >= formData.value.co2Up) {
+				callback(new Error('CO2下限不能大于或等于CO2上限'))
+			} else {
+				callback()
+			}
+		} else if (rule.field == 'co2Up') {
+			if (formData.value.co2Down !== null && value <= formData.value.co2Down) {
+				callback(new Error('CO2上限不能小于或等于CO2下限'))
+			} else {
+				callback()
+			}
+		}
+	}
+
+	// 打开抽屉
+	const onOpen = (record) => {
+		visible.value = true
+		if (record) {
+			formData.value = Object.assign({}, record)
+		} else {
+			formData.value = { sortCode: 99 }
+		}
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		formRef.value.resetFields()
+		visible.value = false
+	}
+
+	// 验证并提交数据
+	const onSubmit = () => {
+		formRef.value
+			.validate()
+			.then(() => {
+				devicetypeApi.submitForm(formData.value, formData.value.id).then(() => {
+					onClose()
+					emit('successful')
+				})
+			})
+			.catch((error) => {
+				console.log(error)
+			})
+	}
+
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		onOpen
+	})
+</script>
+
+<style lang="less" scoped>
+	:deep(.ant-form-item) {
+		width: 100%;
+		margin-bottom: 10px;
+	}
+	.form_item {
+		width: 100%;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		flex-wrap: wrap;
+	}
+</style>

+ 210 - 0
snowy-admin-web/src/views/biz/monitor/devicetype/index.vue

@@ -0,0 +1,210 @@
+<template>
+	<div class="table_item">
+		<s-table
+			ref="tableRef"
+			:columns="columns"
+			:data="loadData"
+			:row-key="(record) => record.id"
+			bordered
+			@resizeColumn="handleResizeColumn"
+		>
+			<template #operator>
+				<!-- 搜索区域 -->
+				<div class="table-search">
+					<div class="table-search-form">
+						<a-row :gutter="10">
+							<a-form
+								ref="searchFormRef"
+								name="advanced_search"
+								layout="inline"
+								:label-col="{ style: { width: '70px', justifyContent: 'end' } }"
+								:model="searchFormState"
+								class="ant-advanced-search-form"
+							>
+								<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8"
+									><a-form-item label="型号名称" name="name">
+										<a-input v-model:value="searchFormState.name" placeholder="请输入型号名称" /> </a-form-item
+								></a-col>
+							</a-form>
+						</a-row>
+					</div>
+					<div class="table-search-buttons">
+						<a-button type="primary" @click="tableRef.refresh(true)">查询</a-button>
+						<a-button class="xn-mg08" @click="reset">重置</a-button>
+					</div>
+				</div>
+
+				<!-- 其他操作区域 -->
+				<div class="table-head-btn">
+					<div class="btn-left">
+						<a-button type="primary" @click="formRef.onOpen()">
+							<template #icon><plus-outlined /></template>新增
+						</a-button>
+					</div>
+				</div>
+			</template>
+			<template #bodyCell="{ column, record, index }">
+				<!-- 序号 -->
+				<template v-if="column.dataIndex === 'index'">{{ index + 1 }} </template>
+				<template v-if="column.dataIndex === 'action'">
+					<a-popconfirm title="确定要删除吗?" @confirm="deleteData(record)">
+						<a-button type="link" danger size="small">删除</a-button>
+					</a-popconfirm>
+				</template>
+			</template>
+		</s-table>
+
+		<Form ref="formRef" @successful="tableRef.refresh(true)" />
+	</div>
+</template>
+
+<script setup>
+	import devicetypeApi from '@/api/basicset/devicetypeApi'
+	import Form from './form.vue'
+	const formRef = ref()
+	const searchFormRef = ref()
+	const searchFormState = ref({})
+	const tableRef = ref()
+
+	const columns = [
+		{
+			title: '序号',
+			dataIndex: 'index',
+			align: 'center',
+			width: 50
+		},
+		{
+			title: '型号名称',
+			dataIndex: 'name',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '型号编号',
+			dataIndex: 'code',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '传感器总路数',
+			dataIndex: 'sensorCount',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '温度上限',
+			dataIndex: 'temperatureUp',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '温度下限',
+			dataIndex: 'temperatureDown',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '湿度上限',
+			dataIndex: 'humidityUp',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+
+		{
+			title: '湿度下限',
+			dataIndex: 'humidityDown',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: 'CO2上限',
+			dataIndex: 'co2Up',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: 'CO2下限',
+			dataIndex: 'co2Down',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+
+		{
+			title: '排序码',
+			dataIndex: 'sortCode',
+			align: 'center',
+			ellipsis: true,
+			resizable: true,
+			sorter: true
+		},
+
+		{
+			title: '操作',
+			dataIndex: 'action',
+			align: 'center',
+			width: 150
+		}
+	]
+	// 可伸缩列
+	const handleResizeColumn = (w, col) => {
+		col.width = w
+	}
+
+	const loadData = (parameter) => {
+		return devicetypeApi.devicetypePage(Object.assign(parameter, searchFormState.value)).then((res) => {
+			return res
+		})
+	}
+	// 重置
+	const reset = () => {
+		searchFormRef.value.resetFields()
+		tableRef.value.refresh(true)
+	}
+	// 删除
+	const deleteData = (record) => {
+		let params = [
+			{
+				id: record.id
+			}
+		]
+		devicetypeApi.devicetypeDelete(params).then(() => {
+			tableRef.value.refresh(true)
+			reset()
+		})
+	}
+</script>
+
+<style lang="less" scoped>
+	.table_item {
+		padding: 15px 20px;
+		background-color: #ffffff;
+		:deep(.ant-table-pagination-right) {
+			justify-content: center !important;
+		}
+	}
+	.list {
+		width: 60%;
+		padding: 10px 20px;
+		border-radius: 10px;
+		background-color: #ffffff;
+
+		:deep(.ant-descriptions) {
+			margin-bottom: 10px;
+		}
+		:deep(.ant-descriptions-header) {
+			margin: 0;
+			padding: 10px;
+			border: 1px solid #f0f0f0;
+			border-bottom: none;
+		}
+	}
+</style>

+ 304 - 0
snowy-admin-web/src/views/biz/monitor/mem/configForm.vue

@@ -0,0 +1,304 @@
+<template>
+	<xn-form-container title="配置" :width="'60%'" :visible="visible" :destroy-on-close="true" @close="onClose">
+		<a-form
+			ref="formRef"
+			:model="formData"
+			:rules="formRules"
+			layout="inline"
+			:label-col="{ style: { width: '85px', justifyContent: 'end' } }"
+		>
+			<div class="form_left">
+				<div class="form_title">网络目标参数</div>
+				<div class="form_row">
+					<a-form-item label="目标端口" name="code">
+						<a-input v-model:value="formData.code" placeholder="请输入目标端口" allow-clear />
+					</a-form-item>
+					<a-form-item label="目标IP(域名)" name="code">
+						<a-input v-model:value="formData.code" placeholder="请输入目标IP" allow-clear />
+					</a-form-item>
+				</div>
+				<div class="form_title">WIFI目标路由器SSID参数</div>
+				<div class="form_row container">
+					<a-form-item label="目标路由器SSID" name="code" :label-col="{ style: { width: '135px' } }">
+						<a-input v-model:value="formData.code" placeholder="请输入目标路由器SSID" allow-clear />
+					</a-form-item>
+					<a-form-item label="目标路由器登录密码" name="code" :label-col="{ style: { width: '135px' } }">
+						<a-input v-model:value="formData.code" placeholder="请输入目标路由器登录密码" allow-clear />
+					</a-form-item>
+				</div>
+				<div class="form_title">WIFI-WAN口参数</div>
+				<div class="form_row">
+					<a-form-item label="IP获取方式" name="code">
+						<a-input v-model:value="formData.code" placeholder="请输入IP获取方式" allow-clear />
+					</a-form-item>
+					<a-form-item label="本地IP" name="code">
+						<a-input v-model:value="formData.code" placeholder="请输入本地IP" allow-clear />
+					</a-form-item>
+					<a-form-item label="子网掩码" name="code">
+						<a-input v-model:value="formData.code" placeholder="请输入子网掩码" allow-clear />
+					</a-form-item>
+					<a-form-item label="网关地址" name="code">
+						<a-input v-model:value="formData.code" placeholder="请输入网关地址" allow-clear />
+					</a-form-item>
+				</div>
+				<div class="form_title">MAC地址</div>
+
+				<div class="form_row">
+					<a-form-item label="MAC地址" name="code">
+						<a-input v-model:value="formData.code" placeholder="请输入MAC地址" allow-clear />
+					</a-form-item>
+				</div>
+			</div>
+			<div class="form_right">
+				<div class="form_title">设备参数</div>
+				<a-form-item label="终端地址" name="code" :label-col="{ style: { width: '130px' } }">
+					<a-input v-model:value="formData.code" allow-clear />
+					<span class="suffix">00000001~99999999</span>
+				</a-form-item>
+				<a-form-item label="登录帧间隔时间" name="code" :label-col="{ style: { width: '130px' } }">
+					<a-input v-model:value="formData.code" allow-clear />
+					<span class="suffix">S(1-600000S)</span>
+				</a-form-item>
+				<a-form-item label="心跳帧间隔时间" name="code" :label-col="{ style: { width: '130px' } }">
+					<a-input v-model:value="formData.code" allow-clear />
+					<span class="suffix">S(1-600000S)</span>
+				</a-form-item>
+				<a-form-item label="主动上送帧间隔时间" name="code" :label-col="{ style: { width: '130px' } }">
+					<a-input v-model:value="formData.code" allow-clear />
+					<span class="suffix">S(1-600000S)</span>
+				</a-form-item>
+
+				<!-- 温度 -->
+				<div class="temp">
+					<div>温度:</div>
+					<div class="temp_data">
+						<div class="data_item">
+							<div>上限</div>
+							<a-form-item>
+								<a-input-number id="inputNumber" v-model:value="formData.code" />
+							</a-form-item>
+						</div>
+						<div class="data_item">
+							<div>下限</div>
+							<a-form-item>
+								<a-input-number id="inputNumber" v-model:value="formData.code" />
+							</a-form-item>
+						</div>
+						<div class="data_item">
+							<div>回差</div>
+							<a-form-item>
+								<a-input-number id="inputNumber" v-model:value="formData.code" />
+							</a-form-item>
+						</div>
+						<div class="data_item">
+							<div>偏差</div>
+							<a-form-item>
+								<a-input-number id="inputNumber" v-model:value="formData.code" />
+							</a-form-item>
+						</div>
+					</div>
+				</div>
+				<!-- 湿度 -->
+				<div class="temp">
+					<div>湿度:</div>
+					<div class="temp_data">
+						<div class="data_item">
+							<div>上限</div>
+							<a-form-item>
+								<a-input-number id="inputNumber" v-model:value="formData.code" />
+							</a-form-item>
+						</div>
+						<div class="data_item">
+							<div>下限</div>
+							<a-form-item>
+								<a-input-number id="inputNumber" v-model:value="formData.code" />
+							</a-form-item>
+						</div>
+						<div class="data_item">
+							<div>回差</div>
+							<a-form-item>
+								<a-input-number id="inputNumber" v-model:value="formData.code" />
+							</a-form-item>
+						</div>
+						<div class="data_item">
+							<div>偏差</div>
+							<a-form-item>
+								<a-input-number id="inputNumber" v-model:value="formData.code" />
+							</a-form-item>
+						</div>
+					</div>
+				</div>
+				<div class="temp">
+					<a-form-item label="温度上限报警使能" name="code" :label-col="{ style: { width: '130px' } }">
+						<a-checkbox v-model:checked="formData.checked" />
+					</a-form-item>
+					<a-form-item label="温度下限报警使能" name="code" :label-col="{ style: { width: '130px' } }">
+						<a-checkbox v-model:checked="formData.checked" />
+					</a-form-item>
+				</div>
+				<div class="temp">
+					<a-form-item label="湿度上限报警使能" name="code" :label-col="{ style: { width: '130px' } }">
+						<a-checkbox v-model:checked="formData.checked" />
+					</a-form-item>
+					<a-form-item label="湿度下限报警使能" name="code" :label-col="{ style: { width: '130px' } }">
+						<a-checkbox v-model:checked="formData.checked" />
+					</a-form-item>
+				</div>
+				<a-form-item label="设备数据记录间隔(分)" name="code" :label-col="{ style: { width: '150px' } }">
+					<a-input v-model:value="formData.code" allow-clear />
+				</a-form-item>
+				<a-form-item label="数据记录模式" name="code" :label-col="{ style: { width: '150px' } }">
+					<a-input v-model:value="formData.code" allow-clear />
+				</a-form-item>
+				<a-form-item label="设备中储存的数据主动上传" name="code" :label-col="{ style: { width: '180px' } }">
+					<a-checkbox v-model:checked="formData.checked" />
+				</a-form-item>
+			</div>
+		</a-form>
+
+		<template #footer>
+			<a-button class="xn-mr8" @click="onClose">退出</a-button>
+			<a-button type="primary" @click="onSubmit">保存</a-button>
+		</template>
+	</xn-form-container>
+</template>
+
+<script setup>
+	import tool from '@/utils/tool'
+	import { message } from 'ant-design-vue'
+	import { required } from '@/utils/formRules'
+	import memApi from '@/api/basicset/memApi'
+	const emit = defineEmits({ successful: null })
+	const visible = ref(false) // 默认是关闭状态
+	const formRef = ref()
+	const formData = ref({}) // 表单数据
+
+	// 默认要校验的
+	const formRules = {
+		deviceName: [required('请输入设备名称')],
+		deviceCode: [required('请输入设备编号')],
+		modelName: [required('请选择设备型号')],
+		sensorCount: [required('请输入传感器路数')]
+	}
+
+	// 打开抽屉
+	const onOpen = (record) => {
+		visible.value = true
+		if (record) {
+			formData.value = Object.assign({}, record)
+		} else {
+			formData.value = {}
+		}
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		formRef.value.resetFields()
+		visible.value = false
+	}
+
+	// 验证并提交数据
+	const onSubmit = () => {
+		formRef.value
+			.validate()
+			.then(() => {
+				memApi.submitForm(formData.value, formData.value.id).then(() => {
+					onClose()
+					emit('successful')
+				})
+			})
+			.catch((error) => {
+				console.log(error)
+			})
+	}
+
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		onOpen
+	})
+</script>
+
+<style lang="less" scoped>
+	.ant-form {
+		width: 100%;
+
+		.form_title {
+			height: 25px;
+			font-weight: bold;
+			line-height: 25px;
+			padding-left: 15px;
+			margin-bottom: 10px;
+			position: relative;
+		}
+		.form_title::before {
+			content: '';
+			position: absolute;
+			top: 5px;
+			left: 0px;
+			height: 15px;
+			border-left: 4px solid #1878ff;
+			border-radius: 8px;
+		}
+
+		// 左边表单
+		.form_left {
+			width: 60%;
+			padding: 0 10px;
+			.form_row {
+				display: flex;
+				align-items: center;
+				flex-wrap: wrap;
+				margin-bottom: 15px;
+				:deep(.ant-form-item) {
+					width: 47%;
+					margin-bottom: 10px;
+				}
+			}
+			.container {
+				:deep(.ant-form-item) {
+					width: 60%;
+					margin-bottom: 10px;
+				}
+			}
+		}
+		// 右边表单
+		.form_right {
+			width: 40%;
+			padding: 0 10px;
+			background-color: #deeeff;
+			.ant-form-item {
+				margin-bottom: 10px;
+				.ant-input-affix-wrapper {
+					width: 150px;
+				}
+				.suffix {
+					font-size: 12px;
+					padding-left: 5px;
+				}
+			}
+
+			.temp {
+				width: 100%;
+				display: flex;
+				align-items: center;
+				justify-content: space-around;
+
+				.temp_data {
+					width: 90%;
+					margin-left: 10px;
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+					.data_item {
+						display: flex;
+						flex-wrap: wrap;
+						align-items: center;
+						justify-content: center;
+						:deep(.ant-form-item) {
+							margin-inline-end: 0 !important;
+						}
+					}
+				}
+			}
+		}
+	}
+</style>

+ 262 - 0
snowy-admin-web/src/views/biz/monitor/mem/form.vue

@@ -0,0 +1,262 @@
+<template>
+	<xn-form-container
+		:title="formData.id ? '编辑采集器' : '新增采集器'"
+		:width="'800px'"
+		:visible="visible"
+		:destroy-on-close="true"
+		@close="onClose"
+	>
+		<a-form
+			ref="formRef"
+			:model="formData"
+			:rules="formRules"
+			layout="inline"
+			:label-col="{ style: { width: '105px', justifyContent: 'end' } }"
+		>
+			<a-row :gutter="10">
+				<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+					<a-form-item label="设备型号" name="modelName">
+						<a-select
+							ref="select"
+							v-model:value="formData.modelName"
+							:options="modeOptions"
+							placeholder="请选择设备型号"
+							@change="modelNameChange"
+						/>
+					</a-form-item>
+				</a-col>
+				<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+					<a-form-item label="设备名称" name="deviceName">
+						<a-input v-model:value="formData.deviceName" placeholder="请输入设备名称" allow-clear />
+					</a-form-item>
+				</a-col>
+				<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+					<a-form-item label="设备编号" name="deviceCode">
+						<a-input v-model:value="formData.deviceCode" placeholder="请输入设备编号" allow-clear :maxlength="10" />
+					</a-form-item>
+				</a-col>
+				<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+					<a-form-item label="传感器总路数" name="sensorCount">
+						<a-input-number
+							v-model:value="formData.sensorCount"
+							placeholder="请输入传感器总路数"
+							allow-clear
+							:min="1"
+							:max="sensorRouteMax"
+							style="width: 100%"
+							:precision="0"
+							disabled
+						/>
+					</a-form-item>
+				</a-col>
+				<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+					<a-form-item label="设备状态" name="status">
+						<a-select ref="select" v-model:value="formData.status" placeholder="请选择设备状态">
+							<a-select-option value="1">正常</a-select-option>
+							<a-select-option value="2">闲置</a-select-option>
+						</a-select>
+					</a-form-item>
+				</a-col>
+				<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+					<a-form-item label="排序:" name="sortCode">
+						<a-input-number class="xn-wd" v-model:value="formData.sortCode" :max="100" />
+					</a-form-item>
+				</a-col>
+
+				<div
+					class="form_item"
+					v-if="modeData.sensorType == 'W' || modeData.sensorType == 'WS' || modeData.sensorType == 'WSC'"
+				>
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="温度上限" name="temperatureUp">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.temperatureUp"
+								placeholder="请输入温度上限"
+								allow-clear
+								disabled
+								style="width: 100%"
+							/>
+						</a-form-item>
+					</a-col>
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="温度下限" name="temperatureDown">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.temperatureDown"
+								placeholder="请输入温度下限"
+								allow-clear
+								disabled
+								style="width: 100%"
+							/>
+						</a-form-item>
+					</a-col>
+				</div>
+				<div class="form_item" v-if="modeData.sensorType == 'WS' || modeData.sensorType == 'WSC'">
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="湿度上限" name="humidityUp">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.humidityUp"
+								placeholder="请输入湿度上限"
+								allow-clear
+								disabled
+								style="width: 100%"
+							/>
+						</a-form-item>
+					</a-col>
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="湿度下限" name="humidityDown">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.humidityDown"
+								placeholder="请输入湿度下限"
+								allow-clear
+								disabled
+								style="width: 100%"
+							/>
+						</a-form-item>
+					</a-col>
+				</div>
+				<div class="form_item" v-if="modeData.sensorType == 'WSC'">
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="CO2上限" name="co2Up">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.co2Up"
+								placeholder="请输入CO2上限"
+								allow-clear
+								disabled
+								style="width: 100%"
+							/>
+						</a-form-item>
+					</a-col>
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="CO2下限" name="co2Down">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.co2Down"
+								placeholder="请输入CO2下限"
+								allow-clear
+								disabled
+								style="width: 100%"
+							/>
+						</a-form-item>
+					</a-col>
+				</div>
+			</a-row>
+		</a-form>
+
+		<template #footer>
+			<a-button class="xn-mr8" @click="onClose">退出</a-button>
+			<a-button type="primary" @click="onSubmit">保存</a-button>
+		</template>
+	</xn-form-container>
+</template>
+
+<script setup>
+	import { required, rules } from '@/utils/formRules'
+	import devicetypeApi from '@/api/basicset/devicetypeApi'
+	import memApi from '@/api/basicset/memApi'
+	const emit = defineEmits({ successful: null })
+	const visible = ref(false) // 默认是关闭状态
+	const formRef = ref()
+	const formData = ref({ sortCode: 99 }) // 表单数据
+	const modeOptions = ref([]) // 设备型号
+	const sensorRouteMax = ref(1) //传感器最大值默认1
+
+	// 默认要校验的
+	const formRules = {
+		deviceName: [required('请输入设备名称')],
+		deviceCode: [required('请输入设备编号'), rules.number],
+		modelName: [required('请选择设备型号')],
+		status: [required('请选择设备状态')],
+		sensorCount: [required('请输入传感器总路数')]
+	}
+
+	// 获取设备型号
+	const loadData = () => {
+		devicetypeApi.devicetypeList().then((res) => {
+			modeOptions.value = (res || []).map((item) => {
+				return {
+					...item,
+					value: item['id'],
+					label: item['name']
+				}
+			})
+		})
+	}
+
+	const modeData = ref({ sensorType: '' }) //选中的设备型号
+
+	// 选中设备型号获取上下限值
+	const modelNameChange = (value) => {
+		if (value) {
+			const data = modeOptions.value.find((item) => item.value === value)
+			modeData.value = data
+
+			formData.value.temperatureUp = data.temperatureUp
+			formData.value.temperatureDown = data.temperatureDown
+			formData.value.humidityUp = data.humidityUp
+			formData.value.humidityDown = data.humidityDown
+			formData.value.co2Up = data.co2Up
+			formData.value.co2Down = data.co2Down
+			formData.value.sensorCount = data.sensorCount
+			sensorRouteMax.value = data.sensorCount
+		}
+	}
+
+	// 打开抽屉
+	const onOpen = (record) => {
+		modeData.value = { sensorType: '' }
+		loadData()
+		visible.value = true
+		if (record) {
+			formData.value = Object.assign({}, record)
+			setTimeout(() => {
+				modelNameChange(formData.value.modelName)
+			}, 300)
+		} else {
+			formData.value = { sortCode: 99 }
+		}
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		formRef.value.resetFields()
+		visible.value = false
+	}
+
+	// 验证并提交数据
+	const onSubmit = () => {
+		formRef.value
+			.validate()
+			.then(() => {
+				memApi.submitForm(formData.value, formData.value.id).then(() => {
+					onClose()
+					emit('successful')
+				})
+			})
+			.catch((error) => {
+				console.log(error)
+			})
+	}
+
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		onOpen
+	})
+</script>
+
+<style lang="less" scoped>
+	:deep(.ant-form-item) {
+		width: 100%;
+		margin-bottom: 10px;
+	}
+	.form_item {
+		width: 100%;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		flex-wrap: wrap;
+	}
+</style>

+ 209 - 0
snowy-admin-web/src/views/biz/monitor/mem/index.vue

@@ -0,0 +1,209 @@
+<template>
+	<div class="table_item">
+		<s-table
+			ref="tableRef"
+			:columns="columns"
+			:data="loadData"
+			:row-key="(record) => record.id"
+			bordered
+			@resizeColumn="handleResizeColumn"
+		>
+			<template #operator>
+				<!-- 搜索区域 -->
+				<div class="table-search">
+					<div class="table-search-form">
+						<a-row :gutter="10">
+							<a-form
+								ref="searchFormRef"
+								name="advanced_search"
+								layout="inline"
+								:label-col="{ style: { width: '70px', justifyContent: 'end' } }"
+								:model="searchFormState"
+								class="ant-advanced-search-form"
+							>
+								<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8"
+									><a-form-item label="设备名称" name="deviceName">
+										<a-input v-model:value="searchFormState.deviceName" placeholder="请输入设备名称" /> </a-form-item
+								></a-col>
+								<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8">
+									<a-form-item label="设备编号" name="deviceCode">
+										<a-input v-model:value="searchFormState.deviceCode" placeholder="请输入设备编号" /> </a-form-item
+								></a-col>
+							</a-form>
+						</a-row>
+					</div>
+					<div class="table-search-buttons">
+						<a-button type="primary" @click="tableRef.refresh(true)">查询</a-button>
+						<a-button class="xn-mg08" @click="reset">重置</a-button>
+					</div>
+				</div>
+
+				<!-- 其他操作区域 -->
+				<div class="table-head-btn">
+					<div class="btn-left">
+						<a-button type="primary" @click="formRef.onOpen()">
+							<template #icon><plus-outlined /></template>新增
+						</a-button>
+					</div>
+				</div>
+			</template>
+			<template #bodyCell="{ column, record, index }">
+				<template v-if="column.dataIndex === 'index'">{{ index + 1 }} </template>
+				<template v-if="column.dataIndex === 'status'">
+					<span>
+						<a-tag :color="record.status == '1' ? '#87d068' : '#cd201f'">
+							{{ record.status == '1' ? '正常' : '闲置' }}
+						</a-tag>
+					</span>
+				</template>
+				<template v-if="column.dataIndex === 'action'">
+					<a-button type="link" size="small" @click="formRef.onOpen(record)">编辑</a-button>
+					<a-popconfirm title="确定要删除吗?" @confirm="deleteData(record)">
+						<a-button type="link" danger size="small">删除</a-button>
+					</a-popconfirm>
+				</template>
+			</template>
+		</s-table>
+
+		<Form ref="formRef" @successful="tableRef.refresh(true)" />
+		<configForm ref="configformRef" @successful="tableRef.refresh(true)" />
+	</div>
+</template>
+
+<script setup>
+	import memApi from '@/api/basicset/memApi'
+	import Form from './form.vue'
+	import configForm from './configForm.vue'
+	const formRef = ref()
+	const configformRef = ref()
+	const searchFormRef = ref()
+	const searchFormState = ref({})
+	const tableRef = ref()
+
+	const columns = [
+		{
+			title: '序号',
+			dataIndex: 'index',
+			align: 'center',
+			width: 50
+		},
+		{
+			title: '设备名称',
+			dataIndex: 'deviceName',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '设备编号',
+			dataIndex: 'deviceCode',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '设备型号',
+			dataIndex: 'typeName',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '最后心跳时间',
+			dataIndex: 'lastHeartbeatTime',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '设备状态',
+			dataIndex: 'status',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+
+		{
+			title: '传感器总路数',
+			dataIndex: 'sensorCount',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '最近登录时间',
+			dataIndex: 'lastLoginTime',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '排序码',
+			dataIndex: 'sortCode',
+			align: 'center',
+			ellipsis: true,
+			resizable: true,
+			sorter: true
+		},
+
+		{
+			title: '操作',
+			dataIndex: 'action',
+			align: 'center',
+			width: 150
+		}
+	]
+	// 可伸缩列
+	const handleResizeColumn = (w, col) => {
+		col.width = w
+	}
+
+	const loadData = (parameter) => {
+		return memApi.memPage(Object.assign(parameter, searchFormState.value)).then((res) => {
+			return res
+		})
+	}
+	// 重置
+	const reset = () => {
+		searchFormRef.value.resetFields()
+		tableRef.value.refresh(true)
+	}
+	// 删除
+	const deleteData = (record) => {
+		let params = [
+			{
+				id: record.id
+			}
+		]
+		memApi.memDelete(params).then(() => {
+			tableRef.value.refresh(true)
+			reset()
+		})
+	}
+</script>
+
+<style lang="less" scoped>
+	.table_item {
+		padding: 15px 20px;
+		background-color: #ffffff;
+		:deep(.ant-table-pagination-right) {
+			justify-content: center !important;
+		}
+	}
+	.list {
+		width: 60%;
+		padding: 10px 20px;
+		border-radius: 10px;
+		background-color: #ffffff;
+
+		:deep(.ant-descriptions) {
+			margin-bottom: 10px;
+		}
+		:deep(.ant-descriptions-header) {
+			margin: 0;
+			padding: 10px;
+			border: 1px solid #f0f0f0;
+			border-bottom: none;
+		}
+	}
+</style>

+ 96 - 0
snowy-admin-web/src/views/biz/monitor/object/form.vue

@@ -0,0 +1,96 @@
+<template>
+	<xn-form-container
+		:title="formData.id ? '编辑监控对象' : '新增监控对象'"
+		:width="'500px'"
+		:visible="visible"
+		:destroy-on-close="true"
+		@close="onClose"
+	>
+		<a-form
+			ref="formRef"
+			:model="formData"
+			:rules="formRules"
+			layout="inline"
+			:label-col="{ style: { width: '100px', justifyContent: 'end' } }"
+		>
+			<a-form-item label="对象名称" name="name">
+				<a-input v-model:value="formData.name" placeholder="请输入对象名称" allow-clear />
+			</a-form-item>
+			<a-form-item label="对象状态" name="status">
+				<a-select ref="select" v-model:value="formData.status" placeholder="请选择对象状态">
+					<a-select-option value="1">正常</a-select-option>
+					<a-select-option value="2">停用</a-select-option>
+				</a-select>
+			</a-form-item>
+			<a-form-item label="监控位置区域">
+				<a-input v-model:value="formData.monitorPoint" placeholder="请输入监控位置区域" allow-clear />
+			</a-form-item>
+		</a-form>
+
+		<template #footer>
+			<a-button class="xn-mr8" @click="onClose">退出</a-button>
+			<a-button type="primary" @click="onSubmit">保存</a-button>
+		</template>
+	</xn-form-container>
+</template>
+
+<script setup>
+	import { required } from '@/utils/formRules'
+	import setupApi from '@/api/basicset/setupApi'
+
+	// 默认是关闭状态
+	const visible = ref(false)
+	const emit = defineEmits({ successful: null })
+	const formRef = ref()
+
+	// 默认要校验的
+	const formRules = {
+		name: [required('请输入对象名称')],
+		status: [required('请选择对象状态')]
+	}
+
+	// 表单数据
+	const formData = ref({})
+
+	// 打开抽屉
+	const onOpen = (record) => {
+		visible.value = true
+		if (record) {
+			formData.value = Object.assign({}, record)
+		} else {
+			formData.value = {}
+		}
+	}
+	// 关闭抽屉
+	const onClose = () => {
+		formRef.value.resetFields()
+		visible.value = false
+	}
+
+	// 验证并提交数据
+	const onSubmit = () => {
+		formRef.value
+			.validate()
+			.then(() => {
+				setupApi.submitForm(formData.value, formData.value.id).then(() => {
+					onClose()
+					emit('successful')
+				})
+			})
+			.catch((error) => {
+				console.log(error)
+			})
+	}
+
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		onOpen
+	})
+</script>
+
+<style lang="less" scoped>
+	:deep(.ant-form-item) {
+		width: 100%;
+		margin-bottom: 10px;
+	}
+</style>

+ 254 - 0
snowy-admin-web/src/views/biz/monitor/object/index.vue

@@ -0,0 +1,254 @@
+<template>
+	<div class="table_item">
+		<s-table
+			ref="tableRef"
+			:columns="columns"
+			:data="loadData"
+			:row-key="(record) => record.id"
+			bordered
+			@resizeColumn="handleResizeColumn"
+		>
+			<template #operator>
+				<!-- 搜索区域 -->
+				<div class="table-search">
+					<div class="table-search-form">
+						<a-row :gutter="10">
+							<a-form
+								ref="searchFormRef"
+								name="advanced_search"
+								layout="inline"
+								:label-col="{ style: { width: '70px', justifyContent: 'end' } }"
+								:model="searchFormState"
+								class="ant-advanced-search-form"
+							>
+								<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8"
+									><a-form-item label="对象名称" name="name">
+										<a-input v-model:value="searchFormState.name" placeholder="请输入对象名称" /> </a-form-item
+								></a-col>
+							</a-form>
+						</a-row>
+					</div>
+					<div class="table-search-buttons">
+						<a-button type="primary" @click="tableRef.refresh(true)">查询</a-button>
+						<a-button class="xn-mg08" @click="reset">重置</a-button>
+					</div>
+				</div>
+
+				<!-- 其他操作区域 -->
+				<div class="table-head-btn">
+					<div class="btn-left">
+						<a-button type="primary" @click="formRef.onOpen()">
+							<template #icon><plus-outlined /></template>新增
+						</a-button>
+					</div>
+					<!-- <div class="btn-right">
+						<a-button class="xn-mg08">
+							<template #icon><CloudUploadOutlined /> </template>导入
+						</a-button>
+						<a-button>
+							<template #icon><download-outlined /></template>导出
+						</a-button>
+					</div> -->
+				</div>
+			</template>
+			<template #bodyCell="{ column, record, index }">
+				<!-- 序号 -->
+				<template v-if="column.dataIndex === 'index'">{{ index + 1 }} </template>
+				<template v-if="column.dataIndex === 'status'">
+					<span>
+						<a-tag :color="record.status == '1' ? '#87d068' : '#cd201f'">
+							{{ record.status == '1' ? '正常' : '停用' }}
+						</a-tag>
+					</span>
+				</template>
+				<template v-if="column.dataIndex === 'sensorType'">
+					{{ $TOOL.dictTypeData('SENSORTYPE', record.sensorType) }}
+				</template>
+				<template v-if="column.dataIndex === 'action'">
+					<a-button type="link" size="small" @click="formRef.onOpen(record)">编辑</a-button>
+					<a-popconfirm title="确定要删除吗?" @confirm="deleteData(record)">
+						<a-button type="link" danger size="small">删除</a-button>
+					</a-popconfirm>
+					<a-button type="link" size="small" @click="leaveFor('/biz/monitor/point', record)">监测点配置</a-button>
+				</template>
+			</template>
+			<!-- <template #expandedRowRender="{ record }">
+				<a-tabs v-model:activeKey="record.activeKey" size="small" type="card">
+					<a-tab-pane key="1" tab="基本信息">
+						<div class="list">
+							<a-descriptions bordered size="small">
+								<a-descriptions-item label="对象名称" :span="2">{{ record.name }}</a-descriptions-item>
+								<a-descriptions-item label="对象编号" :span="2">{{ record.dxcode }}</a-descriptions-item>
+								<a-descriptions-item label="品牌">{{ record.pp }}</a-descriptions-item>
+								<a-descriptions-item label="型号">{{ record.xh }}</a-descriptions-item>
+								<a-descriptions-item label="类型">{{ record.type }}</a-descriptions-item>
+								<a-descriptions-item label="使用状态">{{ record.syzt }}</a-descriptions-item>
+								<a-descriptions-item label="报警状态">{{ record.bjzt }}</a-descriptions-item>
+								<a-descriptions-item label="位置">{{ record.wz }}</a-descriptions-item>
+							</a-descriptions>
+						</div>
+					</a-tab-pane>
+					<a-tab-pane key="2" tab="冷链设备">
+						<div class="list">
+							<a-descriptions bordered size="small">
+								<a-descriptions-item label="冷链编号" :span="2">{{ record.llcode }}</a-descriptions-item>
+								<a-descriptions-item label="对应路数" :span="2">{{ record.dyls }}</a-descriptions-item>
+								<a-descriptions-item label="所属中继" :span="2">{{ record.sszj }}</a-descriptions-item>
+							</a-descriptions>
+						</div>
+					</a-tab-pane>
+					<a-tab-pane key="3" tab="温湿度设定">
+						<div class="list">
+							<a-descriptions title="报警上下限" bordered size="small">
+								<a-descriptions-item label="温度上限(℃)" :span="2">{{ record.wdsx }}</a-descriptions-item>
+								<a-descriptions-item label="温度下限(℃)" :span="2">{{ record.wdxx }}</a-descriptions-item>
+								<a-descriptions-item label="湿度上限(%)" :span="2">{{ record.sdsx }}</a-descriptions-item>
+								<a-descriptions-item label="湿度下限(%)" :span="2">{{ record.sdxx }}</a-descriptions-item>
+							</a-descriptions>
+
+							<a-descriptions title="温湿度修正" bordered size="small">
+								<a-descriptions-item label="温度(-10~10)(℃)" :span="2">{{ record.wdxz }}</a-descriptions-item>
+								<a-descriptions-item label="湿度(-20~20)(%)" :span="2">{{ record.sdxz }}</a-descriptions-item>
+							</a-descriptions>
+						</div>
+					</a-tab-pane>
+					<a-tab-pane key="4" tab="报警信息">
+						<div class="list">
+							<a-descriptions title="对象信息" bordered size="small">
+								<a-descriptions-item label="对象名称" :span="2">{{ record.name }}</a-descriptions-item>
+								<a-descriptions-item label="监控点名称" :span="2">{{ record.jkd }}</a-descriptions-item>
+								<a-descriptions-item label="报警级别" :span="2">{{ '1' }}</a-descriptions-item>
+								<a-descriptions-item label="延时时间" :span="2">{{ '0' }}</a-descriptions-item>
+								<a-descriptions-item label="报警频率" :span="2">{{ '1' }}</a-descriptions-item>
+
+								<a-descriptions-item label="执行间隔" :span="2">{{ '10' }}</a-descriptions-item>
+								<a-descriptions-item label="是否逐级报警" :span="2">{{ '否' }}</a-descriptions-item>
+								<a-descriptions-item label="逐级报警时间间隔" :span="2">{{ '0' }}</a-descriptions-item>
+								<a-descriptions-item label="允许报警开始时间" :span="2"></a-descriptions-item>
+								<a-descriptions-item label="允许报警结束时间" :span="2"></a-descriptions-item>
+							</a-descriptions>
+
+							<a-descriptions title="短信报警" bordered size="small">
+								<a-descriptions-item label="提醒人姓名" :span="2">{{ '欧阳天添' }}</a-descriptions-item>
+								<a-descriptions-item label="接收电话" :span="2">{{ '13476548750' }}</a-descriptions-item>
+							</a-descriptions>
+						</div>
+					</a-tab-pane>
+					<a-tab-pane key="5" tab="温度记录">
+						<div class="list">温度记录</div>
+					</a-tab-pane>
+				</a-tabs>
+			</template> -->
+		</s-table>
+		<Form ref="formRef" @successful="tableRef.refresh(true)" />
+	</div>
+</template>
+
+<script setup>
+	import router from '@/router'
+	import setupApi from '@/api/basicset/setupApi'
+	import Form from './form.vue'
+	const formRef = ref()
+
+	const searchFormRef = ref()
+	const searchFormState = ref({})
+	const tableRef = ref()
+	const columns = [
+		{
+			title: '序号',
+			dataIndex: 'index',
+			align: 'center',
+			width: 50
+		},
+		{
+			title: '对象名称',
+			dataIndex: 'name',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '对象状态',
+			dataIndex: 'status',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '监控位置区域',
+			dataIndex: 'monitorPoint',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '操作',
+			dataIndex: 'action',
+			align: 'center',
+			width: 200
+		}
+	]
+	// 可伸缩列
+	const handleResizeColumn = (w, col) => {
+		col.width = w
+	}
+
+	const loadData = (parameter) => {
+		return setupApi.setupPage(Object.assign(parameter, searchFormState.value)).then((res) => {
+			return res
+		})
+	}
+
+	// 跳转到监测点界面
+	const leaveFor = (url, record) => {
+		router.push({
+			path: url,
+			query: {
+				id: record.id
+			}
+		})
+	}
+	// 重置
+	const reset = () => {
+		searchFormRef.value.resetFields()
+		tableRef.value.refresh(true)
+	}
+	// 删除
+	const deleteData = (record) => {
+		let params = [
+			{
+				id: record.id
+			}
+		]
+		setupApi.setupDelete(params).then(() => {
+			tableRef.value.refresh(true)
+			reset()
+		})
+	}
+</script>
+
+<style lang="less" scoped>
+	.table_item {
+		padding: 15px 20px;
+
+		:deep(.ant-table-pagination-right) {
+			justify-content: center !important;
+		}
+	}
+	.list {
+		width: 60%;
+		padding: 10px 20px;
+		border-radius: 10px;
+		background-color: #ffffff;
+
+		:deep(.ant-descriptions) {
+			margin-bottom: 10px;
+		}
+		:deep(.ant-descriptions-header) {
+			margin: 0;
+			padding: 10px;
+			border: 1px solid #f0f0f0;
+			border-bottom: none;
+		}
+	}
+</style>

+ 434 - 0
snowy-admin-web/src/views/biz/monitor/point/form.vue

@@ -0,0 +1,434 @@
+<template>
+	<xn-form-container
+		:title="formData.id ? '编辑点位' : '新增点位'"
+		:width="'800px'"
+		:visible="visible"
+		:destroy-on-close="true"
+		@close="onClose"
+	>
+		<a-form
+			ref="formRef"
+			:model="formData"
+			:rules="formRules"
+			layout="inline"
+			:label-col="{ style: { width: '110px', justifyContent: 'end' } }"
+		>
+			<a-row :gutter="10">
+				<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+					<a-form-item label="监控对象" name="monitorTargetId">
+						<a-select
+							ref="select"
+							v-model:value="formData.monitorTargetId"
+							:options="monitorTargetOptions"
+							placeholder="请选择监控对象"
+							disabled
+						/>
+					</a-form-item>
+				</a-col>
+				<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+					<a-form-item label="点位名称" name="name">
+						<a-input v-model:value="formData.name" placeholder="请输入点位名称" allow-clear />
+					</a-form-item>
+				</a-col>
+				<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+					<a-form-item label="传感器类型" name="sensorType">
+						<a-select
+							ref="select"
+							v-model:value="formData.sensorType"
+							:options="sensorOptions"
+							placeholder="请选择传感器类型"
+							@change="sensorTypeChange"
+						/>
+					</a-form-item>
+				</a-col>
+
+				<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+					<a-form-item label="监控设备" name="monitorDeviceId">
+						<a-select
+							ref="select"
+							v-model:value="formData.monitorDeviceId"
+							:options="memListOptions"
+							placeholder="请选择监控设备"
+							@change="monitorDeviceIdChange"
+						/>
+					</a-form-item>
+				</a-col>
+
+				<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" v-if="formData.monitorDeviceId">
+					<a-form-item label="传感器编号" name="sensorCode">
+						<a-input v-model:value="formData.sensorCode" disabled placeholder="请输入传感器编号" allow-clear />
+					</a-form-item>
+				</a-col>
+
+				<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" v-if="formData.monitorDeviceId">
+					<a-form-item label="传感器路数" name="sensorRoute">
+						<a-radio-group v-model:value="formData.sensorRoute">
+							<a-radio-button v-for="i in sensorRouteMax" :key="i" :value="i" :disabled="isRouteDisabled(i)">
+								{{ i }}路
+							</a-radio-button>
+						</a-radio-group>
+					</a-form-item>
+				</a-col>
+
+				<!-- 报警接收人 -->
+				<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+					<a-form-item label="报警接收人" name="alarmUsers">
+						<a-select
+							v-model:value="formData.alarmUsers"
+							mode="multiple"
+							style="width: 100%"
+							placeholder="请选择报警接收人"
+							:options="alarmUsersOptions"
+							:field-names="{ label: 'userName', value: 'openId' }"
+						></a-select>
+					</a-form-item>
+				</a-col>
+
+				<div
+					class="form_item"
+					v-if="formData.sensorType == 'W' || formData.sensorType == 'WS' || formData.sensorType == 'WSC'"
+				>
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="温度上限" name="temperatureUp">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.temperatureUp"
+								placeholder="请输入温度上限"
+								allow-clear
+								:min="temperatureMax.downMax"
+								:max="temperatureMax.upMax"
+								style="width: 100%"
+								:disabled="temperatureMax.upMax == null || temperatureMax.upMax == undefined"
+							/>
+						</a-form-item>
+					</a-col>
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="温度下限" name="temperatureDown">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.temperatureDown"
+								placeholder="请输入温度下限"
+								allow-clear
+								:min="temperatureMax.downMax"
+								:max="temperatureMax.upMax"
+								style="width: 100%"
+								:disabled="!temperatureMax.downMax == null || temperatureMax.downMax == undefined"
+							/>
+						</a-form-item>
+					</a-col>
+				</div>
+
+				<div class="form_item" v-if="formData.sensorType == 'WS' || formData.sensorType == 'WSC'">
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="湿度上限" name="humidityUp">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.humidityUp"
+								placeholder="请输入湿度上限"
+								allow-clear
+								:min="humidityMax.downMax"
+								:max="humidityMax.upMax"
+								style="width: 100%"
+								:disabled="!humidityMax.upMax"
+							/>
+						</a-form-item>
+					</a-col>
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="湿度下限" name="humidityDown">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.humidityDown"
+								placeholder="请输入湿度下限"
+								allow-clear
+								:min="humidityMax.downMax"
+								:max="humidityMax.upMax"
+								style="width: 100%"
+								:disabled="!humidityMax.downMax"
+							/>
+						</a-form-item>
+					</a-col>
+				</div>
+
+				<div class="form_item" v-if="formData.sensorType == 'WSC'">
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="CO2上限" name="co2Up">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.co2Up"
+								placeholder="请输入CO2上限"
+								allow-clear
+								:max="co2Max.upMax"
+								style="width: 100%"
+							/>
+						</a-form-item>
+					</a-col>
+					<a-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
+						<a-form-item label="CO2下限" name="co2Down">
+							<a-input-number
+								id="inputNumber"
+								v-model:value="formData.co2Down"
+								placeholder="请输入CO2下限"
+								allow-clear
+								:max="co2Max.downMax"
+								style="width: 100%"
+							/>
+						</a-form-item>
+					</a-col>
+				</div>
+			</a-row>
+		</a-form>
+
+		<template #footer>
+			<a-button class="xn-mr8" @click="onClose">退出</a-button>
+			<a-button type="primary" @click="onSubmit">保存</a-button>
+		</template>
+	</xn-form-container>
+</template>
+
+<script setup>
+	import tool from '@/utils/tool'
+	import { required } from '@/utils/formRules'
+	import locationApi from '@/api/basicset/locationApi.js'
+	import setupApi from '@/api/basicset/setupApi'
+	import memApi from '@/api/basicset/memApi'
+	const emit = defineEmits({ successful: null })
+	const visible = ref(false) // 默认是关闭状态
+	const formRef = ref()
+	const formData = ref({ alarmUsers: [] }) // 表单数据
+	const sensorOptions = tool.dictList('SENSORTYPE') // 传感器类型
+	const monitorTargetOptions = ref([]) // 监控对象
+	const memListOptions = ref([]) // 监控设备数据
+	const alarmUsersOptions = ref([]) // 报警接收人数据
+
+	const temperatureMax = ref({ upMax: Infinity, downMax: Infinity }) // 温度最大值
+	const humidityMax = ref({ upMax: Infinity, downMax: Infinity }) // 湿度最大值
+	const co2Max = ref({ upMax: Infinity, downMax: Infinity }) // CO2最大值
+
+	const sensorRouteMax = ref(1) //传感器最大值默认1
+
+	// 默认要校验的
+	const formRules = {
+		name: [required('请输入区域名称')],
+		monitorTargetId: [required('请选择监控对象')],
+		monitorDeviceId: [required('请选择监控设备')],
+		sensorType: [required('请选择传感器类型')],
+		sensorRoute: [required('请输入传感器路数')],
+		alarmUsers: [required('请选择报警接收人')],
+		temperatureUp: [{ validator: validateTemperature, trigger: ['change', 'blur'] }],
+		temperatureDown: [{ validator: validateTemperature, trigger: ['change', 'blur'] }],
+		humidityUp: [{ validator: validateHumidity, trigger: ['change', 'blur'] }],
+		humidityDown: [{ validator: validateHumidity, trigger: ['change', 'blur'] }],
+		co2Up: [{ validator: validateCo2, trigger: ['change', 'blur'] }],
+		co2Down: [{ validator: validateCo2, trigger: ['change', 'blur'] }]
+	}
+
+	// 温度上下限校验
+	function validateTemperature(rule, value, callback) {
+		if (rule.field == 'temperatureDown') {
+			if (formData.value.temperatureUp !== null && value >= formData.value.temperatureUp) {
+				callback(new Error('温度下限不能大于或等于温度上限'))
+			} else {
+				callback()
+			}
+		} else if (rule.field == 'temperatureUp') {
+			if (formData.value.temperatureDown !== null && value <= formData.value.temperatureDown) {
+				callback(new Error('温度上限不能小于或等于温度下限'))
+			} else {
+				callback()
+			}
+		}
+	}
+
+	// 湿度上下限校验
+	function validateHumidity(rule, value, callback) {
+		if (rule.field == 'humidityDown') {
+			if (formData.value.humidityUp !== null && value >= formData.value.humidityUp) {
+				callback(new Error('湿度下限不能大于或等于湿度上限'))
+			} else {
+				callback()
+			}
+		} else if (rule.field == 'humidityUp') {
+			if (formData.value.humidityDown !== null && value <= formData.value.humidityDown) {
+				callback(new Error('湿度上限不能小于或等于湿度下限'))
+			} else {
+				callback()
+			}
+		}
+	}
+
+	// CO2上下限校验
+	function validateCo2(rule, value, callback) {
+		if (rule.field == 'co2Down') {
+			if (formData.value.co2Up !== null && value >= formData.value.co2Up) {
+				callback(new Error('CO2下限不能大于或等于CO2上限'))
+			} else {
+				callback()
+			}
+		} else if (rule.field == 'co2Up') {
+			if (formData.value.co2Down !== null && value <= formData.value.co2Down) {
+				callback(new Error('CO2上限不能小于或等于CO2下限'))
+			} else {
+				callback()
+			}
+		}
+	}
+
+	// 打开抽屉
+	const onOpen = (record) => {
+		setupListData()
+		visible.value = true
+		if (record) {
+			formData.value = Object.assign({}, record)
+
+			// 报警接收人回显
+			if (formData.value.alarmUsers && formData.value.alarmUsers.length > 0) {
+				formData.value.alarmUsers = formData.value.alarmUsers.map((item) => item.openId)
+			}
+			sensorType.value = formData.value.sensorType
+
+			memListData(sensorType.value)
+		} else {
+			formData.value = { alarmUsers: [] }
+			memListData()
+		}
+	}
+	// 获取监控对象 以及获取报警接收人的数据
+	const setupListData = () => {
+		setupApi.setupList({ isAll: false }).then((res) => {
+			monitorTargetOptions.value = (res || []).map((item) => {
+				return {
+					...item,
+					value: item['id'],
+					label: item['name']
+				}
+			})
+			const list = monitorTargetOptions.value.find((item) => item.value === formData.value.monitorTargetId)
+			alarmUsersOptions.value = list.alarmUsers || []
+		})
+	}
+
+	const sensorType = ref() //选中的传感器类型
+	// 监控设备通过传感器类型来获取, sensorType可为空
+	const memListData = (value) => {
+		memApi.memList({ isAll: false, sensorType: value }).then((res) => {
+			memListOptions.value = (res || []).map((item) => {
+				return {
+					...item,
+					value: item['id'],
+					label: item['deviceName'],
+					modelName: item['modelName'],
+					deviceCode: item['deviceCode'],
+					sensorCount: item['sensorCount']
+				}
+			})
+		})
+	}
+
+	// 选中传感器类型
+	const sensorTypeChange = (value) => {
+		if (value) {
+			formData.value.monitorDeviceId = null
+			formData.value.sensorRoute = null
+			sensorRouteMax.value = 1
+			sensorType.value = value
+			memListData(value)
+		}
+	}
+
+	// 选中监控设备
+	const monitorDeviceIdChange = (value) => {
+		if (value) {
+			askRegionByDeviceIdData(value)
+			const data = memListOptions.value.find((item) => item.value === value)
+			formData.value = {
+				...formData.value,
+				sensorCode: data.deviceCode, //传感器编号
+				modelName: data.modelName, //监控设备型号
+				deviceCode: data.deviceCode, //冷链编号
+				temperatureUp: data.temperatureUp, //温度上限
+				temperatureDown: data.temperatureDown, //温度下限
+				humidityUp: data.humidityUp, //湿度上限
+				humidityDown: data.humidityDown, //湿度下限
+				co2Up: data.co2Up, //CO2上限
+				co2Down: data.co2Down //CO2下限
+			}
+			sensorRouteMax.value = data.sensorCount //传感器路数的最大值
+
+			temperatureMax.value = {
+				upMax: data.temperatureUp,
+				downMax: data.temperatureDown
+			}
+			humidityMax.value = {
+				upMax: data.humidityUp,
+				downMax: data.humidityDown
+			}
+			co2Max.value = {
+				upMax: data.co2Up,
+				downMax: data.co2Down
+			}
+		}
+	}
+
+	const useSensorRoute = ref([]) //已使用的传感器路数
+	// 根据检测设备id获取已使用的路数
+	const askRegionByDeviceIdData = (value) => {
+		if (value) {
+			memApi.getRegionByDeviceIdData({ deviceId: value }).then((res) => {
+				useSensorRoute.value = res
+			})
+		}
+	}
+
+	// 检查该路是否已被禁用
+	const isRouteDisabled = (route) => {
+		// 通过遍历useSensorRoute中的数据,判断该路是否已被占用
+		return useSensorRoute.value.some((item) => item.sensorRoute === route)
+	}
+
+	// 验证并提交数据
+	const onSubmit = () => {
+		formRef.value
+			.validate()
+			.then(() => {
+				const parameter = {
+					...formData.value,
+					alarmUsers: alarmUsersOptions.value.filter((user) => formData.value.alarmUsers.includes(user.openId))
+				}
+				locationApi.submitForm(parameter, formData.value.id).then(() => {
+					onClose()
+					emit('successful')
+				})
+			})
+			.catch((error) => {
+				console.log(error)
+			})
+	}
+
+	// 关闭抽屉
+	const onClose = () => {
+		memListOptions.value = []
+
+		formRef.value.resetFields()
+		visible.value = false
+	}
+
+	// 调用这个函数将子组件的一些数据和方法暴露出去
+	defineExpose({
+		onOpen
+	})
+</script>
+
+<style lang="less" scoped>
+	:deep(.ant-form-item) {
+		width: 100%;
+		margin-bottom: 10px;
+	}
+
+	.form_item {
+		width: 100%;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		flex-wrap: wrap;
+	}
+</style>

+ 184 - 0
snowy-admin-web/src/views/biz/monitor/point/index.vue

@@ -0,0 +1,184 @@
+<template>
+	<div class="table_item">
+		<a-page-header title="返回" sub-title="监测点配置" @back="goBack" style="padding: 20px 0" />
+		<s-table
+			ref="tableRef"
+			:columns="columns"
+			:data="loadData"
+			:row-key="(record) => record.id"
+			bordered
+			@resizeColumn="handleResizeColumn"
+		>
+			<template #operator>
+				<!-- 搜索区域 -->
+				<div class="table-search">
+					<div class="table-search-form">
+						<a-row :gutter="10">
+							<a-form
+								ref="searchFormRef"
+								name="advanced_search"
+								layout="inline"
+								:label-col="{ style: { width: '70px', justifyContent: 'end' } }"
+								:model="searchFormState"
+								class="ant-advanced-search-form"
+							>
+								<a-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8"
+									><a-form-item label="点位名称" name="name">
+										<a-input v-model:value="searchFormState.name" placeholder="请输入点位名称" /> </a-form-item
+								></a-col>
+							</a-form>
+						</a-row>
+					</div>
+					<div class="table-search-buttons">
+						<a-button type="primary" @click="tableRef.refresh(true)">查询</a-button>
+						<a-button class="xn-mg08" @click="reset">重置</a-button>
+					</div>
+				</div>
+
+				<!-- 其他操作区域 -->
+				<div class="table-head-btn">
+					<div class="btn-left">
+						<a-button type="primary" @click="formRef.onOpen({ monitorTargetId: queryId })">
+							<template #icon><plus-outlined /></template>新增
+						</a-button>
+					</div>
+				</div>
+			</template>
+			<template #bodyCell="{ column, record, index }">
+				<!-- 序号 -->
+				<template v-if="column.dataIndex === 'index'">{{ index + 1 }} </template>
+				<template v-if="column.dataIndex === 'action'">
+					<a-button type="link" size="small" @click="formRef.onOpen(record)">编辑</a-button>
+					<a-popconfirm title="确定要删除吗?" @confirm="deleteData(record)">
+						<a-button type="link" danger size="small">删除</a-button>
+					</a-popconfirm>
+				</template>
+			</template>
+		</s-table>
+		<Form ref="formRef" @successful="tableRef.refresh(true)" />
+	</div>
+</template>
+
+<script setup>
+	import locationApi from '@/api/basicset/locationApi.js'
+	import { useRoute } from 'vue-router'
+	import router from '@/router'
+	import Form from './form.vue'
+	const formRef = ref()
+	const searchFormRef = ref()
+	const searchFormState = ref({})
+	const tableRef = ref()
+	const Route = useRoute()
+
+	const columns = [
+		{
+			title: '序号',
+			dataIndex: 'index',
+			align: 'center',
+			width: 50
+		},
+		{
+			title: '区域名称',
+			dataIndex: 'name',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+
+		{
+			title: '监控对象',
+			dataIndex: 'targetName',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '监控设备',
+			dataIndex: 'deviceName',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '传感器编号',
+			dataIndex: 'sensorCode',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '传感器类型',
+			dataIndex: 'sensorType',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+		{
+			title: '传感器路数',
+			dataIndex: 'sensorRoute',
+			align: 'center',
+			ellipsis: true,
+			resizable: true
+		},
+
+		{
+			title: '操作',
+			dataIndex: 'action',
+			align: 'center',
+			width: 150
+		}
+	]
+	// 可伸缩列
+	const handleResizeColumn = (w, col) => {
+		col.width = w
+	}
+
+	const queryId = ref() //监控对象的id
+	onBeforeMount(() => {
+		if (Route.query.id) {
+			queryId.value = Route.query.id
+		}
+	})
+
+	// 返回上一页
+	const goBack = () => {
+		router.go(-1)
+	}
+
+	const loadData = (parameter) => {
+		if (queryId.value) {
+			parameter.monitorTargetId = queryId.value
+			return locationApi.locationPage(Object.assign(parameter, searchFormState.value)).then((res) => {
+				return res
+			})
+		}
+	}
+	// 重置
+	const reset = () => {
+		searchFormRef.value.resetFields()
+		tableRef.value.refresh(true)
+	}
+	// 删除
+	const deleteData = (record) => {
+		let params = [
+			{
+				id: record.id
+			}
+		]
+		locationApi.locationDelete(params).then(() => {
+			tableRef.value.refresh(true)
+			reset()
+		})
+	}
+</script>
+
+<style lang="less" scoped>
+	.table_item {
+		padding: 15px 20px;
+		background-color: #ffffff;
+
+		:deep(.ant-table-pagination-right) {
+			justify-content: center !important;
+		}
+	}
+</style>