From 767fe69bf69ceaeefa5458d231fd7f727cb40f2c Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 14 一月 2026 17:50:29 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_New' into dev_New

---
 src/views/qualityManagement/metricBinding/index.vue |  432 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 432 insertions(+), 0 deletions(-)

diff --git a/src/views/qualityManagement/metricBinding/index.vue b/src/views/qualityManagement/metricBinding/index.vue
new file mode 100644
index 0000000..80eb66f
--- /dev/null
+++ b/src/views/qualityManagement/metricBinding/index.vue
@@ -0,0 +1,432 @@
+<template>
+  <div class="app-container metric-binding">
+    <!-- 宸︿晶锛氭娴嬫爣鍑嗗垪琛紙鍙锛� -->
+    <div class="left-panel">
+      <PIMTable
+        rowKey="id"
+        :column="standardColumns"
+        :tableData="standardTableData"
+        :page="page"
+        :isSelection="false"
+        :rowClassName="rowClassNameCenter"
+        :tableLoading="tableLoading"
+        @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>
+
+    <!-- 鍙充晶锛氱粦瀹氬垪琛� -->
+    <div class="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>
+
+    <!-- 娣诲姞缁戝畾寮规 -->
+    <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-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="closeBindingDialog">鍙栨秷</el-button>
+          <el-button type="primary" @click="submitBinding">纭畾</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </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 {
+  qualityTestStandardBindingList,
+  qualityTestStandardBindingAdd,
+  qualityTestStandardBindingDel
+} from '@/api/qualityManagement/qualityTestStandardBinding.js'
+
+const { proxy } = getCurrentInstance()
+
+// 宸︿晶鏍囧噯鍒楄〃锛氭暣琛屽唴瀹瑰眳涓紙閰嶅悎鏍峰紡锛�
+const rowClassNameCenter = () => 'row-center'
+
+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 standardColumns = ref([
+  { label: '鏍囧噯缂栧彿', prop: 'standardNo', dataType: 'slot', slot: 'standardNoCell', minWidth: 160, headerSlot: 'standardNoHeader' },
+  { label: '鏍囧噯鍚嶇О', prop: 'standardName', minWidth: 180, headerSlot: 'standardNameHeader' },
+  {
+    label: '妫�娴嬬被鍨�',
+    prop: 'inspectType',
+    headerSlot: 'inspectTypeHeader',
+    dataType: 'tag',
+    formatData: (val) => {
+      const map = { 0: '鍘熸潗鏂欐楠�', 1: '杩囩▼妫�楠�', 2: '鍑哄巶妫�楠�' }
+      return map[val] || val
+    }
+  },
+  // {
+  //   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 bindingTableData = ref([])
+const bindingLoading = ref(false)
+const bindingSelectedRows = ref([])
+const bindingDialogVisible = ref(false)
+
+// 浜у搧鏍戯紙鐢ㄤ簬缁戝畾閫夋嫨锛�
+const productOptions = ref([])
+const selectedProductIds = ref([])
+
+const getProductOptions = async () => {
+  // 閬垮厤閲嶅璇锋眰
+  if (productOptions.value?.length) return
+  const res = await productTreeList()
+  productOptions.value = convertIdToValue(Array.isArray(res) ? res : [])
+}
+
+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 handleQuery = () => {
+  page.current = 1
+  getStandardList()
+}
+
+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
+  })
+    .then((res) => {
+      const records = res?.data?.records || []
+      standardTableData.value = records
+      page.total = res?.data?.total || records.length
+    })
+    .finally(() => {
+      tableLoading.value = false
+    })
+}
+
+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) => {
+      bindingTableData.value = res?.data || []
+    })
+    .finally(() => {
+      bindingLoading.value = false
+    })
+}
+
+const handleBindingSelectionChange = (selection) => {
+  bindingSelectedRows.value = selection
+}
+
+const openBindingDialog = () => {
+  if (!currentStandard.value?.id) return
+  selectedProductIds.value = []
+  getProductOptions()
+  bindingDialogVisible.value = true
+}
+
+const closeBindingDialog = () => {
+  bindingDialogVisible.value = false
+}
+
+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 handleUnbind = async (row) => {
+  if (!row?.id) return
+  try {
+    await ElMessageBox.confirm('纭鍒犻櫎璇ョ粦瀹氾紵', '鎻愮ず', { type: 'warning' })
+  } catch {
+    return
+  }
+  await qualityTestStandardBindingDel([row.qualityTestStandardBindingId])
+  proxy.$message.success('鍒犻櫎鎴愬姛')
+  loadBindingList()
+}
+
+const handleBatchUnbind = async () => {
+  if (!bindingSelectedRows.value.length) {
+    proxy.$message.warning('璇烽�夋嫨鏁版嵁')
+    return
+  }
+  const ids = bindingSelectedRows.value.map((i) => i.qualityTestStandardBindingId)
+  try {
+    await ElMessageBox.confirm('閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�', '鍒犻櫎鎻愮ず', { type: 'warning' })
+  } catch {
+    return
+  }
+  await qualityTestStandardBindingDel(ids)
+  proxy.$message.success('鍒犻櫎鎴愬姛')
+  loadBindingList()
+}
+
+onMounted(() => {
+  getStandardList()
+})
+</script>
+
+<style scoped>
+.metric-binding {
+  display: flex;
+  gap: 16px;
+}
+
+.left-panel,
+.right-panel {
+  flex: 1;
+  background: #ffffff;
+  padding: 16px;
+  box-sizing: border-box;
+}
+
+.toolbar {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 12px;
+}
+
+.toolbar-right {
+  flex-shrink: 0;
+}
+
+.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 .desc {
+  font-size: 13px;
+  color: #666;
+}
+
+.right-toolbar {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+  margin-bottom: 10px;
+}
+
+.link-text {
+  color: #409eff;
+  cursor: default;
+}
+
+.clickable-link {
+  color: #409eff;
+  cursor: pointer;
+}
+
+.clickable-link:hover {
+  text-decoration: underline;
+}
+
+:deep(.row-center td) {
+  text-align: center !important;
+}
+
+/* el-table 琛ㄥご/鍐呭缁熶竴灞呬腑锛坮ow-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;
+}
+</style>

--
Gitblit v1.9.3