feat(mes): 新增设备台账点检、保养和维修记录列表组件

pull/871/MERGE
YunaiV 2026-04-02 23:50:24 +08:00
parent 2ddfb9b722
commit acf673d2d2
9 changed files with 364 additions and 55 deletions

View File

@ -39,6 +39,7 @@ import { formatDate } from '@/utils/formatTime'
import { HolidayType } from '@/views/mes/utils/constants'
defineOptions({ name: 'HolidayForm' })
const emit = defineEmits(['success'])
const message = useMessage()
@ -73,10 +74,7 @@ const open = async (day: string) => {
formLoading.value = false
}
}
defineExpose({ open })
/** 提交表单 */
const emit = defineEmits(['success'])
const submitForm = async () => {
//
if (!formRef) return
@ -105,4 +103,6 @@ const resetForm = () => {
dayDisplay.value = ''
formRef.value?.resetFields()
}
defineExpose({ open })
</script>

View File

@ -151,6 +151,7 @@ import CalShiftList from './CalShiftList.vue'
import CalPlanTeamList from './CalPlanTeamList.vue'
defineOptions({ name: 'CalPlanForm' })
const emit = defineEmits(['success'])
const { t } = useI18n() //
const message = useMessage() //
@ -211,10 +212,7 @@ const open = async (type: string, id?: number) => {
}
}
}
defineExpose({ open })
/** 提交表单 */
const emit = defineEmits(['success'])
const submitForm = async () => {
await formRef.value.validate()
formLoading.value = true
@ -271,4 +269,6 @@ const resetForm = () => {
}
formRef.value?.resetFields()
}
defineExpose({ open })
</script>

View File

