| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |