feat 反应器页面

This commit is contained in:
Asoka 2025-06-13 10:02:40 +08:00
parent 49cb6bb23d
commit 48971c9dc7
6 changed files with 1337 additions and 0 deletions

84
src/api/admin/reactor.ts Normal file
View File

@ -0,0 +1,84 @@
import { ReactorPageInput, ReactorPageResponse, ReactorOutput, ReactorAddInput, ReactorUpdateInput, ReactorTypeEnumListOutput } from '/@/api/types/ReactorType'
import { RequestParams } from './http-client'
import { ContentType, HttpClient } from './http-client'
export class ReactorApi extends HttpClient {
/**
*
*/
getPage = (data: ReactorPageInput, params: RequestParams = {}) =>
this.request<ReactorPageResponse>({
path: `/api/admin/equ-reactor/get-page`,
method: 'POST',
body: data,
type: ContentType.Json,
...params,
})
/**
*
*/
get = (params: { id: number }, requestParams: RequestParams = {}) =>
this.request<ReactorOutput>({
path: `/api/admin/equ-reactor/get`,
method: 'GET',
query: params,
...requestParams,
})
/**
*
*/
add = (data: ReactorAddInput, params: RequestParams = {}) =>
this.request<any>({
path: `/api/admin/equ-reactor/add`,
method: 'POST',
body: data,
type: ContentType.Json,
...params,
})
/**
*
*/
update = (data: ReactorUpdateInput, params: RequestParams = {}) =>
this.request<any>({
path: `/api/admin/equ-reactor/update`,
method: 'PUT',
body: data,
type: ContentType.Json,
...params,
})
/**
*
*/
softDelete = (params: { id: number }, requestParams: RequestParams = {}) =>
this.request<any>({
path: `/api/admin/equ-reactor/soft-delete`,
method: 'DELETE',
query: params,
...requestParams,
})
/**
*
*/
getReactorTypeEnumList = (params: RequestParams = {}) =>
this.request<ReactorTypeEnumListOutput>({
path: `/api/admin/equ-reactor/get-reactor-type-enum-list`,
method: 'GET',
...params,
})
/**
*
*/
getDeviceStatusEnumList = (params: RequestParams = {}) =>
this.request<ReactorTypeEnumListOutput>({
path: `/api/admin/equ-reactor/get-device-status-enum-list`,
method: 'GET',
...params,
})
}

View File

@ -0,0 +1,114 @@
import { ServiceResponse } from './response';
import { ServiceRequstPage } from './pageInput'
import { PageResponse } from './pageResponse'
// 设备状态枚举
export enum DeviceStatusEnum {
Normal = 0, // 正常
Fault = 1, // 故障
Offline = 2, // 离线
Maintenance = 3 // 维护中
}
// 维护标志枚举
export enum MaintenanceFlagEnum {
None = 0, // 无
Regular = 1, // 定期维护
Emergency = 2 // 紧急维护
}
// 过滤条件
export interface ReactorFilter {
/** 设备编号 */
deviceNo?: string | null
/** 资产编号 */
assetNo?: string | null
/** 产品ID */
productID?: string | null
/** 型号 */
model?: string | null
/** 规格 */
specification?: string | null
/** 房间ID */
roomID?: number | null
/** 负责人ID */
principalId?: number | null
/** 设备状态 */
deviceStatus?: DeviceStatusEnum | null
/** 维护标志 */
maintenanceFlag?: MaintenanceFlagEnum | null
/** 状态 */
status?: boolean | null
/** 是否维护中 */
isMaintenance?: boolean | null
/** 开始时间 */
startTime?: string | null
/** 结束时间 */
endTime?: string | null
}
// 反应器DTO
export interface ReactorDto {
/** 设备反应器ID */
id?: number
/** 排序 */
sort?: number
/** 设备编号 */
deviceNo?: string | null
/** 资产编号 */
assetNo?: string | null
/** 产品ID */
productID?: string | null
/** 型号 */
model?: string | null
/** 规格 */
specification?: string | null
/** 容量g */
capacity?: number
/** 房间ID */
roomID?: number
/** 房间名称 */
roomName?: string | null
/** 负责人ID */
principalId?: number
/** 负责人姓名 */
principalName?: string | null
/** 设备状态 */
deviceStatus?: DeviceStatusEnum
/** 设备状态描述 */
deviceStatusDesc?: string | null
/** 维护标志 */
maintenanceFlag?: MaintenanceFlagEnum
/** 维护标志描述 */
maintenanceFlagDesc?: string | null
/** 错误码 */
errorCode?: string | null
/** 状态(启用禁用) */
status?: boolean
/** 是否维护中 */
isMaintenance?: boolean
/** 维护时间 */
maintenanceTime?: string | null
/** 创建时间 */
createdTime?: string | null
/** 修改时间 */
modifiedTime?: string | null
}
// 反应器类型枚举项
export interface ReactorTypeEnumItem {
/** 枚举值 */
value: number
/** 枚举名称 */
name: string
/** 显示名称/描述 */
label: string
}
// API 类型定义
export type ReactorPageInput = ServiceRequstPage<ReactorFilter>;
export type ReactorPageResponse = ServiceResponse<PageResponse<ReactorDto>>;
export type ReactorOutput = ServiceResponse<ReactorDto[]>;
export type ReactorAddInput = ReactorDto;
export type ReactorUpdateInput = ReactorDto;
export type ReactorTypeEnumListOutput = ServiceResponse<ReactorTypeEnumItem[]>;

View File

@ -0,0 +1,467 @@
<template>
<el-dialog
v-model="dialogVisible"
:title="form.id ? '编辑反应器' : '新增反应器'"
width="900px"
:close-on-click-modal="false"
:close-on-press-escape="false"
destroy-on-close
class="reactor-form-dialog"
>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="100px"
class="reactor-form"
>
<el-tabs v-model="activeTab" class="reactor-tabs">
<el-tab-pane label="基本信息" name="basic">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="设备编号" prop="deviceNo">
<el-input v-model="form.deviceNo" placeholder="请输入设备编号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="资产编号" prop="assetNo">
<el-input v-model="form.assetNo" placeholder="请输入资产编号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产品编号" prop="productID">
<el-input v-model="form.productID" placeholder="请输入产品编号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备型号" prop="model">
<el-input v-model="form.model" placeholder="请输入设备型号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="规格" prop="specification">
<el-input v-model="form.specification" placeholder="请输入规格" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="容量" prop="capacity">
<el-input-number v-model="form.capacity" :min="0" :step="1" style="width: 100%" placeholder="请输入容量" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="房间" prop="roomID">
<el-select v-model="form.roomID" placeholder="请选择房间" style="width: 100%">
<el-option
v-for="item in roomOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="负责人" prop="principalId">
<el-select v-model="form.principalId" placeholder="请选择负责人" style="width: 100%">
<el-option
v-for="item in principalOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备状态" prop="deviceStatus">
<el-select v-model="form.deviceStatus" placeholder="请选择设备状态" style="width: 100%">
<el-option
v-for="item in deviceStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="维护标志" prop="maintenanceFlag">
<el-select v-model="form.maintenanceFlag" placeholder="请选择维护标志" style="width: 100%">
<el-option
v-for="item in maintenanceFlagOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" :min="0" :step="1" style="width: 100%" placeholder="请输入排序" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否维护中" prop="isMaintenance">
<el-switch v-model="form.isMaintenance" />
</el-form-item>
</el-col>
</el-row>
</el-tab-pane>
<el-tab-pane label="设备配置" name="config">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="地秤ID" prop="equScaleId">
<el-input-number v-model="form.equScaleId" :min="0" :step="1" style="width: 100%" placeholder="请输入地秤ID" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="补料秤ID" prop="feedingScaleId">
<el-input-number v-model="form.feedingScaleId" :min="0" :step="1" style="width: 100%" placeholder="请输入补料秤ID" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="继电器ID" prop="relayID">
<el-input-number v-model="form.relayID" :min="0" :step="1" style="width: 100%" placeholder="请输入继电器ID" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="报警器ID" prop="equAlarmId">
<el-input-number v-model="form.equAlarmId" :min="0" :step="1" style="width: 100%" placeholder="请输入报警器ID" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="警告下限" prop="warningLowerLimit">
<el-input-number v-model="form.warningLowerLimit" :min="0" :step="1" style="width: 100%" placeholder="请输入警告下限" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="警告上限" prop="warningUpperLimit">
<el-input-number v-model="form.warningUpperLimit" :min="0" :step="1" style="width: 100%" placeholder="请输入警告上限" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否外置泵" prop="isExternalPump">
<el-switch v-model="form.isExternalPump" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="OPC IP" prop="opcip">
<el-input v-model="form.opcip" placeholder="请输入OPC IP" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="OPC 端口" prop="opcPort">
<el-input v-model="form.opcPort" placeholder="请输入OPC 端口" />
</el-form-item>
</el-col>
</el-row>
</el-tab-pane>
</el-tabs>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" :loading="loading" @click="handleSubmit"> </el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { ReactorApi } from '/@/api/admin/reactor'
import { RoomApi } from '/@/api/admin/Room'
import { UserApi } from '/@/api/admin/User'
import type { ReactorDto } from '/@/api/types/ReactorType'
import { DeviceStatusEnum, MaintenanceFlagEnum } from '/@/api/types/ReactorType'
import type { ReactorTypeEnumItem } from '/@/api/types/ReactorType'
const props = defineProps<{
title: string
}>()
const emit = defineEmits(['ok'])
const dialogVisible = ref(false)
const formRef = ref<FormInstance>()
const activeTab = ref('basic')
const activePumpTab = ref('pump1')
const deviceStatusOptions = ref<ReactorTypeEnumItem[]>([])
// API
const roomOptions = ref([
{ id: 1, name: '实验室1' },
{ id: 2, name: '实验室2' }
])
const principalOptions = ref([
{ id: 1, name: '张三' },
{ id: 2, name: '李四' }
])
const defaultPumpConfig = {
speed: 0,
timeout: 0,
exception: '',
errorCode: '',
isFeeding: true,
speedOffset: 0,
configId: 0
}
const form = reactive<ReactorDto>({
sort: 0,
deviceNo: '',
assetNo: '',
productID: '',
model: '',
specification: '',
capacity: 0,
roomID: undefined,
principalId: undefined,
deviceStatus: undefined ,
maintenanceFlag: undefined,
equScaleId: 0,
feedingScaleId: 0,
relayID: 0,
warningLowerLimit: 0,
warningUpperLimit: 0,
errorCode: '',
pump1: { ...defaultPumpConfig },
pump2: { ...defaultPumpConfig },
pump3: { ...defaultPumpConfig },
pump4: { ...defaultPumpConfig },
pump5: { ...defaultPumpConfig },
pump6: { ...defaultPumpConfig },
pump7: { ...defaultPumpConfig },
pump8: { ...defaultPumpConfig },
isExternalPump: true,
opcip: '',
opcPort: '',
equAlarmId: 0,
status: true,
isMaintenance: false,
maintenanceTime: '',
id: 0
})
const rules = reactive<FormRules>({
deviceNo: [{ required: true, message: '请输入设备编号', trigger: 'blur' }],
assetNo: [{ required: true, message: '请输入资产编号', trigger: 'blur' }],
productID: [{ required: true, message: '请输入产品编号', trigger: 'blur' }],
model: [{ required: true, message: '请输入设备型号', trigger: 'blur' }],
specification: [{ required: true, message: '请输入规格', trigger: 'blur' }],
capacity: [{ required: true, message: '请输入容量', trigger: 'blur' }],
roomID: [{ required: true, message: '请选择房间', trigger: 'change' }],
principalId: [{ required: true, message: '请选择负责人', trigger: 'change' }],
deviceStatus: [{ required: true, message: '请选择设备状态', trigger: 'change' }],
maintenanceFlag: [{ required: true, message: '请选择维护标志', trigger: 'change' }],
status: [{ required: true, message: '请选择状态', trigger: 'change' }],
isMaintenance: [{ required: true, message: '请选择是否维护中', trigger: 'change' }],
maintenanceTime: [{ required: true, message: '请选择维护时间', trigger: 'change' }],
sort: [{ required: true, message: '请输入排序', trigger: 'blur' }],
equScaleId: [{ required: true, message: '请输入地秤ID', trigger: 'blur' }],
feedingScaleId: [{ required: true, message: '请输入补料秤ID', trigger: 'blur' }],
relayID: [{ required: true, message: '请输入继电器ID', trigger: 'blur' }],
warningLowerLimit: [{ required: true, message: '请输入警告下限', trigger: 'blur' }],
warningUpperLimit: [{ required: true, message: '请输入警告上限', trigger: 'blur' }],
opcip: [{ required: true, message: '请输入OPC IP', trigger: 'blur' }],
opcPort: [{ required: true, message: '请输入OPC 端口', trigger: 'blur' }],
equAlarmId: [{ required: true, message: '请输入报警器ID', trigger: 'blur' }]
})
/** 获取设备状态枚举列表 */
const getDeviceStatusOptions = async () => {
try {
const res = await new ReactorApi().getDeviceStatusEnumList()
deviceStatusOptions.value = res.data ?? []
} catch (error) {
console.error('获取设备状态枚举列表失败:', error)
}
}
/** 获取设备状态描述 */
const getDeviceStatusDesc = (status: DeviceStatusEnum) => {
const statusMap = {
[DeviceStatusEnum.Normal]: '正常',
[DeviceStatusEnum.Fault]: '故障',
[DeviceStatusEnum.Offline]: '离线',
[DeviceStatusEnum.Maintenance]: '维护中'
}
return statusMap[status] || '未知'
}
/** 获取维护标志描述 */
const getMaintenanceFlagDesc = (flag: MaintenanceFlagEnum) => {
const flagMap = {
[MaintenanceFlagEnum.None]: '无',
[MaintenanceFlagEnum.Regular]: '定期维护',
[MaintenanceFlagEnum.Emergency]: '紧急维护'
}
return flagMap[flag] || '未知'
}
/** 获取房间列表 */
const getRoomOptions = async () => {
try {
const res = await new RoomApi().getPage({
currentPage: 1,
pageSize: 1000,
filter: {}
})
roomOptions.value = res.data?.list?.map((item: any) => ({
id: item.id,
name: item.roomName
})) || []
} catch (error) {
console.error('获取房间列表失败:', error)
}
}
/** 获取负责人列表 */
const getPrincipalOptions = async () => {
try {
const res = await new UserApi().getPage({
currentPage: 1,
pageSize: 1000,
filter: {}
})
principalOptions.value = res.data?.list?.map((item: any) => ({
id: item.id,
name: item.name
})) || []
} catch (error) {
console.error('获取负责人列表失败:', error)
}
}
/** 打开弹窗 */
const open = (row?: ReactorDto) => {
dialogVisible.value = true
if (row) {
Object.assign(form, row)
}
getDeviceStatusOptions()
getRoomOptions()
getPrincipalOptions()
}
/** 提交表单 */
const submitForm = async () => {
if (!formRef.value) return
await formRef.value.validate(async (valid) => {
if (valid) {
try {
const api = new ReactorApi()
const res = form.id ? await api.update(form) : await api.add(form)
if (res.success) {
ElMessage.success(form.id ? '修改成功' : '新增成功')
dialogVisible.value = false
emit('ok')
}
} catch (error) {
console.error('提交失败:', error)
}
}
})
}
/** 取消 */
const cancel = () => {
dialogVisible.value = false
reset()
}
/** 重置表单 */
const reset = () => {
if (!formRef.value) return
formRef.value.resetFields()
Object.assign(form, {
sort: 0,
deviceNo: '',
assetNo: '',
productID: '',
model: '',
specification: '',
capacity: 0,
roomID: 0,
principalId: 0,
deviceStatus: DeviceStatusEnum.Normal,
maintenanceFlag: MaintenanceFlagEnum.None,
equScaleId: 0,
feedingScaleId: 0,
relayID: 0,
warningLowerLimit: 0,
warningUpperLimit: 0,
errorCode: '',
pump1: { ...defaultPumpConfig },
pump2: { ...defaultPumpConfig },
pump3: { ...defaultPumpConfig },
pump4: { ...defaultPumpConfig },
pump5: { ...defaultPumpConfig },
pump6: { ...defaultPumpConfig },
pump7: { ...defaultPumpConfig },
pump8: { ...defaultPumpConfig },
isExternalPump: true,
opcip: '',
opcPort: '',
equAlarmId: 0,
status: true,
isMaintenance: false,
maintenanceTime: '',
id: 0
})
}
onMounted(() => {
getDeviceStatusOptions()
getRoomOptions()
getPrincipalOptions()
})
defineExpose({
open
})
</script>
<style lang="scss" scoped>
.reactor-form-dialog {
:deep(.el-dialog__body) {
padding: 20px 30px;
}
}
.reactor-form {
.el-form-item {
margin-bottom: 22px;
}
}
.reactor-tabs {
:deep(.el-tabs__content) {
padding: 20px 0;
}
:deep(.el-tabs__nav) {
margin-bottom: 20px;
}
}
:deep(.el-input-number) {
width: 100%;
.el-input__wrapper {
padding-left: 11px;
padding-right: 11px;
}
}
:deep(.el-select) {
width: 100%;
}
:deep(.el-switch) {
margin-top: 8px;
}
</style>