@ -201,6 +201,7 @@ const { t } = useI18n() // 国际化
const loading = ref(true) //
const list = ref<CalPlanVO[]>([]) //
const total = ref(0) //
const exportLoading = ref(false) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
@ -212,7 +213,7 @@ const queryParams = reactive({
status: undefined
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
const formRef = ref() //
/** 查询列表 */
const getList = async () => {
@ -239,7 +240,6 @@ const resetQuery = () => {
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}

View File

@ -0,0 +1,77 @@
<!-- 设备台账 - 点检记录列表嵌入设备详情 Tab -->
<template>
<div class="overflow-hidden">
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<!-- TODO @AI计划编码计划名称开始时间结束日期频率状态 -->
<el-table-column label="计划名称" align="center" prop="planName" min-width="120" />
<el-table-column
label="点检时间"
align="center"
prop="checkTime"
:formatter="dateFormatter"
width="180"
/>
<el-table-column label="点检人" align="center" prop="nickname" width="100" />
<el-table-column label="设备编码" align="center" prop="machineryCode" width="120" />
<el-table-column label="设备名称" align="center" prop="machineryName" width="120" />
<el-table-column label="品牌" align="center" prop="machineryBrand" width="100" />
<el-table-column label="规格型号" align="center" prop="machinerySpec" width="120" />
<el-table-column label="状态" align="center" prop="status" width="100">
<template #default="scope">
<dict-tag :type="DICT_TYPE.MES_DV_CHECK_RECORD_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
</el-table>
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script setup lang="ts">
import { DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import { DvCheckRecordApi } from '@/api/mes/dv/checkrecord'
defineOptions({ name: 'MachineryCheckRecordList' })
const props = defineProps<{
machineryId: number
}>()
const loading = ref(false)
const list = ref<any[]>([])
const total = ref(0)
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
machineryId: undefined as number | undefined
})
/** 查询列表 */
const getList = async () => {
if (!queryParams.machineryId) return
loading.value = true
try {
const data = await DvCheckRecordApi.getCheckRecordPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 监听 machineryId 变化,自动加载 */
watch(
() => props.machineryId,
(val) => {
queryParams.machineryId = val
queryParams.pageNo = 1
getList()
},
{ immediate: true }
)
</script>

View File

@ -7,17 +7,14 @@
:rules="formRules"
label-width="120px"
v-loading="formLoading"
:disabled="isDetail"
>
<el-row>
<el-col :span="8">
<el-form-item label="设备编码" prop="code">
<el-input
v-model="formData.code"
placeholder="请输入设备编码"
:disabled="formType === 'update'"
>
<el-input v-model="formData.code" placeholder="请输入设备编码">
<template #append>
<el-button @click="generateCode" :disabled="formType === 'update'">
<el-button @click="generateCode" :disabled="formType !== 'create'">
生成
</el-button>
</template>
@ -37,17 +34,8 @@
</el-row>
<el-row>
<el-col :span="8">
<!-- TODO @AI抽成一个独立的 components type 模块里然后只允许选择子节点你看看有分类实现了这个功能的 -->
<el-form-item label="设备类型" prop="machineryTypeId">
<el-tree-select
v-model="formData.machineryTypeId"
:data="machineryTypeTree"
:props="defaultProps"
check-strictly
default-expand-all
placeholder="请选择设备类型"
class="!w-1/1"
/>
<DvMachineryTypeSelect v-model="formData.machineryTypeId" />
</el-form-item>
</el-col>
<el-col :span="8">
@ -74,25 +62,22 @@
<el-input v-model="formData.spec" placeholder="请输入规格型号" />
</el-form-item>
</el-col>
<!-- TODO @AI最近 xx 两个字段仅详情时展示然后不需要 placeholder -->
<el-col :span="8">
<el-col v-if="isDetail" :span="8">
<el-form-item label="最近点检时间" prop="lastCheckTime">
<el-date-picker
v-model="formData.lastCheckTime"
type="datetime"
placeholder="请选择最近点检时间"
value-format="x"
class="!w-1/1"
disabled
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col v-if="isDetail" :span="8">
<el-form-item label="最近保养时间" prop="lastMaintenTime">
<el-date-picker
v-model="formData.lastMaintenTime"
type="datetime"
placeholder="请选择最近保养时间"
value-format="x"
class="!w-1/1"
disabled
@ -109,23 +94,22 @@
</el-row>
</el-form>
<!-- 编辑时显示子资源 Tab -->
<el-tabs v-if="formType === 'update'" v-model="activeTab" class="mt-10px">
<el-tab-pane label="点检记录" name="check">
<!-- TODO TODO @AI等点检模块实现后对接 -->
<el-empty description="暂无点检记录" />
<!-- 编辑/详情时显示子资源 Tab -->
<el-tabs v-if="formType !== 'create' && formData.id" v-model="activeTab" class="mt-10px">
<el-tab-pane label="点检记录" name="check" lazy>
<MachineryCheckRecordList :machinery-id="formData.id" />
</el-tab-pane>
<el-tab-pane label="保养记录" name="mainten">
<!-- TODO TODO @AI 等保养模块实现后对接 -->
<el-empty description="暂无保养记录" />
<el-tab-pane label="保养记录" name="mainten" lazy>
<MachineryMaintenRecordList :machinery-id="formData.id" />
</el-tab-pane>
<el-tab-pane label="维修工单" name="repair">
<!-- TODO TODO @AI 等维修模块实现后对接 -->
<el-empty description="暂无维修工单" />
<el-tab-pane label="维修记录" name="repair" lazy>
<MachineryRepairList :machinery-id="formData.id" />
</el-tab-pane>
</el-tabs>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button v-if="!isDetail" @click="submitForm" type="primary" :disabled="formLoading"
> </el-button
>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
@ -133,10 +117,11 @@
<script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import { DvMachineryApi, DvMachineryVO } from '@/api/mes/dv/machinery'
import { DvMachineryTypeApi } from '@/api/mes/dv/machinery/type'
import MdWorkshopSelect from '@/views/mes/md/workstation/components/MdWorkshopSelect.vue'
import { defaultProps, handleTree } from '@/utils/tree'
import DvMachineryTypeSelect from '@/views/mes/dv/machinery/type/components/DvMachineryTypeSelect.vue'
import MachineryCheckRecordList from './MachineryCheckRecordList.vue'
import MachineryMaintenRecordList from './MachineryMaintenRecordList.vue'
import MachineryRepairList from './MachineryRepairList.vue'
import { MesDvMachineryStatusEnum, MesAutoCodeRuleCode } from '@/views/mes/utils/constants'
import { AutoCodeRecordApi } from '@/api/mes/md/autocode/record'
@ -146,9 +131,17 @@ const { t } = useI18n() // 国际化
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formType = ref('') // create - update - detail -
const isDetail = computed(() => formType.value === 'detail')
const dialogTitle = computed(() => {
const titles: Record<string, string> = {
create: '新增设备',
update: '修改设备',
detail: '查看设备'
}
return titles[formType.value] || formType.value
})
const activeTab = ref('check') // Tab
const formData = ref({
id: undefined,
@ -171,7 +164,6 @@ const formRules = reactive({
status: [{ required: true, message: '设备状态不能为空', trigger: 'change' }]
})
const formRef = ref() // Ref
const machineryTypeTree = ref<any[]>([]) //
/** 生成设备编码 */
const generateCode = async () => {
@ -183,13 +175,9 @@ const generateCode = async () => {
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
const typeData = await DvMachineryTypeApi.getMachineryTypeSimpleList()
machineryTypeTree.value = handleTree(typeData)
//
// /
if (id) {
formLoading.value = true
try {
@ -197,6 +185,7 @@ const open = async (type: string, id?: number) => {
} finally {
formLoading.value = false
}
activeTab.value = 'check'
}
}
defineExpose({ open }) // open
@ -242,5 +231,6 @@ const resetForm = () => {
}
formRef.value?.resetFields()
activeTab.value = 'check'
}
</script>

View File

@ -0,0 +1,77 @@
<!-- 设备台账 - 保养记录列表嵌入设备详情 Tab -->
<template>
<div class="overflow-hidden">
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<!-- TODO @AI计划编码计划名称开始时间结束日期频率状态 -->
<el-table-column label="计划名称" align="center" prop="planName" min-width="120" />
<el-table-column
label="保养时间"
align="center"
prop="maintenTime"
:formatter="dateFormatter"
width="180"
/>
<el-table-column label="保养人" align="center" prop="nickname" width="100" />
<el-table-column label="设备编码" align="center" prop="machineryCode" width="120" />
<el-table-column label="设备名称" align="center" prop="machineryName" width="120" />
<el-table-column label="品牌" align="center" prop="machineryBrand" width="100" />
<el-table-column label="规格型号" align="center" prop="machinerySpec" width="120" />
<el-table-column label="状态" align="center" prop="status" width="100">
<template #default="scope">
<dict-tag :type="DICT_TYPE.MES_MAINTEN_RECORD_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
</el-table>
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script setup lang="ts">
import { DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import { DvMaintenRecordApi } from '@/api/mes/dv/maintenrecord'
defineOptions({ name: 'MachineryMaintenRecordList' })
const props = defineProps<{
machineryId: number
}>()
const loading = ref(false)
const list = ref<any[]>([])
const total = ref(0)
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
machineryId: undefined as number | undefined
})
/** 查询列表 */
const getList = async () => {
if (!queryParams.machineryId) return
loading.value = true
try {
const data = await DvMaintenRecordApi.getMaintenRecordPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 监听 machineryId 变化,自动加载 */
watch(
() => props.machineryId,
(val) => {
queryParams.machineryId = val
queryParams.pageNo = 1
getList()
},
{ immediate: true }
)
</script>

View File

@ -0,0 +1,94 @@
<!-- 设备台账 - 维修工单列表嵌入设备详情 Tab -->
<template>
<div class="overflow-hidden">
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<!-- TODO @AI维护单编号维修单名称保修日期维修完成日期验收日期维修结果维修人员验收人员单据状态 -->
<el-table-column label="工单编码" align="center" prop="code" width="120" />
<el-table-column label="工单名称" align="center" prop="name" min-width="120" />
<el-table-column
label="报修日期"
align="center"
prop="requireDate"
:formatter="dateFormatter"
width="180"
/>
<el-table-column
label="维修完成日期"
align="center"
prop="finishDate"
:formatter="dateFormatter"
width="180"
/>
<el-table-column
label="验收日期"
align="center"
prop="confirmDate"
:formatter="dateFormatter"
width="180"
/>
<el-table-column label="维修人" align="center" prop="acceptedUserNickname" width="100" />
<el-table-column label="验收人" align="center" prop="confirmUserNickname" width="100" />
<el-table-column label="维修结果" align="center" prop="result" width="100">
<template #default="scope">
<dict-tag :type="DICT_TYPE.MES_DV_REPAIR_RESULT" :value="scope.row.result" />
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status" width="100">
<template #default="scope">
<dict-tag :type="DICT_TYPE.MES_DV_REPAIR_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
</el-table>
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script setup lang="ts">
import { DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import { DvRepairApi } from '@/api/mes/dv/repair'
defineOptions({ name: 'MachineryRepairList' })
const props = defineProps<{
machineryId: number
}>()
const loading = ref(false)
const list = ref<any[]>([])
const total = ref(0)
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
machineryId: undefined as number | undefined
})
/** 查询列表 */
const getList = async () => {
if (!queryParams.machineryId) return
loading.value = true
try {
const data = await DvRepairApi.getRepairPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 监听 machineryId 变化,自动加载 */
watch(
() => props.machineryId,
(val) => {
queryParams.machineryId = val
queryParams.pageNo = 1
getList()
},
{ immediate: true }
)
</script>

View File

@ -80,7 +80,6 @@
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<!-- TODO @AI导入的 mysql 权限标识没插入你处理下 -->
<el-button
type="warning"
plain
@ -105,8 +104,13 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<!-- TODO @AI点击设备编码查看详情对标其他界面的交互 -->
<el-table-column label="设备编码" align="center" prop="code" width="120" />
<el-table-column label="设备编码" align="center" prop="code" width="120">
<template #default="scope">
<el-link type="primary" @click="openForm('detail', scope.row.id)">
{{ scope.row.code }}
</el-link>
</template>
</el-table-column>
<el-table-column label="设备名称" align="center" prop="name" min-width="150" />
<el-table-column label="品牌" align="center" prop="brand" width="100" />
<el-table-column label="规格型号" align="center" prop="spec" width="120" />

View File

@ -0,0 +1,67 @@
<!-- MES 设备类型选择器树形下拉只允许选择叶节点 -->
<template>
<el-tree-select
v-model="selectValue"
:data="treeData"
:props="defaultProps"
:placeholder="placeholder"
:disabled="disabled"
check-strictly
default-expand-all
filterable
class="!w-1/1"
@change="handleChange"
/>
</template>
<script setup lang="ts">
import { DvMachineryTypeApi, DvMachineryTypeVO } from '@/api/mes/dv/machinery/type'
import { defaultProps, handleTree } from '@/utils/tree'
defineOptions({ name: 'DvMachineryTypeSelect' })
const props = withDefaults(
defineProps<{
modelValue?: number
disabled?: boolean
placeholder?: string
}>(),
{
disabled: false,
placeholder: '请选择设备类型'
}
)
const emit = defineEmits<{
'update:modelValue': [value: number | undefined]
change: [item: DvMachineryTypeVO | undefined]
}>()
const allList = ref<DvMachineryTypeVO[]>([])
const treeData = ref<any[]>([])
const selectValue = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
const handleChange = (val: number | undefined) => {
const item = allList.value.find((o) => o.id === val)
emit('change', item)
}
/** 递归将有子节点的分支节点标记为 disabled */
const markParentsDisabled = (nodes: any[]): any[] => {
return nodes.map((node) => {
if (node.children?.length) {
return { ...node, disabled: true, children: markParentsDisabled(node.children) }
}
return node
})
}
onMounted(async () => {
allList.value = await DvMachineryTypeApi.getMachineryTypeSimpleList()
treeData.value = markParentsDisabled(handleTree(allList.value))
})
</script>