<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>
|