1399 lines
38 KiB
Vue
1399 lines
38 KiB
Vue
|
|
<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>
|