| | |
| | | <template> |
| | | <div class="app-container metric-binding"> |
| | | <el-row :gutter="16" class="metric-binding-row"> |
| | | <el-row :gutter="16" |
| | | class="metric-binding-row"> |
| | | <!-- 左侧:检测标准列表 --> |
| | | <el-col :xs="24" :sm="24" :md="12" :lg="14" :xl="14" class="left-col"> |
| | | <el-col :xs="24" |
| | | :sm="24" |
| | | :md="12" |
| | | :lg="14" |
| | | :xl="14" |
| | | class="left-col"> |
| | | <div class="panel left-panel"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | :column="standardColumns" |
| | | :tableData="standardTableData" |
| | | :page="page" |
| | | :isSelection="false" |
| | | :rowClassName="rowClassNameCenter" |
| | | :tableLoading="tableLoading" |
| | | :rowClick="handleTableRowClick" |
| | | @pagination="handlePagination" |
| | | :total="page.total" |
| | | > |
| | | <template #standardNoCell="{ row }"> |
| | | <span class="clickable-link" @click="handleStandardRowClick(row)"> |
| | | {{ row.standardNo }} |
| | | </span> |
| | | </template> |
| | | |
| | | <!-- 表头搜索 --> |
| | | <template #standardNoHeader> |
| | | <el-input |
| | | v-model="searchForm.standardNo" |
| | | placeholder="标准编号" |
| | | clearable |
| | | size="small" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" |
| | | /> |
| | | </template> |
| | | <template #standardNameHeader> |
| | | <el-input |
| | | v-model="searchForm.standardName" |
| | | placeholder="标准名称" |
| | | clearable |
| | | size="small" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" |
| | | /> |
| | | </template> |
| | | <template #inspectTypeHeader> |
| | | <el-select |
| | | v-model="searchForm.inspectType" |
| | | placeholder="类别" |
| | | clearable |
| | | size="small" |
| | | style="width: 120px" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" |
| | | > |
| | | <el-option label="原材料检验" value="0" /> |
| | | <el-option label="过程检验" value="1" /> |
| | | <el-option label="出厂检验" value="2" /> |
| | | </el-select> |
| | | </template> |
| | | <template #stateHeader> |
| | | <el-select |
| | | v-model="searchForm.state" |
| | | placeholder="状态" |
| | | clearable |
| | | size="small" |
| | | style="width: 110px" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" |
| | | > |
| | | <el-option label="草稿" value="0" /> |
| | | <el-option label="通过" value="1" /> |
| | | <el-option label="撤销" value="2" /> |
| | | </el-select> |
| | | </template> |
| | | </PIMTable> |
| | | <PIMTable rowKey="id" |
| | | :column="standardColumns" |
| | | :tableData="standardTableData" |
| | | :page="page" |
| | | :isSelection="false" |
| | | :rowClassName="rowClassNameCenter" |
| | | :tableLoading="tableLoading" |
| | | :rowClick="handleTableRowClick" |
| | | @pagination="handlePagination" |
| | | :total="page.total"> |
| | | <template #standardNoCell="{ row }"> |
| | | <span class="clickable-link" |
| | | @click="handleStandardRowClick(row)"> |
| | | {{ row.standardNo }} |
| | | </span> |
| | | </template> |
| | | <!-- 表头搜索 --> |
| | | <template #standardNoHeader> |
| | | <el-input v-model="searchForm.standardNo" |
| | | placeholder="标准编号" |
| | | clearable |
| | | size="small" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" /> |
| | | </template> |
| | | <template #standardNameHeader> |
| | | <el-input v-model="searchForm.standardName" |
| | | placeholder="标准名称" |
| | | clearable |
| | | size="small" |
| | | @change="handleQuery" |
| | | @clear="handleQuery" /> |
| | | </template> |
| | | <template #inspectTypeHeader> |
| | | <el-select v-model="searchForm.inspectType" |
| | | placeholder="类别" |
| | | clearable |
| | | size="small" |
| | | style="width: 120px" |
| | | @change="handleQuery" |
| | | @clear="handleQuery"> |
| | | <el-option label="原材料检验" |
| | | value="0" /> |
| | | <el-option label="过程检验" |
| | | value="1" /> |
| | | <el-option label="出厂检验" |
| | | value="2" /> |
| | | </el-select> |
| | | </template> |
| | | <template #stateHeader> |
| | | <el-select v-model="searchForm.state" |
| | | placeholder="状态" |
| | | clearable |
| | | size="small" |
| | | style="width: 110px" |
| | | @change="handleQuery" |
| | | @clear="handleQuery"> |
| | | <el-option label="草稿" |
| | | value="0" /> |
| | | <el-option label="通过" |
| | | value="1" /> |
| | | <el-option label="撤销" |
| | | value="2" /> |
| | | </el-select> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | | </el-col> |
| | | |
| | | <!-- 右侧:绑定列表 --> |
| | | <el-col :xs="24" :sm="24" :md="12" :lg="10" :xl="10" class="right-col"> |
| | | <el-col :xs="24" |
| | | :sm="24" |
| | | :md="12" |
| | | :lg="10" |
| | | :xl="10" |
| | | class="right-col"> |
| | | <div class="panel right-panel"> |
| | | <div class="right-header"> |
| | | <div class="title">绑定关系</div> |
| | | <div class="desc" v-if="currentStandard"> |
| | | 当前检测标准编号:<span class="link-text">{{ currentStandard.standardNo }}</span> |
| | | </div> |
| | | <div class="desc" v-else>请选择左侧检测标准</div> |
| | | </div> |
| | | |
| | | <div class="right-toolbar"> |
| | | <el-button type="primary" :disabled="!currentStandard" @click="openBindingDialog">添加绑定</el-button> |
| | | <el-button type="danger" plain :disabled="!currentStandard" @click="handleBatchUnbind">删除</el-button> |
| | | </div> |
| | | |
| | | <el-table |
| | | v-loading="bindingLoading" |
| | | :data="bindingTableData" |
| | | border |
| | | :row-class-name="() => 'row-center'" |
| | | class="center-table" |
| | | style="width: 100%" |
| | | height="calc(100vh - 220px)" |
| | | @selection-change="handleBindingSelectionChange" |
| | | > |
| | | <el-table-column type="selection" width="48" align="center" /> |
| | | <el-table-column type="index" label="序号" width="60" align="center" /> |
| | | <el-table-column prop="productName" label="产品名称" min-width="140" /> |
| | | <el-table-column label="操作" width="120" fixed="right" align="center"> |
| | | <template #default="{ row }"> |
| | | <el-button link type="danger" size="small" @click="handleUnbind(row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <div class="right-header"> |
| | | <div class="title">绑定关系</div> |
| | | <div class="desc" |
| | | v-if="currentStandard"> |
| | | 当前检测标准编号:<span class="link-text">{{ currentStandard.standardNo }}</span> |
| | | </div> |
| | | <div class="desc" |
| | | v-else>请选择左侧检测标准</div> |
| | | </div> |
| | | <div class="right-toolbar"> |
| | | <el-button type="primary" |
| | | :disabled="!currentStandard" |
| | | @click="openBindingDialog">添加绑定</el-button> |
| | | <el-button type="danger" |
| | | plain |
| | | :disabled="!currentStandard" |
| | | @click="handleBatchUnbind">删除</el-button> |
| | | </div> |
| | | <el-table v-loading="bindingLoading" |
| | | :data="bindingTableData" |
| | | border |
| | | :row-class-name="() => 'row-center'" |
| | | class="center-table" |
| | | style="width: 100%" |
| | | height="calc(100vh - 220px)" |
| | | @selection-change="handleBindingSelectionChange"> |
| | | <el-table-column type="selection" |
| | | width="48" |
| | | align="center" /> |
| | | <el-table-column type="index" |
| | | label="序号" |
| | | width="60" |
| | | align="center" /> |
| | | <el-table-column prop="productName" |
| | | label="产品名称" |
| | | min-width="140" /> |
| | | <el-table-column label="操作" |
| | | width="120" |
| | | fixed="right" |
| | | align="center"> |
| | | <template #default="{ row }"> |
| | | <el-button link |
| | | type="danger" |
| | | size="small" |
| | | @click="handleUnbind(row)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <!-- 添加绑定弹框 --> |
| | | <el-dialog |
| | | v-model="bindingDialogVisible" |
| | | title="添加绑定" |
| | | width="520px" |
| | | @close="closeBindingDialog" |
| | | > |
| | | <el-dialog v-model="bindingDialogVisible" |
| | | title="添加绑定" |
| | | width="520px" |
| | | @close="closeBindingDialog"> |
| | | <el-form label-width="100px"> |
| | | <el-form-item label="产品"> |
| | | <el-tree-select |
| | | v-model="selectedProductIds" |
| | | multiple |
| | | collapse-tags |
| | | collapse-tags-tooltip |
| | | placeholder="请选择产品(可多选)" |
| | | clearable |
| | | check-strictly |
| | | :data="productOptions" |
| | | :render-after-expand="false" |
| | | style="width: 100%" |
| | | /> |
| | | <el-button type="primary" |
| | | @click="openProductSelectDialog">选择产品</el-button> |
| | | <div class="selected-products mt-2" |
| | | v-if="selectedProducts.length > 0"> |
| | | <el-tag v-for="product in selectedProducts" |
| | | :key="product.id" |
| | | closable |
| | | @close="removeSelectedProduct(product.id)" |
| | | class="mr-2 mb-2"> |
| | | {{ product.productName }} - {{ product.model }} |
| | | </el-tag> |
| | | </div> |
| | | <div v-else |
| | | class="text-gray-400"></div> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="closeBindingDialog">取消</el-button> |
| | | <el-button type="primary" @click="submitBinding">确定</el-button> |
| | | <el-button type="primary" |
| | | :disabled="selectedProducts.length === 0" |
| | | @click="submitBinding">确定</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | <!-- 产品选择对话框 --> |
| | | <ProductSelectDialog v-model="productSelectDialogVisible" |
| | | :single="false" |
| | | @confirm="handleProductSelect" /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search } from '@element-plus/icons-vue' |
| | | import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue' |
| | | import { ElMessageBox } from 'element-plus' |
| | | import PIMTable from '@/components/PIMTable/PIMTable.vue' |
| | | import { productTreeList } from '@/api/basicData/product.js' |
| | | import { |
| | | qualityTestStandardListPage |
| | | } from '@/api/qualityManagement/metricMaintenance.js' |
| | | import { productProcessListPage } from '@/api/basicData/productProcess.js' |
| | | import { |
| | | qualityTestStandardBindingList, |
| | | qualityTestStandardBindingAdd, |
| | | qualityTestStandardBindingDel |
| | | } from '@/api/qualityManagement/qualityTestStandardBinding.js' |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { ref, reactive, toRefs, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessageBox, ElMessage } from "element-plus"; |
| | | import PIMTable from "@/components/PIMTable/PIMTable.vue"; |
| | | import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue"; |
| | | import { qualityTestStandardListPage } from "@/api/qualityManagement/metricMaintenance.js"; |
| | | import { productProcessListPage } from "@/api/basicData/productProcess.js"; |
| | | import { |
| | | qualityTestStandardBindingList, |
| | | qualityTestStandardBindingAdd, |
| | | qualityTestStandardBindingDel, |
| | | } from "@/api/qualityManagement/qualityTestStandardBinding.js"; |
| | | |
| | | const { proxy } = getCurrentInstance() |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | // 左侧标准列表:整行内容居中(配合样式) |
| | | const rowClassNameCenter = () => 'row-center' |
| | | // 左侧标准列表:整行内容居中(配合样式) |
| | | const rowClassNameCenter = () => "row-center"; |
| | | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | standardNo: '', |
| | | standardName: '', |
| | | state: '', |
| | | inspectType: '' |
| | | } |
| | | }) |
| | | const { searchForm } = toRefs(data) |
| | | const data = reactive({ |
| | | searchForm: { |
| | | standardNo: "", |
| | | standardName: "", |
| | | state: "", |
| | | inspectType: "", |
| | | }, |
| | | }); |
| | | const { searchForm } = toRefs(data); |
| | | |
| | | // 左侧 |
| | | const standardTableData = ref([]) |
| | | const tableLoading = ref(false) |
| | | const page = reactive({ current: 1, size: 10, total: 0 }) |
| | | // 左侧 |
| | | const standardTableData = ref([]); |
| | | const tableLoading = ref(false); |
| | | const page = reactive({ current: 1, size: 10, total: 0 }); |
| | | |
| | | // 工序下拉(用于列表回显) |
| | | const processOptions = ref([]) |
| | | // 工序下拉(用于列表回显) |
| | | const processOptions = ref([]); |
| | | |
| | | const getProcessList = async () => { |
| | | try { |
| | | const res = await productProcessListPage({ current: 1, size: 1000 }) |
| | | if (res?.code === 200) { |
| | | const records = res?.data?.records || [] |
| | | processOptions.value = records.map((item) => ({ |
| | | label: item.processName || item.name || item.label, |
| | | value: item.id || item.processId || item.value |
| | | })) |
| | | const getProcessList = async () => { |
| | | try { |
| | | const res = await productProcessListPage({ current: 1, size: 1000 }); |
| | | if (res?.code === 200) { |
| | | const records = res?.data?.records || []; |
| | | processOptions.value = records.map(item => ({ |
| | | label: item.processName || item.name || item.label, |
| | | value: item.id || item.processId || item.value, |
| | | })); |
| | | } |
| | | } catch (error) { |
| | | console.error("获取工序列表失败:", error); |
| | | } |
| | | } catch (error) { |
| | | console.error('获取工序列表失败:', error) |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const standardColumns = ref([ |
| | | { label: '标准编号', prop: 'standardNo', dataType: 'slot', slot: 'standardNoCell', minWidth: 160, align: 'center', headerSlot: 'standardNoHeader' }, |
| | | { label: '标准名称', prop: 'standardName', minWidth: 180, align: 'center', headerSlot: 'standardNameHeader' }, |
| | | { |
| | | label: '类别', |
| | | prop: 'inspectType', |
| | | headerSlot: 'inspectTypeHeader', |
| | | align: 'center', |
| | | dataType: 'tag', |
| | | formatData: (val) => { |
| | | const map = { 0: '原材料检验', 1: '过程检验', 2: '出厂检验' } |
| | | return map[val] || val |
| | | } |
| | | }, |
| | | { |
| | | label: '工序', |
| | | prop: 'processId', |
| | | align: 'center', |
| | | dataType: 'tag', |
| | | formatData: (val) => { |
| | | const target = processOptions.value.find( |
| | | (item) => String(item.value) === String(val) |
| | | ) |
| | | return target?.label || val |
| | | } |
| | | }, |
| | | { |
| | | label: '备注', |
| | | prop: 'remark', |
| | | minWidth: 160, |
| | | align: 'center' |
| | | } |
| | | // { |
| | | // label: '状态', |
| | | // prop: 'state', |
| | | // headerSlot: 'stateHeader', |
| | | // dataType: 'tag', |
| | | // formatData: (val) => { |
| | | // const map = { 0: '草稿', 1: '通过', 2: '撤销' } |
| | | // return map[val] || val |
| | | // }, |
| | | // formatType: (val) => { |
| | | // if (val == 1) return 'success' |
| | | // if (val == 2) return 'warning' |
| | | // return 'info' |
| | | // } |
| | | // } |
| | | ]) |
| | | const standardColumns = ref([ |
| | | { |
| | | label: "标准编号", |
| | | prop: "standardNo", |
| | | dataType: "slot", |
| | | slot: "standardNoCell", |
| | | minWidth: 160, |
| | | align: "center", |
| | | headerSlot: "standardNoHeader", |
| | | }, |
| | | { |
| | | label: "标准名称", |
| | | prop: "standardName", |
| | | minWidth: 180, |
| | | align: "center", |
| | | headerSlot: "standardNameHeader", |
| | | }, |
| | | { |
| | | label: "类别", |
| | | prop: "inspectType", |
| | | headerSlot: "inspectTypeHeader", |
| | | align: "center", |
| | | dataType: "tag", |
| | | formatData: val => { |
| | | const map = { 0: "原材料检验", 1: "过程检验", 2: "出厂检验" }; |
| | | return map[val] || val; |
| | | }, |
| | | }, |
| | | { |
| | | label: "工序", |
| | | prop: "processId", |
| | | align: "center", |
| | | dataType: "tag", |
| | | formatData: val => { |
| | | const target = processOptions.value.find( |
| | | item => String(item.value) === String(val) |
| | | ); |
| | | return target?.label || val; |
| | | }, |
| | | }, |
| | | { |
| | | label: "备注", |
| | | prop: "remark", |
| | | minWidth: 160, |
| | | align: "center", |
| | | }, |
| | | // { |
| | | // label: '状态', |
| | | // prop: 'state', |
| | | // headerSlot: 'stateHeader', |
| | | // dataType: 'tag', |
| | | // formatData: (val) => { |
| | | // const map = { 0: '草稿', 1: '通过', 2: '撤销' } |
| | | // return map[val] || val |
| | | // }, |
| | | // formatType: (val) => { |
| | | // if (val == 1) return 'success' |
| | | // if (val == 2) return 'warning' |
| | | // return 'info' |
| | | // } |
| | | // } |
| | | ]); |
| | | |
| | | const currentStandard = ref(null) |
| | | const currentStandard = ref(null); |
| | | |
| | | // 右侧绑定 |
| | | const bindingTableData = ref([]) |
| | | const bindingLoading = ref(false) |
| | | const bindingSelectedRows = ref([]) |
| | | const bindingDialogVisible = ref(false) |
| | | // 右侧绑定 |
| | | const bindingTableData = ref([]); |
| | | const bindingLoading = ref(false); |
| | | const bindingSelectedRows = ref([]); |
| | | const bindingDialogVisible = ref(false); |
| | | |
| | | // 产品树(用于绑定选择) |
| | | const productOptions = ref([]) |
| | | const selectedProductIds = ref([]) |
| | | // 产品选择 |
| | | const productSelectDialogVisible = ref(false); |
| | | const selectedProducts = ref([]); |
| | | |
| | | const getProductOptions = async () => { |
| | | // 避免重复请求 |
| | | if (productOptions.value?.length) return |
| | | const res = await productTreeList() |
| | | productOptions.value = convertIdToValue(Array.isArray(res) ? res : []) |
| | | } |
| | | const openProductSelectDialog = () => { |
| | | productSelectDialogVisible.value = true; |
| | | }; |
| | | |
| | | function convertIdToValue(data) { |
| | | return (data || []).map((item) => { |
| | | const { id, children, ...rest } = item |
| | | const newItem = { |
| | | ...rest, |
| | | value: id |
| | | } |
| | | if (children && children.length > 0) { |
| | | newItem.children = convertIdToValue(children) |
| | | } |
| | | return newItem |
| | | }) |
| | | } |
| | | const handleProductSelect = products => { |
| | | // 合并已选择的产品,避免重复 |
| | | const existingIds = new Set(selectedProducts.value.map(p => p.id)); |
| | | const newProducts = products.filter(p => !existingIds.has(p.id)); |
| | | selectedProducts.value = [...selectedProducts.value, ...newProducts]; |
| | | }; |
| | | |
| | | const handleQuery = () => { |
| | | page.current = 1 |
| | | getStandardList() |
| | | } |
| | | const removeSelectedProduct = id => { |
| | | selectedProducts.value = selectedProducts.value.filter(p => p.id !== id); |
| | | }; |
| | | |
| | | const handlePagination = (obj) => { |
| | | page.current = obj.page |
| | | page.size = obj.limit |
| | | getStandardList() |
| | | } |
| | | const handleQuery = () => { |
| | | page.current = 1; |
| | | getStandardList(); |
| | | }; |
| | | |
| | | const getStandardList = () => { |
| | | tableLoading.value = true |
| | | qualityTestStandardListPage({ |
| | | ...searchForm.value, |
| | | current: page.current, |
| | | size: page.size, |
| | | state: 1 |
| | | }) |
| | | .then((res) => { |
| | | const records = res?.data?.records || [] |
| | | standardTableData.value = records |
| | | page.total = res?.data?.total || records.length |
| | | const handlePagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getStandardList(); |
| | | }; |
| | | |
| | | const getStandardList = () => { |
| | | tableLoading.value = true; |
| | | qualityTestStandardListPage({ |
| | | ...searchForm.value, |
| | | current: page.current, |
| | | size: page.size, |
| | | state: 1, |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false |
| | | }) |
| | | } |
| | | .then(res => { |
| | | const records = res?.data?.records || []; |
| | | standardTableData.value = records; |
| | | page.total = res?.data?.total || records.length; |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // 表格行点击,加载右侧绑定列表 |
| | | const handleTableRowClick = (row) => { |
| | | currentStandard.value = row |
| | | loadBindingList() |
| | | } |
| | | // 表格行点击,加载右侧绑定列表 |
| | | const handleTableRowClick = row => { |
| | | currentStandard.value = row; |
| | | loadBindingList(); |
| | | }; |
| | | |
| | | // 左侧行点击,加载右侧绑定列表(保留用于标准编号列的点击) |
| | | const handleStandardRowClick = (row) => { |
| | | currentStandard.value = row |
| | | loadBindingList() |
| | | } |
| | | // 左侧行点击,加载右侧绑定列表(保留用于标准编号列的点击) |
| | | const handleStandardRowClick = row => { |
| | | currentStandard.value = row; |
| | | loadBindingList(); |
| | | }; |
| | | |
| | | const loadBindingList = () => { |
| | | if (!currentStandard.value?.id) { |
| | | bindingTableData.value = [] |
| | | return |
| | | } |
| | | bindingLoading.value = true |
| | | qualityTestStandardBindingList({ testStandardId: currentStandard.value.id }) |
| | | .then((res) => { |
| | | const base = res?.data || [] |
| | | // 将当前标准的工序和备注带到绑定列表中展示 |
| | | bindingTableData.value = base.map((item) => ({ |
| | | ...item, |
| | | processId: currentStandard.value?.processId, |
| | | remark: currentStandard.value?.remark |
| | | })) |
| | | }) |
| | | .finally(() => { |
| | | bindingLoading.value = false |
| | | }) |
| | | } |
| | | const loadBindingList = () => { |
| | | if (!currentStandard.value?.id) { |
| | | bindingTableData.value = []; |
| | | return; |
| | | } |
| | | bindingLoading.value = true; |
| | | qualityTestStandardBindingList({ testStandardId: currentStandard.value.id }) |
| | | .then(res => { |
| | | const base = res?.data || []; |
| | | // 将当前标准的工序和备注带到绑定列表中展示 |
| | | bindingTableData.value = base.map(item => ({ |
| | | ...item, |
| | | processId: currentStandard.value?.processId, |
| | | remark: currentStandard.value?.remark, |
| | | })); |
| | | }) |
| | | .finally(() => { |
| | | bindingLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const handleBindingSelectionChange = (selection) => { |
| | | bindingSelectedRows.value = selection |
| | | } |
| | | const handleBindingSelectionChange = selection => { |
| | | bindingSelectedRows.value = selection; |
| | | }; |
| | | |
| | | const openBindingDialog = () => { |
| | | if (!currentStandard.value?.id) return |
| | | selectedProductIds.value = [] |
| | | getProductOptions() |
| | | bindingDialogVisible.value = true |
| | | } |
| | | const openBindingDialog = () => { |
| | | if (!currentStandard.value?.id) return; |
| | | selectedProducts.value = []; |
| | | bindingDialogVisible.value = true; |
| | | }; |
| | | |
| | | const closeBindingDialog = () => { |
| | | bindingDialogVisible.value = false |
| | | } |
| | | const closeBindingDialog = () => { |
| | | bindingDialogVisible.value = false; |
| | | selectedProducts.value = []; |
| | | }; |
| | | |
| | | const submitBinding = async () => { |
| | | const testStandardId = currentStandard.value?.id |
| | | if (!testStandardId) return |
| | | const ids = (selectedProductIds.value || []).filter(Boolean) |
| | | if (!ids.length) { |
| | | proxy.$message.warning('请选择产品') |
| | | return |
| | | } |
| | | const payload = ids.map((pid) => ({ |
| | | productId: pid, |
| | | testStandardId |
| | | })) |
| | | await qualityTestStandardBindingAdd(payload) |
| | | proxy.$message.success('添加成功') |
| | | bindingDialogVisible.value = false |
| | | loadBindingList() |
| | | } |
| | | const submitBinding = async () => { |
| | | const testStandardId = currentStandard.value?.id; |
| | | if (!testStandardId) return; |
| | | const ids = (selectedProducts.value || []).map(p => p.id).filter(Boolean); |
| | | if (!ids.length) { |
| | | ElMessage.warning("请选择产品"); |
| | | return; |
| | | } |
| | | const payload = ids.map(pid => ({ |
| | | productId: pid, |
| | | testStandardId, |
| | | })); |
| | | await qualityTestStandardBindingAdd(payload); |
| | | ElMessage.success("添加成功"); |
| | | bindingDialogVisible.value = false; |
| | | selectedProducts.value = []; |
| | | loadBindingList(); |
| | | }; |
| | | |
| | | const handleUnbind = async (row) => { |
| | | const id = row?.id ?? row?.qualityTestStandardBindingId |
| | | if (id == null || id === '') return |
| | | try { |
| | | await ElMessageBox.confirm('确认删除该绑定?', '提示', { type: 'warning' }) |
| | | } catch { |
| | | return |
| | | } |
| | | try { |
| | | await qualityTestStandardBindingDel([id]) |
| | | proxy.$message.success('删除成功') |
| | | loadBindingList() |
| | | } catch (err) { |
| | | console.error('删除绑定失败:', err) |
| | | proxy.$message?.error(err?.message || '删除失败') |
| | | } |
| | | } |
| | | const handleUnbind = async row => { |
| | | const id = row?.id ?? row?.qualityTestStandardBindingId; |
| | | if (id == null || id === "") return; |
| | | try { |
| | | await ElMessageBox.confirm("确认删除该绑定?", "提示", { type: "warning" }); |
| | | } catch { |
| | | return; |
| | | } |
| | | try { |
| | | await qualityTestStandardBindingDel([id]); |
| | | proxy.$message.success("删除成功"); |
| | | loadBindingList(); |
| | | } catch (err) { |
| | | console.error("删除绑定失败:", err); |
| | | proxy.$message?.error(err?.message || "删除失败"); |
| | | } |
| | | }; |
| | | |
| | | const handleBatchUnbind = async () => { |
| | | if (!bindingSelectedRows.value.length) { |
| | | proxy.$message.warning('请选择数据') |
| | | return |
| | | } |
| | | const ids = bindingSelectedRows.value |
| | | .map((i) => i?.id ?? i?.qualityTestStandardBindingId) |
| | | .filter((id) => id != null && id !== '') |
| | | if (!ids.length) { |
| | | proxy.$message.warning('选中数据缺少有效 id') |
| | | return |
| | | } |
| | | try { |
| | | await ElMessageBox.confirm('选中的内容将被删除,是否确认删除?', '删除提示', { type: 'warning' }) |
| | | } catch { |
| | | return |
| | | } |
| | | try { |
| | | await qualityTestStandardBindingDel(ids) |
| | | proxy.$message.success('删除成功') |
| | | loadBindingList() |
| | | } catch (err) { |
| | | console.error('批量删除绑定失败:', err) |
| | | proxy.$message?.error(err?.message || '删除失败') |
| | | } |
| | | } |
| | | const handleBatchUnbind = async () => { |
| | | if (!bindingSelectedRows.value.length) { |
| | | proxy.$message.warning("请选择数据"); |
| | | return; |
| | | } |
| | | const ids = bindingSelectedRows.value |
| | | .map(i => i?.id ?? i?.qualityTestStandardBindingId) |
| | | .filter(id => id != null && id !== ""); |
| | | if (!ids.length) { |
| | | proxy.$message.warning("选中数据缺少有效 id"); |
| | | return; |
| | | } |
| | | try { |
| | | await ElMessageBox.confirm( |
| | | "选中的内容将被删除,是否确认删除?", |
| | | "删除提示", |
| | | { type: "warning" } |
| | | ); |
| | | } catch { |
| | | return; |
| | | } |
| | | try { |
| | | await qualityTestStandardBindingDel(ids); |
| | | proxy.$message.success("删除成功"); |
| | | loadBindingList(); |
| | | } catch (err) { |
| | | console.error("批量删除绑定失败:", err); |
| | | proxy.$message?.error(err?.message || "删除失败"); |
| | | } |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getStandardList() |
| | | getProcessList() |
| | | }) |
| | | onMounted(() => { |
| | | getStandardList(); |
| | | getProcessList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .metric-binding { |
| | | padding: 0; |
| | | } |
| | | .metric-binding { |
| | | padding: 0; |
| | | } |
| | | |
| | | .metric-binding-row { |
| | | width: 100%; |
| | | } |
| | | .metric-binding-row { |
| | | width: 100%; |
| | | } |
| | | |
| | | .metric-binding-row .left-col, |
| | | .metric-binding-row .right-col { |
| | | margin-bottom: 16px; |
| | | } |
| | | .metric-binding-row .left-col, |
| | | .metric-binding-row .right-col { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .metric-binding-row .panel { |
| | | background: #ffffff; |
| | | padding: 16px; |
| | | box-sizing: border-box; |
| | | height: 100%; |
| | | min-height: 400px; |
| | | } |
| | | .metric-binding-row .panel { |
| | | background: #ffffff; |
| | | padding: 16px; |
| | | box-sizing: border-box; |
| | | height: 100%; |
| | | min-height: 400px; |
| | | } |
| | | |
| | | .left-panel, |
| | | .right-panel { |
| | | height: 100%; |
| | | } |
| | | .left-panel, |
| | | .right-panel { |
| | | height: 100%; |
| | | } |
| | | |
| | | .toolbar { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 12px; |
| | | } |
| | | .toolbar { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .toolbar-right { |
| | | flex-shrink: 0; |
| | | } |
| | | .toolbar-right { |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .right-header { |
| | | display: flex; |
| | | align-items: baseline; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | .right-header { |
| | | display: flex; |
| | | align-items: baseline; |
| | | justify-content: space-between; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .right-header .title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | } |
| | | .right-header .title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .right-header .desc { |
| | | font-size: 13px; |
| | | color: #666; |
| | | } |
| | | .right-header .desc { |
| | | font-size: 13px; |
| | | color: #666; |
| | | } |
| | | |
| | | .right-toolbar { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | margin-bottom: 10px; |
| | | } |
| | | .right-toolbar { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | gap: 10px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .link-text { |
| | | color: #409eff; |
| | | cursor: default; |
| | | } |
| | | .link-text { |
| | | color: #409eff; |
| | | cursor: default; |
| | | } |
| | | |
| | | .clickable-link { |
| | | color: #409eff; |
| | | cursor: pointer; |
| | | } |
| | | .clickable-link { |
| | | color: #409eff; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .clickable-link:hover { |
| | | text-decoration: underline; |
| | | } |
| | | .clickable-link:hover { |
| | | text-decoration: underline; |
| | | } |
| | | |
| | | :deep(.row-center td) { |
| | | text-align: center !important; |
| | | } |
| | | :deep(.row-center td) { |
| | | text-align: center !important; |
| | | } |
| | | |
| | | /* el-table 表头/内容统一居中(row-class-name 不作用于表头) */ |
| | | :deep(.center-table .el-table__header-wrapper th .cell) { |
| | | text-align: center !important; |
| | | } |
| | | :deep(.center-table .el-table__body-wrapper td .cell) { |
| | | text-align: center !important; |
| | | } |
| | | /* el-table 表头/内容统一居中(row-class-name 不作用于表头) */ |
| | | :deep(.center-table .el-table__header-wrapper th .cell) { |
| | | text-align: center !important; |
| | | } |
| | | :deep(.center-table .el-table__body-wrapper td .cell) { |
| | | text-align: center !important; |
| | | } |
| | | |
| | | /* PIMTable 表头居中 */ |
| | | :deep(.lims-table .pim-table-header-cell) { |
| | | text-align: center; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | /* PIMTable 表头居中 */ |
| | | :deep(.lims-table .pim-table-header-cell) { |
| | | text-align: center; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | :deep(.lims-table .pim-table-header-title) { |
| | | text-align: center; |
| | | width: 100%; |
| | | } |
| | | :deep(.lims-table .pim-table-header-title) { |
| | | text-align: center; |
| | | width: 100%; |
| | | } |
| | | |
| | | :deep(.lims-table .pim-table-header-extra) { |
| | | width: 100%; |
| | | margin-top: 4px; |
| | | } |
| | | :deep(.lims-table .pim-table-header-extra) { |
| | | width: 100%; |
| | | margin-top: 4px; |
| | | } |
| | | </style> |