Files
RCZN-bs-program/rc_autoplc_front/src/views/goods/index.vue

1399 lines
38 KiB
Vue
Raw Normal View History

2026-01-14 14:04:08 +08:00
<template>
<div class="goodsinfo-page">
<el-breadcrumb separator="/" style="margin-bottom: 16px">
<el-breadcrumb-item>流程管理</el-breadcrumb-item>
<el-breadcrumb-item>样品信息管理</el-breadcrumb-item>
</el-breadcrumb>
<!-- 搜索栏 -->
<el-card class="search-card" shadow="never">
<div class="search-bar">
<el-form :inline="true" :model="queryForm" label-width="120px">
<el-form-item label="样品名称">
<el-input
v-model="queryForm.goodsName"
placeholder="输入样品名称"
clearable
style="width: 200px"
/>
</el-form-item>
<el-form-item label="样品基质类型">
<el-select
v-model="queryForm.goodsType"
placeholder="请选择样品基质类型"
clearable
filterable
style="width: 200px"
>
<el-option
v-for="option in goodsTypeOptions"
:key="option.id"
:label="option.dicValue"
:value="option.dicValue"
/>
</el-select>
</el-form-item>
<el-form-item label="样品状态">
<el-select
v-model="queryForm.goodsStatus"
placeholder="请选择样品状态"
clearable
style="width: 200px"
>
<el-option label="待处理" :value="0" />
<el-option label="处理成功" :value="1" />
<el-option label="处理失败" :value="4" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="resetSearch">重置</el-button>
</el-form-item>
</el-form>
<div class="toolbar">
<el-button type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增样品
</el-button>
</div>
</div>
</el-card>
<!-- 样品信息列表 -->
<el-card shadow="never">
<el-table
:data="tableData"
stripe
border
style="width: 100%"
v-loading="loading"
>
<el-table-column
type="index"
label="序号"
width="60"
:index="getIndex"
/>
<el-table-column
prop="goodsName"
label="样品名称"
min-width="150"
:formatter="formatCell"
/>
<el-table-column
prop="goodsType"
label="样品基质类型"
min-width="150"
:formatter="formatCell"
/>
<el-table-column
prop="incomeTime"
label="采样时间"
min-width="120"
:formatter="formatDate"
/>
<el-table-column
prop="goodFrom"
label="采样地点"
min-width="150"
:formatter="formatCell"
/>
<el-table-column
prop="goalDetect"
label="目标检测物"
min-width="150"
:formatter="formatCell"
/>
<el-table-column
prop="barCode"
label="条形码"
min-width="120"
:formatter="formatCell"
/>
<el-table-column
prop="goodsStatus"
label="样品状态"
width="120"
align="center"
>
<template #default="{ row }">
<el-tag :type="getStatusType(row.goodsStatus)" size="small">
{{ getStatusText(row.goodsStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="desc"
label="描述"
min-width="150"
:formatter="formatCell"
/>
<el-table-column label="操作" width="240" fixed="right">
<template #default="scope">
<el-button type="warning" link @click="openFlowSelectDialog(scope.row)">
选择标准
</el-button>
<el-button type="primary" link @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="danger" link @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="pagination.pageNum"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
background
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</div>
</el-card>
<!-- 新增/编辑抽屉 -->
<el-drawer
v-model="drawerVisible"
:title="drawerTitle"
direction="rtl"
size="600px"
:before-close="handleDrawerClose"
>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="120px"
label-position="left"
>
<el-form-item label="样品名称" prop="goodsName">
<el-input
v-model="formData.goodsName"
placeholder="请输入样品名称"
clearable
/>
</el-form-item>
<el-form-item label="样品基质类型" prop="goodsType">
<div style="display: flex; gap: 8px; width: 100%">
<el-select
v-model="formData.goodsType"
placeholder="请选择样品基质类型"
clearable
filterable
style="flex: 1"
>
<el-option
v-for="option in goodsTypeOptions"
:key="option.id"
:label="option.dicValue"
:value="option.dicValue"
/>
</el-select>
<el-button
type="success"
@click="handleConfigGoodsType"
>
<el-icon><Setting /></el-icon>
配置
</el-button>
</div>
</el-form-item>
<el-form-item label="采样时间" prop="incomeTime">
<el-date-picker
v-model="formData.incomeTime"
type="date"
placeholder="请选择采样时间"
value-format="YYYY-MM-DD"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="采样地点">
<el-input
v-model="formData.goodFrom"
placeholder="请输入采样地点"
clearable
/>
</el-form-item>
<el-form-item label="目标检测物">
<el-input
v-model="formData.goalDetect"
placeholder="请输入目标检测物"
clearable
/>
</el-form-item>
<el-form-item label="条形码">
<el-input
v-model="formData.barCode"
placeholder="请输入条形码"
clearable
/>
</el-form-item>
<el-form-item label="描述">
<el-input
v-model="formData.desc"
type="textarea"
:rows="3"
placeholder="请输入描述"
clearable
/>
</el-form-item>
</el-form>
<template #footer>
<div class="drawer-footer">
<el-button @click="handleDrawerClose">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitting">
确定
</el-button>
</div>
</template>
</el-drawer>
<!-- 下拉框选项配置对话框 -->
<el-dialog
v-model="selectOptionsDialogVisible"
:title="`[样品基质类型]下拉框选项列表配置`"
width="600px"
:before-close="handleSelectOptionsDialogClose"
>
<div class="select-options-container">
<!-- 输入区域 -->
<div class="select-options-input-area">
<span class="input-label">选项数据</span>
<el-input
v-model="selectOptionInput"
placeholder="请输入选项数据"
clearable
style="flex: 1; margin-right: 8px"
@keyup.enter="handleAddSelectOption"
/>
<el-button type="primary" @click="handleAddSelectOption">添加</el-button>
</div>
<!-- 选项卡片列表 -->
<div class="select-options-cards">
<div
v-for="option in selectOptionsList"
:key="option.id"
class="select-option-card"
>
<span class="option-text">{{ option.dicValue }}</span>
<el-icon
class="option-delete-icon"
@click="handleDeleteSelectOption(option)"
>
<Close />
</el-icon>
</div>
<div v-if="selectOptionsList.length === 0" class="empty-tip">
暂无选项数据请添加
</div>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleSelectOptionsDialogClose">取消</el-button>
<el-button type="primary" @click="handleSaveSelectOptions">保存</el-button>
</div>
</template>
</el-dialog>
<!-- 选择标准流程对话框 -->
<el-dialog
v-model="flowSelectDialogVisible"
title="选择标准流程"
width="1000px"
:close-on-click-modal="false"
:destroy-on-close="true"
>
<el-table
:data="flowSelectTableData"
stripe
border
style="width: 100%"
v-loading="flowSelectLoading"
highlight-current-row
:row-class-name="getFlowRowClassName"
>
<el-table-column
prop="flowIndex"
label="标准流程序号"
width="130"
:formatter="formatCell"
/>
<el-table-column
prop="flowName"
label="标准流程名称"
min-width="150"
:formatter="formatCell"
/>
<el-table-column
prop="busyCode"
label="业务编号"
min-width="120"
:formatter="formatCell"
/>
<el-table-column
prop="goodsCode"
label="样品编号"
min-width="120"
:formatter="formatCell"
/>
<el-table-column
prop="goodsName"
label="样品名称"
min-width="150"
:formatter="formatCell"
/>
<el-table-column
prop="checkCode"
label="检样编号"
min-width="120"
:formatter="formatCell"
/>
<el-table-column
prop="programName"
label="项目名称"
min-width="150"
:formatter="formatCell"
/>
<el-table-column
prop="testMethod"
label="检测方法"
min-width="150"
:formatter="formatCell"
/>
<el-table-column
prop="scanNum"
label="扫描编号"
min-width="120"
:formatter="formatCell"
/>
<el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }">
<div style="display: flex; align-items: center; gap: 8px;">
<el-button type="success" link @click="handleChooseFlow(row)">
选择
</el-button>
<el-tag
v-if="isFlowSelected(row)"
type="success"
size="small"
effect="dark"
class="selected-tag-icon"
>
<el-icon><Check /></el-icon>
</el-tag>
</div>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="flowSelectPagination.pageNum"
v-model:page-size="flowSelectPagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="flowSelectTotal"
background
@current-change="handleFlowSelectCurrentChange"
@size-change="handleFlowSelectSizeChange"
/>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleFlowSelectCancel">取消</el-button>
<el-button type="primary" @click="handleFlowSelectSave">
保存
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Setting, Close, Check } from '@element-plus/icons-vue'
import {
goodsInfoadd,
goodsInfodel,
goodsInfoupd,
goodsInfolist,
goodsInfobyid,
} from '@/api/tb/goodsinfo'
import { flowInfolist, flowInfobyid } from '@/api/tb/flowinfo'
import { dictypeadd, dictypelist } from '@/api/system/dictype'
import { dicdataadd, dicdatadel, dicdatabydicid } from '@/api/system/dicdata'
interface GoodsInfoItem {
id?: number | string
goodsName?: string
goodsType?: string
incomeTime?: string
goodFrom?: string
goalDetect?: string
barCode?: string
goodsStatus?: number
desc?: string
remark?: string
flowId?: number | string
flowName?: string
}
interface FlowInfoForSelect {
id?: number | string
flowIndex?: number | string
flowName?: string
busyCode?: string
goodsCode?: string
goodsName?: string
checkCode?: string
programName?: string
testMethod?: string
scanNum?: string
}
const loading = ref(false)
const submitting = ref(false)
const tableData = ref<GoodsInfoItem[]>([])
const total = ref(0)
// 查询表单
const queryForm = reactive({
goodsName: '',
goodsType: '',
goodsStatus: undefined as number | undefined,
})
// 分页
const pagination = reactive({
pageNum: 1,
pageSize: 10,
})
// 抽屉相关
const drawerVisible = ref(false)
const drawerTitle = ref('新增样品')
const isEdit = ref(false)
const formRef = ref<FormInstance>()
// 表单数据
const formData = reactive<GoodsInfoItem>({
id: undefined,
goodsName: '',
goodsType: '',
incomeTime: '',
goodFrom: '',
goalDetect: '',
barCode: '',
goodsStatus: 0, // 状态自动设置为0
desc: '',
})
// 表单验证规则
const formRules: FormRules = {
goodsName: [
{ required: true, message: '请输入样品名称', trigger: 'blur' },
],
goodsType: [
{ required: true, message: '请选择样品基质类型', trigger: 'change' },
],
incomeTime: [
{ required: true, message: '请选择采样时间', trigger: 'change' },
],
}
// 样品基质类型下拉框选项
const goodsTypeOptions = ref<any[]>([])
const goodsTypeDicId = ref<number | null>(null)
// 下拉框选项配置相关
const selectOptionsDialogVisible = ref(false)
const selectOptionInput = ref('')
const selectOptionsList = ref<any[]>([])
// 标准流程选择相关
const flowSelectDialogVisible = ref(false)
const flowSelectLoading = ref(false)
const flowSelectTableData = ref<FlowInfoForSelect[]>([])
const flowSelectTotal = ref(0)
const flowSelectPagination = reactive({
pageNum: 1,
pageSize: 10,
})
const currentGoodsForFlow = ref<GoodsInfoItem | null>(null)
const tempSelectedFlowId = ref<number | string | null>(null)
const tempSelectedFlowName = ref('')
const currentBoundFlowName = ref('')
// 获取序号
const getIndex = (index: number) => {
return (pagination.pageNum - 1) * pagination.pageSize + index + 1
}
// 格式化单元格
const formatCell = (_row: any, _column: any, value: any) => {
if (value === 0) return 0
return value === undefined || value === null || value === '' ? '暂无' : value
}
// 格式化日期(只显示年月日)
const formatDate = (_row: any, _column: any, value: any) => {
if (!value) return '暂无'
try {
const date = new Date(value)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
} catch (error) {
return value
}
}
// 获取状态类型
const getStatusType = (status: number | null | undefined) => {
if (status === 0) return 'primary' // 待处理 - 蓝色
if (status === 1) return 'success' // 处理成功 - 绿色
if (status === 4) return 'danger' // 处理失败 - 红色
return 'info'
}
// 获取状态文本
const getStatusText = (status: number | null | undefined) => {
if (status === 0) return '待处理'
if (status === 1) return '处理成功'
if (status === 4) return '处理失败'
return '未知'
}
// 加载样品基质类型下拉框选项
const loadGoodsTypeOptions = async () => {
try {
// 查找或创建"样品基质类型"字典类型
const dicTypeListRes: any = await dictypelist({
pageNum: 1,
pageSize: 1000,
})
const dicTypeData = dicTypeListRes?.data ?? dicTypeListRes ?? {}
const dicTypeRecords = dicTypeData.records || dicTypeData.list || dicTypeData.rows || []
let dicType = dicTypeRecords.find(
(item: any) => item.dicName === '样品基质类型'
)
if (!dicType) {
// 如果不存在,创建新的字典类型
const dicTypeRes: any = await dictypeadd({
dicName: '样品基质类型',
})
if (dicTypeRes?.code === '0' || dicTypeRes?.code === 0 || dicTypeRes?.success) {
const newDicTypeData = dicTypeRes?.data ?? dicTypeRes ?? {}
dicType = { id: newDicTypeData.id }
// 如果返回的数据中没有ID重新查询
if (!dicType.id) {
const refreshListRes: any = await dictypelist({
pageNum: 1,
pageSize: 1000,
})
const refreshData = refreshListRes?.data ?? refreshListRes ?? {}
const refreshRecords = refreshData.records || refreshData.list || refreshData.rows || []
const newDicType = refreshRecords.find(
(item: any) => item.dicName === '样品基质类型'
)
if (newDicType) {
dicType = newDicType
}
}
}
}
if (dicType && dicType.id) {
goodsTypeDicId.value = dicType.id
// 加载选项列表
const optionsRes: any = await dicdatabydicid(dicType.id)
const optionsData = optionsRes?.data ?? optionsRes ?? {}
const optionsRecords = Array.isArray(optionsData) ? optionsData : (optionsData.records || optionsData.list || optionsData.rows || [])
goodsTypeOptions.value = optionsRecords
}
} catch (error) {
console.error('加载样品基质类型选项失败:', error)
}
}
// 获取样品信息列表
const getGoodsInfoList = async () => {
try {
loading.value = true
const params: any = {
pageNum: pagination.pageNum,
pageSize: pagination.pageSize,
}
// 添加查询条件(过滤空值)
if (queryForm.goodsName && queryForm.goodsName.trim()) {
params.goodsName = queryForm.goodsName.trim()
}
if (queryForm.goodsType && queryForm.goodsType.trim()) {
params.goodsType = queryForm.goodsType.trim()
}
if (queryForm.goodsStatus !== undefined && queryForm.goodsStatus !== null) {
params.goodsStatus = queryForm.goodsStatus
}
const res: any = await goodsInfolist(params)
const data = res?.data ?? res ?? {}
// 兼容多种返回格式
const recordsFromData =
data.records ||
data.list ||
data.rows ||
data.items ||
(Array.isArray(data.data) ? data.data : undefined)
const records = Array.isArray(recordsFromData)
? recordsFromData
: Array.isArray(data)
? data
: []
const totalValue =
data.total ?? data.count ?? data.totalCount ?? records.length ?? 0
tableData.value = records
total.value = Number(totalValue) || 0
} catch (error) {
console.error('获取样品信息列表失败:', error)
ElMessage.error('获取样品信息列表失败')
tableData.value = []
total.value = 0
} finally {
loading.value = false
}
}
// 查询
const handleSearch = () => {
pagination.pageNum = 1
getGoodsInfoList()
}
// 重置查询
const resetSearch = () => {
queryForm.goodsName = ''
queryForm.goodsType = ''
queryForm.goodsStatus = undefined
handleSearch()
}
// 分页大小改变
const handleSizeChange = (size: number) => {
pagination.pageSize = size
getGoodsInfoList()
}
// 当前页改变
const handleCurrentChange = (page: number) => {
pagination.pageNum = page
getGoodsInfoList()
}
// 重置表单
const resetForm = () => {
formData.id = undefined
formData.goodsName = ''
formData.goodsType = ''
formData.incomeTime = ''
formData.goodFrom = ''
formData.goalDetect = ''
formData.barCode = ''
formData.goodsStatus = 0
formData.desc = ''
formRef.value?.clearValidate()
}
// 打开新增抽屉
const handleAdd = () => {
isEdit.value = false
drawerTitle.value = '新增样品'
resetForm()
drawerVisible.value = true
}
// 编辑样品信息
const handleEdit = async (row: GoodsInfoItem) => {
try {
if (!row.id) {
ElMessage.warning('缺少样品信息ID')
return
}
loading.value = true
const res: any = await goodsInfobyid(row.id)
if (res.code === '0' || res.code === 0) {
const data = res.data || res || row
formData.id = data.id ?? row.id
formData.goodsName = data.goodsName ?? row.goodsName ?? ''
formData.goodsType = data.goodsType ?? row.goodsType ?? ''
// 处理日期格式
if (data.incomeTime || row.incomeTime) {
const dateValue = data.incomeTime || row.incomeTime
try {
const date = new Date(dateValue)
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
formData.incomeTime = `${year}-${month}-${day}`
} catch (error) {
formData.incomeTime = dateValue
}
} else {
formData.incomeTime = ''
}
formData.goodFrom = data.goodFrom ?? row.goodFrom ?? ''
formData.goalDetect = data.goalDetect ?? row.goalDetect ?? ''
formData.barCode = data.barCode ?? row.barCode ?? ''
formData.goodsStatus = data.goodsStatus ?? row.goodsStatus ?? 0
formData.desc = data.desc ?? row.desc ?? ''
isEdit.value = true
drawerTitle.value = '编辑样品'
drawerVisible.value = true
} else {
ElMessage.error(res.message || res.msg || '获取样品信息失败')
}
} catch (error) {
console.error('获取样品信息失败:', error)
ElMessage.error('获取样品信息失败')
} finally {
loading.value = false
}
}
// 删除样品信息
const handleDelete = async (row: GoodsInfoItem) => {
if (!row.id) {
ElMessage.warning('缺少样品信息ID')
return
}
try {
await ElMessageBox.confirm(
`确定要删除样品信息"${row.goodsName || '未命名'}"吗?`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
loading.value = true
const res: any = await goodsInfodel(row.id)
if (res.code === '0' || res.code === 0) {
ElMessage.success('删除成功')
getGoodsInfoList()
} else {
ElMessage.error(res.message || res.msg || '删除失败')
}
} catch (error: any) {
if (error !== 'cancel') {
console.error('删除样品信息失败:', error)
ElMessage.error('删除样品信息失败')
}
} finally {
loading.value = false
}
}
// 判断某个标准流程是否被选中
const isFlowSelected = (row: FlowInfoForSelect) => {
if (!row.id || !tempSelectedFlowId.value) return false
return String(row.id) === String(tempSelectedFlowId.value)
}
// 给选中的标准流程行添加样式类
const getFlowRowClassName = ({ row }: { row: FlowInfoForSelect }) => {
if (isFlowSelected(row)) {
return 'selected-flow-row'
}
return ''
}
// 获取标准流程列表(选择弹框用)
const getFlowInfoListForSelect = async () => {
try {
flowSelectLoading.value = true
const params: any = {
pageNum: flowSelectPagination.pageNum,
pageSize: flowSelectPagination.pageSize,
}
const res: any = await flowInfolist(params)
const data = res?.data ?? res ?? {}
const recordsFromData =
data.records ||
data.list ||
data.rows ||
data.items ||
(Array.isArray(data.data) ? data.data : undefined)
const records = Array.isArray(recordsFromData)
? recordsFromData
: Array.isArray(data)
? data
: []
const totalValue =
data.total ?? data.count ?? data.totalCount ?? records.length ?? 0
flowSelectTableData.value = records
flowSelectTotal.value = Number(totalValue) || 0
} catch (error) {
console.error('获取标准流程列表失败:', error)
ElMessage.error('获取标准流程列表失败')
flowSelectTableData.value = []
flowSelectTotal.value = 0
} finally {
flowSelectLoading.value = false
}
}
// 打开标准流程选择对话框
const openFlowSelectDialog = async (row: GoodsInfoItem) => {
if (!row.id) {
ElMessage.warning('缺少样品信息ID')
return
}
currentGoodsForFlow.value = row
tempSelectedFlowId.value = null
tempSelectedFlowName.value = ''
currentBoundFlowName.value = ''
flowSelectPagination.pageNum = 1
flowSelectPagination.pageSize = 10
// 如果样品已绑定标准流程,读取已绑定流程名称并设置为选中状态
const rawFlowId = (row as any).flowId
if (rawFlowId !== undefined && rawFlowId !== null && Number(rawFlowId) !== 0) {
try {
const res: any = await flowInfobyid(rawFlowId)
if (res.code === '0' || res.code === 0) {
const data = res.data || res || {}
const flowName = data.flowName || row.flowName || ''
currentBoundFlowName.value = flowName
// 设置为已选中状态,这样打开弹框时就会显示选中效果
tempSelectedFlowId.value = rawFlowId
tempSelectedFlowName.value = flowName
}
} catch (error) {
console.error('获取已绑定标准流程信息失败:', error)
}
}
flowSelectDialogVisible.value = true
await getFlowInfoListForSelect()
}
// 标准流程分页变化(页码)
const handleFlowSelectCurrentChange = (page: number) => {
flowSelectPagination.pageNum = page
getFlowInfoListForSelect()
}
// 标准流程分页变化(每页大小)
const handleFlowSelectSizeChange = (size: number) => {
flowSelectPagination.pageSize = size
getFlowInfoListForSelect()
}
// 在弹框中选择某个标准流程
const handleChooseFlow = async (row: FlowInfoForSelect) => {
if (!row.id) {
return
}
const newFlowId = row.id
const newFlowName = row.flowName || ''
// 如果当前没有样品上下文,直接缓存
if (!currentGoodsForFlow.value) {
tempSelectedFlowId.value = newFlowId
tempSelectedFlowName.value = newFlowName
return
}
const existingFlowIdRaw = (currentGoodsForFlow.value as any).flowId
const existingFlowId =
existingFlowIdRaw === undefined || existingFlowIdRaw === null
? 0
: Number(existingFlowIdRaw)
// 如果未绑定或绑定为0直接选择
if (!existingFlowId || existingFlowId === 0) {
tempSelectedFlowId.value = newFlowId
tempSelectedFlowName.value = newFlowName
return
}
// 如果选择的是当前已绑定的流程
if (existingFlowId === Number(newFlowId)) {
tempSelectedFlowId.value = newFlowId
tempSelectedFlowName.value = newFlowName
return
}
// 已经有绑定,选择新的需要确认
const boundName =
currentBoundFlowName.value || tempSelectedFlowName.value || '当前标准流程'
try {
await ElMessageBox.confirm(
`该样品已绑定【${boundName}】,确定换绑吗?`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
tempSelectedFlowId.value = newFlowId
tempSelectedFlowName.value = newFlowName
} catch (error) {
// 取消换绑
}
}
// 取消标准流程选择(清空缓存并关闭对话框)
const handleFlowSelectCancel = () => {
tempSelectedFlowId.value = null
tempSelectedFlowName.value = ''
currentBoundFlowName.value = ''
flowSelectDialogVisible.value = false
}
// 保存标准流程选择(调用更新接口)
const handleFlowSelectSave = async () => {
if (!currentGoodsForFlow.value || !currentGoodsForFlow.value.id) {
ElMessage.warning('缺少样品信息ID无法保存')
return
}
// 如果没有选择任何标准流程,相当于不变更,直接关闭
if (!tempSelectedFlowId.value) {
flowSelectDialogVisible.value = false
return
}
try {
const res: any = await goodsInfoupd({
id: currentGoodsForFlow.value.id,
flowId: tempSelectedFlowId.value,
})
if (res.code === '0' || res.code === 0) {
ElMessage.success('标准流程绑定成功')
flowSelectDialogVisible.value = false
tempSelectedFlowId.value = null
tempSelectedFlowName.value = ''
currentBoundFlowName.value = ''
// 重新加载样品列表,刷新绑定显示
getGoodsInfoList()
} else {
ElMessage.error(res.message || res.msg || '标准流程绑定失败')
}
} catch (error) {
console.error('保存标准流程绑定失败:', error)
ElMessage.error('保存标准流程绑定失败')
}
}
// 提交表单
const handleSubmit = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
submitting.value = true
// 构建提交数据
const submitData: any = {
goodsName: formData.goodsName,
goodsType: formData.goodsType,
incomeTime: formData.incomeTime ? `${formData.incomeTime}T00:00:00` : undefined,
goodFrom: formData.goodFrom || undefined,
goalDetect: formData.goalDetect || undefined,
barCode: formData.barCode || undefined,
desc: formData.desc || undefined,
}
// 移除空字符串字段
Object.keys(submitData).forEach((key) => {
if (submitData[key] === '' || submitData[key] === null) {
delete submitData[key]
}
})
let res: any
if (isEdit.value && formData.id) {
// 编辑 - 需传入样品信息ID,其他字段可选(非空则更新)
res = await goodsInfoupd({
id: formData.id,
...submitData,
})
} else {
// 新增 - 状态自动设置为0
res = await goodsInfoadd({
...submitData,
goodsStatus: 0,
})
}
if (res.code === '0' || res.code === 0) {
ElMessage.success(isEdit.value ? '编辑成功' : '新增成功')
drawerVisible.value = false
getGoodsInfoList()
} else {
ElMessage.error(res.message || res.msg || (isEdit.value ? '编辑失败' : '新增失败'))
}
} catch (error: any) {
if (error !== false) {
console.error('提交失败:', error)
ElMessage.error('提交失败,请检查表单')
}
} finally {
submitting.value = false
}
}
// 关闭抽屉
const handleDrawerClose = () => {
drawerVisible.value = false
resetForm()
}
// 打开下拉框选项配置对话框
const handleConfigGoodsType = async () => {
try {
if (!goodsTypeDicId.value) {
// 如果字典ID不存在先创建或查找
await loadGoodsTypeOptions()
}
if (!goodsTypeDicId.value) {
ElMessage.warning('无法获取样品基质类型字典ID')
return
}
selectOptionsDialogVisible.value = true
await loadSelectOptions(goodsTypeDicId.value)
} catch (error) {
console.error('打开下拉框配置失败:', error)
ElMessage.error('打开下拉框配置失败')
}
}
// 加载下拉框选项列表
const loadSelectOptions = async (dicId: number) => {
try {
const res: any = await dicdatabydicid(dicId)
const data = res?.data ?? res ?? {}
const records = Array.isArray(data) ? data : (data.records || data.list || data.rows || [])
selectOptionsList.value = records
} catch (error) {
console.error('加载下拉框选项失败:', error)
ElMessage.error('加载下拉框选项失败')
selectOptionsList.value = []
}
}
// 添加下拉框选项
const handleAddSelectOption = async () => {
if (!selectOptionInput.value || selectOptionInput.value.trim() === '') {
ElMessage.warning('请输入选项数据')
return
}
if (!goodsTypeDicId.value) {
ElMessage.error('字典类型ID不存在')
return
}
try {
const res: any = await dicdataadd({
dicId: goodsTypeDicId.value,
dicLabel: '样品基质类型',
dicValue: selectOptionInput.value.trim(),
})
if (res?.code === '0' || res?.code === 0 || res?.success) {
ElMessage.success('添加成功')
selectOptionInput.value = ''
// 重新加载选项列表
await loadSelectOptions(goodsTypeDicId.value)
// 重新加载下拉框选项
await loadGoodsTypeOptions()
} else {
ElMessage.error(res?.message || res?.msg || '添加失败')
}
} catch (error) {
console.error('添加下拉框选项失败:', error)
ElMessage.error('添加下拉框选项失败')
}
}
// 删除下拉框选项
const handleDeleteSelectOption = async (option: any) => {
try {
await ElMessageBox.confirm(
`确定要删除"${option.dicValue || '该选项'}"吗?`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
const res: any = await dicdatadel(option.id)
if (res?.code === '0' || res?.code === 0 || res?.success) {
ElMessage.success('删除成功')
// 重新加载选项列表
if (goodsTypeDicId.value) {
await loadSelectOptions(goodsTypeDicId.value)
// 重新加载下拉框选项
await loadGoodsTypeOptions()
}
} else {
ElMessage.error(res?.message || res?.msg || '删除失败')
}
} catch (error: any) {
if (error !== 'cancel') {
console.error('删除下拉框选项失败:', error)
ElMessage.error('删除下拉框选项失败')
}
}
}
// 保存下拉框选项配置
const handleSaveSelectOptions = async () => {
if (selectOptionsList.value.length === 0) {
await ElMessageBox.alert(
'样品基质类型的下拉框选项列表还未配置,请先完成配置',
'提示',
{
confirmButtonText: '确定',
type: 'warning',
}
)
return
}
selectOptionsDialogVisible.value = false
ElMessage.success('样品基质类型下拉框选项列表保存成功!')
// 重新加载下拉框选项
await loadGoodsTypeOptions()
}
// 关闭下拉框选项配置对话框
const handleSelectOptionsDialogClose = () => {
selectOptionsDialogVisible.value = false
selectOptionInput.value = ''
selectOptionsList.value = []
}
// 初始化
onMounted(() => {
loadGoodsTypeOptions().then(() => {
getGoodsInfoList()
})
})
</script>
<style lang="scss" scoped>
.goodsinfo-page {
display: flex;
flex-direction: column;
gap: 12px;
}
.search-card {
padding-bottom: 30px;
:deep(.el-card__body) {
padding: 16px 20px;
}
}
.search-bar {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 8px;
:deep(.el-form) {
margin-bottom: 0;
margin-left: -50px;
}
:deep(.el-form-item) {
margin-right: 8px;
margin-bottom: 0;
}
:deep(.el-form-item__label) {
padding-right: 8px;
}
}
.toolbar {
display: flex;
align-items: center;
gap: 8px;
}
.pagination {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
.drawer-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.select-options-container {
display: flex;
flex-direction: column;
gap: 16px;
}
.select-options-input-area {
display: flex;
align-items: center;
gap: 8px;
}
.input-label {
min-width: 80px;
font-weight: 500;
font-size: 16px;
}
.select-options-cards {
display: flex;
flex-wrap: wrap;
gap: 20px;
min-height: 100px;
max-height: 300px;
overflow-y: auto;
padding: 20px;
background: linear-gradient(135deg, #e3f2fd 0%, #f0f2f5 100%);
border-radius: 4px;
background-image:
linear-gradient(rgba(64, 158, 255, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(64, 158, 255, 0.03) 1px, transparent 1px);
background-size: 20px 20px;
}
.select-option-card {
position: relative;
padding: 16px 20px;
background-color: #fff;
border: 1px solid rgba(64, 158, 255, 0.1);
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
cursor: default;
transition: all 0.3s;
min-width: 120px;
font-size: 16px;
}
.select-option-card:hover {
box-shadow: 0 4px 16px rgba(64, 158, 255, 0.2);
transform: translateY(-4px);
border-color: rgba(64, 158, 255, 0.3);
}
.option-text {
font-size: 1.2em;
color: #303133;
font-weight: bold;
}
.option-delete-icon {
position: absolute;
top: 10px;
right: 10px;
font-size: 16px;
color: #909399;
cursor: pointer;
transition: color 0.3s;
padding: 4px;
}
.option-delete-icon:hover {
color: #f56c6c;
}
.empty-tip {
width: 100%;
text-align: center;
color: #909399;
font-size: 16px;
padding: 20px 0;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
// 标准流程选择表格 - 选中行的样式
:deep(.selected-flow-row) {
background-color: #e8f4ff !important;
td {
background-color: #e8f4ff !important;
}
&:hover > td {
background-color: #d9ecff !important;
}
}
// 已选择标签 - 正方形小绿块样式
.selected-tag-icon {
width: 24px;
height: 24px;
padding: 0 !important;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 4px;
.el-icon {
margin: 0;
font-size: 14px;
}
}
</style>