View File

@ -0,0 +1,311 @@
<template>
<my-layout>
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" label-width="auto" :label-position="'left'" @submit.stop.prevent>
<el-form-item label="关键字" prop="keyWord">
<el-input v-model="state.filter.keyWord" placeholder="请输入设备编号/资产编号/产品ID/型号" clearable style="width: 300px" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="房间" prop="roomID">
<el-select v-model="state.filter.roomID" placeholder="请选择房间" clearable style="width: 200px">
<el-option v-for="item in state.roomOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="开始时间">
<el-date-picker v-model="state.filter.stDate" type="datetime" placeholder="选择开始时间"
format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" style="width: 180px" />
</el-form-item>
<el-form-item label="结束时间">
<el-date-picker v-model="state.filter.edDate" type="datetime" placeholder="选择结束时间"
format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" style="width: 180px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="handleQuery">查询</el-button>
<el-button v-auth="'api:admin:equ-reactor:add'" type="primary" icon="ele-Plus" @click="handleAdd">新增</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table v-loading="loading" :data="state.reactorList" row-key="id" style="width: 100%" border>
<el-table-column prop="deviceNo" label="设备编号" min-width="120" show-overflow-tooltip />
<el-table-column prop="assetNo" label="资产编号" min-width="120" show-overflow-tooltip />
<el-table-column prop="productID" label="产品ID" min-width="120" show-overflow-tooltip />
<el-table-column prop="model" label="型号" min-width="120" show-overflow-tooltip />
<el-table-column prop="specification" label="规格" min-width="120" show-overflow-tooltip />
<el-table-column prop="capacity" label="容量(g)" min-width="100" show-overflow-tooltip />
<el-table-column prop="roomName" label="房间名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="principalName" label="负责人" min-width="100" show-overflow-tooltip />
<el-table-column prop="deviceStatus" label="设备状态" width="100">
<template #default="{ row }">
<el-tag :type="getDeviceStatusType(row.deviceStatus)">
{{ getDeviceStatusDesc(row.deviceStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="维护标志" width="100" align="center">
<template #default="{ row }">
<el-tag :type="getMaintenanceFlagType(row.maintenanceFlag)">
{{ row.maintenanceFlagDesc }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="是否维护中" width="100" align="center">
<template #default="{ row }">
<el-tag :type="row.isMaintenance ? 'warning' : 'info'">
{{ row.isMaintenance ? '是' : '否' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createdTime" label="创建时间" min-width="160" show-overflow-tooltip />
<el-table-column label="操作" width="180" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<el-button v-auth="'api:admin:equ-reactor:update'" icon="ele-EditPen" size="small" text type="primary"
@click="handleEdit(row)">编辑</el-button>
<el-button v-auth="'api:admin:equ-reactor:soft-delete'" icon="ele-Delete" size="small" text type="danger"
@click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination v-model:current-page="state.pageInput.currentPage" v-model:page-size="state.pageInput.pageSize"
:total="state.total" :page-sizes="[10, 20, 50, 100]" size="small" background @size-change="handleSizeChange"
@current-change="handleCurrentChange" layout="total, sizes, prev, pager, next, jumper" />
</div>
</el-card>
<reactor-form ref="formRef" :title="state.formTitle" @ok="getList" />
</my-layout>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, onBeforeMount } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { ReactorApi } from '/@/api/admin/reactor'
import { RoomApi } from '/@/api/admin/Room'
import eventBus from '/@/utils/mitt'
import type {
ReactorPageInput,
ReactorDto,
ReactorTypeEnumItem
} from '/@/api/types/ReactorType'
import { DeviceStatusEnum, MaintenanceFlagEnum } from '/@/api/types/ReactorType'
import ReactorForm from './components/reactor-form.vue'
const loading = ref(false)
const formRef = ref()
const state = reactive({
loading: false,
formTitle: '',
total: 0,
filter: {
keyWord: '',
stDate: '',
edDate: '',
deviceStatus: undefined as number | undefined,
maintenanceFlag: undefined as number | undefined,
principalId: undefined as number | undefined,
roomID: undefined as number | undefined,
status: undefined as number | undefined,
isMaintenance: undefined as boolean | undefined,
pageIndex: 1,
pageSize: 10
},
pageInput: {
currentPage: 1,
pageSize: 20,
} as ReactorPageInput,
reactorList: [] as Array<ReactorDto>,
roomOptions: [] as Array<{ id: number; name: string }>,
principalOptions: [] as Array<{ id: number; name: string }>,
deviceStatusOptions: [] as ReactorTypeEnumItem[]
})
/** 获取设备状态枚举列表 */
const getDeviceStatusOptions = async () => {
try {
const res = await new ReactorApi().getDeviceStatusEnumList()
state.deviceStatusOptions = res.data ?? []
} catch (error) {
console.error('获取设备状态枚举列表失败:', error)
}
}
/** 获取设备状态描述 */
const getDeviceStatusDesc = (value: number) => {
const item = state.deviceStatusOptions.find(item => item.value === value)
return item?.label || '未知'
}
/** 获取设备状态类型 */
const getDeviceStatusType = (value: number) => {
switch (value) {
case DeviceStatusEnum.Normal:
return 'success'
case DeviceStatusEnum.Fault:
return 'danger'
case DeviceStatusEnum.Maintenance:
return 'warning'
default:
return 'info'
}
}
/** 获取维护标志描述 */
const getMaintenanceFlagDesc = (flag: MaintenanceFlagEnum) => {
const flagMap = {
[MaintenanceFlagEnum.None]: '无',
[MaintenanceFlagEnum.Regular]: '定期维护',
[MaintenanceFlagEnum.Emergency]: '紧急维护'
}
return flagMap[flag] || '未知'
}
/** 获取维护标志标签类型 */
const getMaintenanceFlagType = (flag: MaintenanceFlagEnum) => {
const typeMap = {
[MaintenanceFlagEnum.None]: 'info',
[MaintenanceFlagEnum.Regular]: 'warning',
[MaintenanceFlagEnum.Emergency]: 'danger'
}
return typeMap[flag] || 'info'
}
/** 查询列表 */
const getList = async () => {
loading.value = true
state.filter.pageIndex = state.pageInput.currentPage ?? 1
state.filter.pageSize = state.pageInput.pageSize ?? 20
try {
const res = await new ReactorApi().getPage(state.filter)
state.reactorList = res.data?.list || []
state.total = res.data?.total || 0
} catch (error) {
console.error('获取列表失败:', error)
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
state.pageInput.currentPage = 1
getList()
}
/** 新增按钮操作 */
const handleAdd = () => {
state.formTitle = '新增反应器'
formRef.value?.open()
}
/** 修改按钮操作 */
const handleEdit = (row: ReactorDto) => {
state.formTitle = '编辑反应器'
formRef.value?.open(row)
}
/** 删除按钮操作 */
const handleDelete = (row: ReactorDto) => {
ElMessageBox.confirm('确认要删除该记录吗?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {
const res = await new ReactorApi().softDelete({ id: row.id! })
if (res.success) {
ElMessage.success('删除成功')
getList()
}
} catch (error) {
console.error('删除失败:', error)
}
})
}
/** 每页条数改变 */
const handleSizeChange = (val: number) => {
state.pageInput.pageSize = val
getList()
}
/** 当前页改变 */
const handleCurrentChange = (val: number) => {
state.pageInput.currentPage = val
getList()
}
/** 获取房间列表 */
const getRoomOptions = async () => {
try {
const res = await new RoomApi().getPage({
currentPage: 1,
pageSize: 1000,
filter: {}
})
state.roomOptions = res.data?.list?.map((item: any) => ({
id: item.id,
name: item.roomName
})) || []
} catch (error) {
console.error('获取房间列表失败:', error)
}
}
/** 获取负责人列表 */
const getPrincipalOptions = async () => {
try {
// TODO: API
state.principalOptions = [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' }
]
} catch (error) {
console.error('获取负责人列表失败:', error)
}
}
onMounted(() => {
getList()
getRoomOptions()
getPrincipalOptions()
getDeviceStatusOptions()
eventBus.on('refreshReactor', getList)
})
onBeforeMount(() => {
eventBus.off('refreshReactor')
})
</script>
<style lang="scss" scoped>
.my-query-box {
:deep(.el-form-item) {
margin-bottom: 16px;
}
}
.my-fill {
flex: 1;
display: flex;
flex-direction: column;
}
.my-flex {
display: flex;
}
.my-flex-end {
justify-content: flex-end;
}
.mt8 {
margin-top: 8px;
}
.mb8 {
margin-bottom: 8px;
}
</style>

View File

@ -0,0 +1,137 @@
<template>
<el-dialog v-model="visible" :title="title" width="500px" append-to-body destroy-on-close>
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="反应器名称" prop="reactorName">
<el-input v-model="form.reactorName" placeholder="请输入反应器名称" />
</el-form-item>
<el-form-item label="反应器类型" prop="reactorType">
<el-select v-model="form.reactorType" placeholder="请选择反应器类型" style="width: 100%">
<el-option v-for="item in reactorTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio :label="true">启用</el-radio>
<el-radio :label="false">禁用</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { ReactorApi } from '/@/api/admin/reactor'
import type { ReactorDto, ReactorTypeEnumItem } from '/@/api/types/ReactorType'
const props = defineProps<{
title: string
}>()
const emit = defineEmits<{
(e: 'ok'): void
}>()
const visible = ref(false)
const formRef = ref()
const reactorTypeOptions = ref<ReactorTypeEnumItem[]>([])
const form = reactive<ReactorDto>({
reactorName: '',
reactorType: '',
status: true
})
const rules = {
reactorName: [
{ required: true, message: '请输入反应器名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
reactorType: [
{ required: true, message: '请选择反应器类型', trigger: 'change' }
],
status: [
{ required: true, message: '请选择状态', trigger: 'change' }
]
}
/** 获取反应器类型枚举列表 */
const getReactorTypeOptions = async () => {
try {
const res = await new ReactorApi().getReactorTypeEnumList()
reactorTypeOptions.value = res.data ?? []
} catch (error) {
console.error('获取反应器类型枚举列表失败:', error)
}
}
/** 打开弹窗 */
const open = (row?: ReactorDto) => {
visible.value = true
if (row) {
Object.assign(form, row)
}
}
/** 提交表单 */
const submitForm = async () => {
if (!formRef.value) return
await formRef.value.validate(async (valid: boolean) => {
if (valid) {
try {
const api = new ReactorApi()
const res = form.id
? await api.update(form)
: await api.add(form)
if (res.success) {
ElMessage.success(form.id ? '修改成功' : '新增成功')
visible.value = false
emit('ok')
}
} catch (error) {
console.error(form.id ? '修改失败:' : '新增失败:', error)
}
}
})
}
/** 取消按钮 */
const cancel = () => {
visible.value = false
reset()
}
/** 重置表单 */
const reset = () => {
if (formRef.value) {
formRef.value.resetFields()
}
Object.assign(form, {
id: undefined,
reactorName: '',
reactorType: '',
status: true
})
}
onMounted(() => {
getReactorTypeOptions()
})
defineExpose({
open
})
</script>
<style lang="scss" scoped>
.dialog-footer {
text-align: right;
}
</style>

View File

@ -0,0 +1,224 @@
<template>
<my-layout>
<el-card class="my-query-box mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" label-width="auto" :label-position="'left'" @submit.stop.prevent>
<el-form-item label="反应器名称" prop="reactorName">
<el-input v-model="state.filter.keyWord" placeholder="请输入反应器名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="反应器类型" prop="reactorType">
<el-select v-model="state.filter.reactorType" placeholder="请选择反应器类型" clearable style="width: 200px">
<el-option v-for="item in state.reactorTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="state.filter.status" placeholder="请选择状态" clearable style="width: 200px">
<el-option label="启用" :value="true" />
<el-option label="禁用" :value="false" />
</el-select>
</el-form-item>
<el-form-item label="开始时间">
<el-date-picker v-model="state.filter.startTime" type="datetime" placeholder="选择开始时间"
format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" style="width: 180px" />
</el-form-item>
<el-form-item label="结束时间">
<el-date-picker v-model="state.filter.endTime" type="datetime" placeholder="选择结束时间"
format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" style="width: 180px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="handleQuery">查询</el-button>
<el-button v-auth="'api:admin:reactor:add'" type="primary" icon="ele-Plus" @click="handleAdd">新增</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table v-loading="loading" :data="state.reactorList" row-key="id" style="width: 100%" border>
<el-table-column prop="reactorName" label="反应器名称" min-width="120" show-overflow-tooltip />
<el-table-column label="反应器类型" min-width="120" show-overflow-tooltip>
<template #default="{ row }">
{{ getReactorTypeName(row.reactorType) }}
</template>
</el-table-column>
<el-table-column label="状态" width="100" align="center">
<template #default="{ row }">
<el-tag :type="row.status ? 'success' : 'danger'">
{{ row.status ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createdUserName" label="创建者" min-width="100" show-overflow-tooltip />
<el-table-column prop="createdTime" label="创建时间" min-width="160" show-overflow-tooltip />
<el-table-column label="操作" width="180" fixed="right" header-align="center" align="center">
<template #default="{ row }">
<el-button v-auth="'api:admin:reactor:update'" icon="ele-EditPen" size="small" text type="primary"
@click="handleEdit(row)">编辑</el-button>
<el-button v-auth="'api:admin:reactor:soft-delete'" icon="ele-Delete" size="small" text type="danger"
@click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 10px">
<el-pagination v-model:current-page="state.pageInput.currentPage" v-model:page-size="state.pageInput.pageSize"
:total="state.total" :page-sizes="[10, 20, 50, 100]" size="small" background @size-change="handleSizeChange"
@current-change="handleCurrentChange" layout="total, sizes, prev, pager, next, jumper" />
</div>
</el-card>
<reactor-form ref="formRef" :title="state.formTitle" @ok="getList" />
</my-layout>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, onBeforeMount } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { ReactorApi } from '/@/api/admin/reactor'
import eventBus from '/@/utils/mitt'
import type {
ReactorPageInput,
ReactorDto,
ReactorTypeEnumItem
} from '/@/api/types/ReactorType'
import ReactorForm from './components/reactor-form.vue'
const loading = ref(false)
const formRef = ref()
const state = reactive({
loading: false,
formTitle: '',
total: 0,
filter: {
keyWord: "",
reactorType: "",
status: null,
startTime: "",
endTime: ""
},
pageInput: {
currentPage: 1,
pageSize: 20,
} as ReactorPageInput,
reactorList: [] as Array<ReactorDto>,
reactorTypeOptions: [] as ReactorTypeEnumItem[]
})
/** 查询列表 */
const getList = async () => {
loading.value = true
state.pageInput.filter = state.filter
try {
const res = await new ReactorApi().getPage(state.pageInput)
state.reactorList = res.data?.list || []
state.total = res.data?.total || 0
} catch (error) {
console.error('获取列表失败:', error)
} finally {
loading.value = false
}
}
/** 获取反应器类型枚举列表 */
const getReactorTypeOptions = async () => {
try {
const res = await new ReactorApi().getReactorTypeEnumList()
state.reactorTypeOptions = res.data ?? []
} catch (error) {
console.error('获取反应器类型枚举列表失败:', error)
}
}
const getReactorTypeName = (reactorType: string) => {
const item = state.reactorTypeOptions.find(item => item.value === Number(reactorType))
return item?.label || ''
}
/** 搜索按钮操作 */
const handleQuery = () => {
state.pageInput.currentPage = 1
getList()
}
/** 新增按钮操作 */
const handleAdd = () => {
state.formTitle = '新增反应器'
formRef.value?.open()
}
/** 修改按钮操作 */
const handleEdit = (row: ReactorDto) => {
state.formTitle = '编辑反应器'
formRef.value?.open(row)
}
/** 删除按钮操作 */
const handleDelete = (row: ReactorDto) => {
ElMessageBox.confirm('确认要删除该记录吗?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {
const res = await new ReactorApi().softDelete({ id: row.id! })
if (res.success) {
ElMessage.success('删除成功')
getList()
}
} catch (error) {
console.error('删除失败:', error)
}
})
}
/** 每页条数改变 */
const handleSizeChange = (val: number) => {
state.pageInput.pageSize = val
getList()
}
/** 当前页改变 */
const handleCurrentChange = (val: number) => {
state.pageInput.currentPage = val
getList()
}
onMounted(() => {
getReactorTypeOptions()
getList()
eventBus.on('refreshReactor', getList)
})
onBeforeMount(() => {
eventBus.off('refreshReactor')
})
</script>
<style lang="scss" scoped>
.my-query-box {
:deep(.el-form-item) {
margin-bottom: 16px;
}
}
.my-fill {
flex: 1;
display: flex;
flex-direction: column;
}
.my-flex {
display: flex;
}
.my-flex-end {
justify-content: flex-end;
}
.mt8 {
margin-top: 8px;
}
.mb8 {
margin-bottom: 8px;
}
</style>