gaoluyang
8 天以前 fe167dd71a1300aeae07522db990d6b3fdb77a0e
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>