first commit
This commit is contained in:
1071
src/views/iot/shared/QualityForm.vue
Normal file
1071
src/views/iot/shared/QualityForm.vue
Normal file
File diff suppressed because it is too large
Load Diff
490
src/views/iot/shared/QualityInspectionList.vue
Normal file
490
src/views/iot/shared/QualityInspectionList.vue
Normal file
@@ -0,0 +1,490 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="120px">
|
||||
<!-- <el-form-item label="罐标" prop="canLabel">
|
||||
<el-input v-model="queryParams.canLabel" placeholder="请输入罐标" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item> -->
|
||||
<el-form-item label="产品名称" prop="productName">
|
||||
<el-select v-model="queryParams.productName" placeholder="请选择产品名称" clearable class="!w-240px">
|
||||
<el-option label="请选择字典生成" value="" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品规格" prop="productSpec">
|
||||
<el-input v-model="queryParams.productSpec" placeholder="请输入产品规格" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="质检员" prop="inspector">
|
||||
<el-input v-model="queryParams.inspector" placeholder="请输入质检员" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="质检日期" prop="inspectionDate">
|
||||
<el-date-picker v-model="queryParams.inspectionDate" value-format="YYYY-MM-DD HH:mm:ss" type="daterange"
|
||||
start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker v-model="queryParams.createTime" value-format="YYYY-MM-DD HH:mm:ss" type="daterange"
|
||||
start-placeholder="开始日期" end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="合格状态" prop="qualifiedStatus">
|
||||
<el-select v-model="queryParams.qualifiedStatus" placeholder="请选择合格状态" clearable class="!w-240px">
|
||||
<el-option v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" :key="String(dict.value)"
|
||||
:label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="质检数据" prop="inspectionData">
|
||||
<el-input v-model="queryParams.inspectionData" placeholder="请输入质检数据" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="关联单据号" prop="relatedDocumentNo">
|
||||
<el-input v-model="queryParams.relatedDocumentNo" placeholder="请输入关联单据号" clearable @keyup.enter="handleQuery"
|
||||
class="!w-240px" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery">
|
||||
<Icon icon="ep:search" class="mr-5px" /> 搜索
|
||||
</el-button>
|
||||
<el-button @click="resetQuery">
|
||||
<Icon icon="ep:refresh" class="mr-5px" /> 重置
|
||||
</el-button>
|
||||
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['iot:quality:create']">
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button type="success" plain @click="handleExport" :loading="exportLoading"
|
||||
v-hasPermi="['iot:quality:export']">
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</el-button>
|
||||
<el-button type="danger" plain :disabled="isEmpty(checkedIds)" @click="handleDeleteBatch"
|
||||
v-hasPermi="['iot:quality:delete']">
|
||||
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table row-key="id" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"
|
||||
@selection-change="handleRowCheckboxChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<!-- <el-table-column label="罐标" align="center" prop="canLabel" /> -->
|
||||
<el-table-column label="产品名称" align="center" prop="productName" />
|
||||
<el-table-column label="产品规格" align="center" prop="productSpec" />
|
||||
<el-table-column label="质检员" align="center" prop="inspector" />
|
||||
<el-table-column label="质检日期" align="center" prop="inspectionDate" :formatter="dateFormatter2" width="180px" />
|
||||
<!-- <el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" /> -->
|
||||
<el-table-column label="合格状态" align="center" prop="qualifiedStatus">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.qualifiedStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="附件" align="center" min-width="120px">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.attachmentName">{{ scope.row.attachmentName }}</span>
|
||||
<span v-else class="text-gray-400">无</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="关联单据号" align="center" prop="relatedDocumentNo" /> -->
|
||||
<el-table-column label="操作" align="center" min-width="260px">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="openForm('update', scope.row.id)" v-hasPermi="['iot:quality:update']">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button link type="info" @click="openDetail(scope.row)">
|
||||
质检详情
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.attachmentUrl"
|
||||
link
|
||||
type="success"
|
||||
@click="handleDownloadAttachment(scope.row)"
|
||||
v-hasPermi="['iot:quality:query']"
|
||||
>
|
||||
下载附件
|
||||
</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['iot:quality:delete']">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<QualityForm ref="formRef" :inspection-type="inspectionType" @success="getList" />
|
||||
|
||||
<!-- 质检详情弹窗 -->
|
||||
<Dialog v-model="detailVisible" title="质检详情" width="80%">
|
||||
<el-table :data="detailData" border>
|
||||
<el-table-column label="项目名称" prop="itemName" align="center" />
|
||||
<el-table-column label="检测结果" prop="result" align="center" />
|
||||
<el-table-column label="检测标准" prop="standard" align="center" />
|
||||
<el-table-column label="是否合格" prop="qualified" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.qualified === '合格' || scope.row.qualified === 'true' ? 'success' : 'danger'">
|
||||
{{ scope.row.qualified === 'true' ? '合格' : scope.row.qualified === 'false' ? '不合格' : scope.row.qualified }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<template #footer>
|
||||
<el-button @click="detailVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
|
||||
import { isEmpty } from '@/utils/is'
|
||||
import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import { QualityApi, Quality } from '@/api/iot/product-inspection'
|
||||
import QualityForm from './QualityForm.vue'
|
||||
|
||||
/** 质检列表通用组件 */
|
||||
defineOptions({ name: 'QualityInspectionList' })
|
||||
|
||||
// 定义props
|
||||
interface Props {
|
||||
inspectionType: number // 质检类型:1-原料质检,2-过程质检,3-产品质检
|
||||
pageTitle?: string // 页面标题,用于导出文件名
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
pageTitle: '质检'
|
||||
})
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const list = ref<Quality[]>([]) // 列表的数据
|
||||
const total = ref(0) // 列表的总页数
|
||||
|
||||
// 质检详情相关
|
||||
const detailVisible = ref(false) // 详情弹窗显示状态
|
||||
const detailData = ref<any[]>([]) // 详情数据
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
canLabel: undefined,
|
||||
productName: undefined,
|
||||
productSpec: undefined,
|
||||
inspector: undefined,
|
||||
inspectionDate: [],
|
||||
createTime: [],
|
||||
qualifiedStatus: undefined,
|
||||
inspectionType: props.inspectionType,
|
||||
inspectionData: undefined,
|
||||
relatedDocumentNo: undefined
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await QualityApi.getQualityPage(queryParams)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 打开质检详情 */
|
||||
const openDetail = async (row: Quality) => {
|
||||
try {
|
||||
// 解析质检数据
|
||||
let inspectionData = {}
|
||||
if (row.inspectionData) {
|
||||
inspectionData = JSON.parse(row.inspectionData)
|
||||
}
|
||||
|
||||
// 构建详情数据
|
||||
const detailItems: any[] = []
|
||||
|
||||
// 检查是否是新的LabView兼容数据结构
|
||||
if (inspectionData.inspectionItems && Array.isArray(inspectionData.inspectionItems)) {
|
||||
// 新格式:LabView兼容的结构化数据
|
||||
inspectionData.inspectionItems.forEach((item: any) => {
|
||||
detailItems.push({
|
||||
itemName: item.itemName || getFieldDisplayName(item.itemKey),
|
||||
result: getItemResultLabel(item),
|
||||
standard: getFieldStandardFromItem(item),
|
||||
qualified: item.qualified === 'true' ? '合格' : item.qualified === 'false' ? '不合格' : item.qualified || '未设置'
|
||||
})
|
||||
})
|
||||
} else {
|
||||
// 旧格式:键值对结构(向后兼容)
|
||||
// 获取产品标准信息
|
||||
let productStandards: Record<string, any> = {}
|
||||
if (row.productName) {
|
||||
try {
|
||||
const { ProductApi } = await import('@/api/erp/product/product')
|
||||
// 通过产品名称查找产品
|
||||
const productList = await ProductApi.getProductSimpleList()
|
||||
const product = productList.find((item: any) => item.name === row.productName)
|
||||
if (product) {
|
||||
const productDetail = await ProductApi.getProduct(product.id)
|
||||
if (productDetail.jsonField) {
|
||||
const jsonConfig = JSON.parse(productDetail.jsonField)
|
||||
if (jsonConfig.items && Array.isArray(jsonConfig.items)) {
|
||||
// 新格式:从FormBuilder配置中获取标准信息
|
||||
jsonConfig.items.forEach((item: any) => {
|
||||
if (item.standard || item.standardValue || item.standardRange) {
|
||||
productStandards[item.key] = {
|
||||
standard: item.standard,
|
||||
standardType: item.standardType,
|
||||
standardValue: item.standardValue,
|
||||
standardRange: item.standardRange,
|
||||
standardOptions: item.standardOptions
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('获取产品标准信息失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历质检数据,分离普通字段和合格状态字段
|
||||
Object.keys(inspectionData).forEach(key => {
|
||||
if (!key.endsWith('_qualified') && inspectionData[key] !== null && inspectionData[key] !== undefined) {
|
||||
const qualifiedKey = `${key}_qualified`
|
||||
const qualifiedValue = inspectionData[qualifiedKey]
|
||||
|
||||
detailItems.push({
|
||||
itemName: getFieldDisplayName(key),
|
||||
result: inspectionData[key],
|
||||
standard: getFieldStandard(key, productStandards[key]),
|
||||
qualified: qualifiedValue === 'true' ? '合格' : qualifiedValue === 'false' ? '不合格' : qualifiedValue || '未设置'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
detailData.value = detailItems
|
||||
detailVisible.value = true
|
||||
} catch (error) {
|
||||
console.error('解析质检数据失败:', error)
|
||||
message.error('解析质检数据失败')
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取字段显示名称 */
|
||||
const getFieldDisplayName = (key: string): string => {
|
||||
const nameMap: Record<string, string> = {
|
||||
quality: '质量等级',
|
||||
defectCount: '缺陷数量',
|
||||
remark: '备注',
|
||||
weight: '重量',
|
||||
color: '颜色',
|
||||
purity: '纯度',
|
||||
moisture: '水分含量',
|
||||
temperature: '温度',
|
||||
pressure: '压力',
|
||||
flowRate: '流量',
|
||||
status: '状态'
|
||||
}
|
||||
return nameMap[key] || key
|
||||
}
|
||||
|
||||
/** 获取检测结果展示值(优先用选项label) */
|
||||
const getItemResultLabel = (item: any): string => {
|
||||
const rawResult = item?.result
|
||||
if (rawResult === null || rawResult === undefined || rawResult === '') {
|
||||
return ''
|
||||
}
|
||||
const options = Array.isArray(item?.options) ? item.options : []
|
||||
if (options.length > 0) {
|
||||
const match = options.find((opt: any) => opt?.value === rawResult)
|
||||
return match?.label ?? String(rawResult)
|
||||
}
|
||||
return String(rawResult)
|
||||
}
|
||||
|
||||
/** 获取字段标准 */
|
||||
const getFieldStandard = (_key: string, standardInfo?: any): string => {
|
||||
if (!standardInfo) {
|
||||
return '未设置标准'
|
||||
}
|
||||
|
||||
// 优先显示标准范围
|
||||
if (standardInfo.standardRange) {
|
||||
const { min, max } = standardInfo.standardRange
|
||||
|
||||
// 处理不同的范围情况
|
||||
if (min !== null && min !== undefined && max !== null && max !== undefined) {
|
||||
// 有最小值和最大值:10 - 20
|
||||
return `${min} - ${max}`
|
||||
} else if (min !== null && min !== undefined && (max === null || max === undefined)) {
|
||||
// 只有最小值:≥ 10
|
||||
return `≥ ${min}`
|
||||
} else if ((min === null || min === undefined) && max !== null && max !== undefined) {
|
||||
// 只有最大值:≤ 20
|
||||
return `≤ ${max}`
|
||||
} else {
|
||||
// 都没有或都是null
|
||||
return '未设置范围'
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有标准描述,返回标准描述
|
||||
if (standardInfo.standard) {
|
||||
return standardInfo.standard
|
||||
}
|
||||
|
||||
// 如果有标准值,显示标准值
|
||||
if (standardInfo.standardValue) {
|
||||
return `标准值: ${standardInfo.standardValue}`
|
||||
}
|
||||
|
||||
// 如果是选择类型,显示可选值
|
||||
if (standardInfo.standardType === 'select' && standardInfo.standardOptions && standardInfo.standardOptions.length > 0) {
|
||||
const options = standardInfo.standardOptions.map((opt: any) => opt.label).join('、')
|
||||
return `可选值: ${options}`
|
||||
}
|
||||
|
||||
return '未设置标准'
|
||||
}
|
||||
|
||||
/** 从LabView兼容的检测项目中获取字段标准 */
|
||||
const getFieldStandardFromItem = (item: any): string => {
|
||||
// 优先显示标准范围
|
||||
if (item.standardRange && (item.standardRange.min !== 0 || item.standardRange.max !== 100)) {
|
||||
const { min, max } = item.standardRange
|
||||
|
||||
// 处理不同的范围情况
|
||||
if (min !== null && min !== undefined && max !== null && max !== undefined) {
|
||||
// 有最小值和最大值:10 - 20
|
||||
return `${min} - ${max}`
|
||||
} else if (min !== null && min !== undefined && (max === null || max === undefined)) {
|
||||
// 只有最小值:≥ 10
|
||||
return `≥ ${min}`
|
||||
} else if ((min === null || min === undefined) && max !== null && max !== undefined) {
|
||||
// 只有最大值:≤ 20
|
||||
return `≤ ${max}`
|
||||
} else {
|
||||
// 都没有或都是null
|
||||
return '未设置范围'
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有标准描述,返回标准描述
|
||||
if (item.standard) {
|
||||
return item.standard
|
||||
}
|
||||
|
||||
// 如果有标准值,显示标准值
|
||||
if (item.standardValue) {
|
||||
return `标准值: ${item.standardValue}`
|
||||
}
|
||||
|
||||
// 如果是选择类型,显示可选值
|
||||
if (item.standardType === 'select' && item.standardOptions && item.standardOptions.length > 0) {
|
||||
const options = item.standardOptions.map((opt: any) => opt.label).join('、')
|
||||
return `可选值: ${options}`
|
||||
}
|
||||
|
||||
return '未设置标准'
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await QualityApi.deleteQuality(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch { }
|
||||
}
|
||||
|
||||
/** 批量删除 */
|
||||
const handleDeleteBatch = async () => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
await QualityApi.deleteQualityList(checkedIds.value);
|
||||
message.success(t('common.delSuccess'))
|
||||
await getList();
|
||||
} catch { }
|
||||
}
|
||||
|
||||
const checkedIds = ref<number[]>([])
|
||||
const handleRowCheckboxChange = (records: Quality[]) => {
|
||||
checkedIds.value = records.map((item) => item.id);
|
||||
}
|
||||
|
||||
/** 下载附件 */
|
||||
const handleDownloadAttachment = async (row: any) => {
|
||||
try {
|
||||
// 直接使用存储的URL下载文件
|
||||
if (row.attachmentUrl) {
|
||||
const link = document.createElement('a')
|
||||
link.href = row.attachmentUrl
|
||||
link.style.display = 'none'
|
||||
link.target = '_blank' // 在新标签页打开
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
message.success('附件下载成功')
|
||||
} else {
|
||||
message.error('附件不存在')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('下载附件失败:', error)
|
||||
message.error('下载附件失败')
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await QualityApi.exportQuality(queryParams)
|
||||
download.excel(data, `定制-上能石化-${props.pageTitle}.xls`)
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user