From fe167dd71a1300aeae07522db990d6b3fdb77a0e Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 12 三月 2026 13:26:02 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_New' into dev_中兴实强

---
 src/views/customerService/feedbackRegistration/components/ProductSelectDialog.vue |  275 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 275 insertions(+), 0 deletions(-)

diff --git a/src/views/customerService/feedbackRegistration/components/ProductSelectDialog.vue b/src/views/customerService/feedbackRegistration/components/ProductSelectDialog.vue
new file mode 100644
index 0000000..37e21ef
--- /dev/null
+++ b/src/views/customerService/feedbackRegistration/components/ProductSelectDialog.vue
@@ -0,0 +1,275 @@
+<template>
+  <el-dialog v-model="visible" title="閫夋嫨浜у搧" width="900px" destroy-on-close :close-on-click-modal="false">
+    <el-form :inline="true" :model="query" class="mb-2">
+      <el-form-item label="浜у搧鍒嗙被">
+        <el-input v-model="query.productCategory" placeholder="杈撳叆浜у搧鍒嗙被" clearable @keyup.enter="onSearch" />
+      </el-form-item>
+
+      <el-form-item label="鍩烘湰鍗曚綅">
+        <el-input v-model="query.unit" placeholder="杈撳叆鍩烘湰鍗曚綅" clearable @keyup.enter="onSearch" />
+      </el-form-item>
+
+      <el-form-item>
+        <el-button type="primary" @click="onSearch">鎼滅储</el-button>
+        <el-button @click="columnsDialogVisible = true">鍒楄〃瀛楁</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 鍒楄〃 -->
+    <el-table ref="tableRef" v-loading="loading" :data="tableData" height="420" highlight-current-row :row-key="getRowKey"
+      @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" />
+      <el-table-column type="index" label="搴忓彿" width="60" />
+      <template v-for="column in visibleColumns" :key="column.prop">
+        <el-table-column :prop="column.prop" :label="column.label" :min-width="column.minWidth" show-overflow-tooltip align="center" />
+      </template>
+    </el-table>
+
+    <div class="mt-3" style="margin-top: 10px;display: flex; justify-content: flex-end;">
+
+
+      <el-pagination background layout="total, sizes, prev, pager, next, jumper" :total="total"
+        v-model:page-size="page.pageSize" v-model:current-page="page.pageNum" :page-sizes="[10, 20, 50, 100]"
+        @size-change="onPageChange" @current-change="onPageChange" />
+    </div>
+
+    <template #footer>
+      <el-button type="primary" :disabled="multipleSelection.length === 0" @click="onConfirm">
+        纭畾
+      </el-button>
+      <el-button @click="close()">鍙栨秷</el-button>
+    </template>
+  </el-dialog>
+
+  <el-dialog v-model="columnsDialogVisible" title="鑷畾涔夋樉绀哄垪椤�" width="600px">
+    <el-checkbox-group v-model="selectedColumns">
+      <el-checkbox v-for="column in allColumns" :key="column.prop" :label="column.prop" :disabled="column.disabled">
+        {{ column.label }}
+      </el-checkbox>
+    </el-checkbox-group>
+    <template #footer>
+      <el-button @click="resetColumns">鎭㈠榛樿</el-button>
+      <el-button type="primary" @click="saveColumns">淇濆瓨</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { computed, onMounted, reactive, ref, watch, nextTick } from "vue";
+import { ElMessage } from "element-plus";
+
+const props = defineProps({
+  modelValue: Boolean,
+  single: Boolean, // 鏄惁鍙兘閫夋嫨涓�涓紝榛樿false锛堝彲閫夋嫨澶氫釜锛�
+  products: {
+    type: Array,
+    default: () => []
+  },
+  selectedIds: {
+    type: Array,
+    default: () => []
+  }
+});
+
+const emit = defineEmits(['update:modelValue', 'confirm']);
+
+const visible = computed({
+  get: () => props.modelValue,
+  set: (v) => emit("update:modelValue", v),
+});
+
+const query = reactive({
+  productCategory: "",
+  unit: "",
+});
+
+const page = reactive({
+  pageNum: 1,
+  pageSize: 10,
+});
+
+const loading = ref(false);
+const tableData = ref([]);
+const total = ref(0);
+const multipleSelection = ref([]);
+const selectedRowMap = ref(new Map());
+const tableRef = ref();
+
+const columnsDialogVisible = ref(false);
+
+const allColumns = ref([
+    { prop: 'productCategory', label: '浜у搧鍒嗙被', selected: true, disabled: false },
+    { prop: 'unit', label: '鍩烘湰鍗曚綅', selected: true, disabled: false },
+]);
+
+const selectedColumns = ref(allColumns.value.filter(c => c.selected).map(c => c.prop));
+
+const visibleColumns = computed(() => {
+  return allColumns.value.filter(c => selectedColumns.value.includes(c.prop));
+});
+
+const getRowKey = (row) => {
+  return row?.id ?? row?.productModelId ?? `${row?.productCategory || ""}-${row?.specificationModel || row?.model || ""}-${row?.unit || ""}`;
+};
+
+const syncMultipleSelection = () => {
+  multipleSelection.value = Array.from(selectedRowMap.value.values());
+};
+
+const initSelectionFromProps = () => {
+  const selectedIdSet = new Set((props.selectedIds || []).map((id) => String(id)));
+  selectedRowMap.value = new Map();
+  if (!selectedIdSet.size) {
+    syncMultipleSelection();
+    return;
+  }
+  (props.products || []).forEach((row) => {
+    if (selectedIdSet.has(String(row?.id))) {
+      selectedRowMap.value.set(getRowKey(row), row);
+    }
+  });
+  syncMultipleSelection();
+};
+
+const resetColumns = () => {
+  selectedColumns.value = allColumns.value.filter(c => c.selected).map(c => c.prop);
+};
+
+const saveColumns = () => {
+  if (selectedColumns.value.length < 1) {
+    ElMessage.warning("鍒楄〃椤规樉绀轰笉寰楀皯浜�1椤�");
+    return;
+  }
+  columnsDialogVisible.value = false;
+};
+
+function close() {
+  visible.value = false;
+}
+
+const handleSelectionChange = (val) => {
+  const currentPageKeys = new Set(tableData.value.map((item) => getRowKey(item)));
+  currentPageKeys.forEach((key) => selectedRowMap.value.delete(key));
+
+  if (props.single && val.length > 1) {
+    const lastSelected = val[val.length - 1];
+    selectedRowMap.value = new Map();
+    if (lastSelected) {
+      selectedRowMap.value.set(getRowKey(lastSelected), lastSelected);
+    }
+    syncMultipleSelection();
+    nextTick(() => {
+      if (tableRef.value) {
+        tableRef.value.clearSelection();
+        tableRef.value.toggleRowSelection(lastSelected, true);
+      }
+    });
+  } else if (props.single) {
+    selectedRowMap.value = new Map();
+    if (val[0]) {
+      selectedRowMap.value.set(getRowKey(val[0]), val[0]);
+    }
+    syncMultipleSelection();
+  } else {
+    val.forEach((row) => {
+      selectedRowMap.value.set(getRowKey(row), row);
+    });
+    syncMultipleSelection();
+  }
+}
+
+function onSearch() {
+  page.pageNum = 1;
+  loadData();
+}
+
+function onReset() {
+  query.productCategory = "";
+  query.unit = "";
+  page.pageNum = 1;
+  loadData();
+}
+
+function onPageChange() {
+  loadData();
+}
+
+function onConfirm() {
+  if (multipleSelection.value.length === 0) {
+    ElMessage.warning("璇烽�夋嫨涓�鏉′骇鍝�");
+    return;
+  }
+  if (props.single && multipleSelection.value.length > 1) {
+    ElMessage.warning("鍙兘閫夋嫨涓�涓骇鍝�");
+    return;
+  }
+  emit("confirm", props.single ? [multipleSelection.value[0]] : multipleSelection.value);
+  close();
+}
+
+async function loadData() {
+  loading.value = true;
+  try {
+    let filtered = props.products || [];
+    if (query.productCategory) {
+      filtered = filtered.filter(item => item.productCategory && item.productCategory.includes(query.productCategory));
+    }
+    if (query.unit) {
+      filtered = filtered.filter(item => item.unit && item.unit.includes(query.unit));
+    }
+
+    total.value = filtered.length;
+    const start = (page.pageNum - 1) * page.pageSize;
+    const end = start + page.pageSize;
+    tableData.value = filtered.slice(start, end);
+
+    nextTick(() => {
+      if (tableRef.value) {
+        tableRef.value.clearSelection();
+        tableData.value.forEach(row => {
+          if (selectedRowMap.value.has(getRowKey(row))) {
+            tableRef.value.toggleRowSelection(row, true);
+          }
+        });
+      }
+      syncMultipleSelection();
+    });
+  } finally {
+    loading.value = false;
+  }
+}
+
+// 鐩戝惉寮圭獥鎵撳紑锛岄噸缃�夋嫨
+watch(() => props.modelValue, (visible) => {
+  if (visible) {
+    initSelectionFromProps();
+    page.pageNum = 1;
+    loadData();
+  }
+});
+
+watch(() => props.products, () => {
+  const latestMap = new Map();
+  const currentKeys = new Set(selectedRowMap.value.keys());
+  (props.products || []).forEach((row) => {
+    const key = getRowKey(row);
+    if (currentKeys.has(key)) {
+      latestMap.set(key, row);
+    }
+  });
+  selectedRowMap.value.forEach((row, key) => {
+    if (!latestMap.has(key)) {
+      latestMap.set(key, row);
+    }
+  });
+  selectedRowMap.value = latestMap;
+  syncMultipleSelection();
+  if (props.modelValue) {
+    loadData();
+  }
+}, { deep: true });
+
+onMounted(() => {
+  loadData()
+})
+</script>

--
Gitblit v1.9.3