314 lines
7.5 KiB
Vue
314 lines
7.5 KiB
Vue
<template>
|
||
<el-dialog
|
||
v-model="visible"
|
||
:title="dialogTitle"
|
||
width="800px"
|
||
:close-on-click-modal="false"
|
||
@close="handleClose"
|
||
>
|
||
<!-- 搜索栏 -->
|
||
<div class="search-bar">
|
||
<el-input
|
||
v-model="searchKeyword"
|
||
placeholder="请输入关键词搜索"
|
||
clearable
|
||
style="width: 300px"
|
||
@keyup.enter="handleSearch"
|
||
>
|
||
<template #append>
|
||
<el-button @click="handleSearch">
|
||
<el-icon><ele-Search /></el-icon>
|
||
</el-button>
|
||
</template>
|
||
</el-input>
|
||
</div>
|
||
|
||
<!-- 数据表格 -->
|
||
<el-table
|
||
ref="tableRef"
|
||
:data="tableData"
|
||
v-loading="loading"
|
||
border
|
||
style="width: 100%; margin-top: 15px"
|
||
@selection-change="handleSelectionChange"
|
||
>
|
||
<el-table-column type="selection" width="55" align="center" />
|
||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||
<el-table-column prop="name" :label="getColumnLabel()" min-width="200" show-overflow-tooltip />
|
||
<el-table-column prop="code" label="编码" width="150" show-overflow-tooltip v-if="scope <= 2" />
|
||
</el-table>
|
||
|
||
<!-- 分页 -->
|
||
<div class="pagination-box">
|
||
<el-pagination
|
||
v-model:current-page="pageNum"
|
||
v-model:page-size="pageSize"
|
||
:page-sizes="[10, 20, 50, 100]"
|
||
:total="total"
|
||
layout="total, sizes, prev, pager, next, jumper"
|
||
@size-change="handleSizeChange"
|
||
@current-change="handleCurrentChange"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 已选项展示 -->
|
||
<div class="selected-box" v-if="selectedItems.length > 0">
|
||
<span class="selected-label">已选择 {{ selectedItems.length }} 项:</span>
|
||
<el-tag
|
||
v-for="item in selectedItems"
|
||
:key="item.id"
|
||
size="small"
|
||
closable
|
||
style="margin-right: 4px; margin-bottom: 4px"
|
||
@close="removeSelected(item)"
|
||
>
|
||
{{ item.name }}
|
||
</el-tag>
|
||
</div>
|
||
|
||
<template #footer>
|
||
<el-button @click="handleClose">取消</el-button>
|
||
<el-button type="primary" @click="handleConfirm">确定</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed } from 'vue';
|
||
import { listWarehouses } from '/@/api/assets/warehouse';
|
||
import { listZones } from '/@/api/assets/zone';
|
||
import { listLocations } from '/@/api/assets/location';
|
||
|
||
const emit = defineEmits(['confirm']);
|
||
|
||
const visible = ref(false);
|
||
const loading = ref(false);
|
||
const scope = ref(1); // 1-仓库 2-库区 3-库位
|
||
const searchKeyword = ref('');
|
||
const tableData = ref<any[]>([]);
|
||
const total = ref(0);
|
||
const pageNum = ref(1);
|
||
const pageSize = ref(10);
|
||
const tableRef = ref();
|
||
|
||
// 已选项
|
||
const selectedItems = ref<any[]>([]);
|
||
|
||
// 父级ID(库区需要仓库ID,库位需要库区ID)
|
||
const parentWarehouseIds = ref<string[]>([]);
|
||
const parentZoneIds = ref<string[]>([]);
|
||
|
||
// 弹窗标题
|
||
const dialogTitle = computed(() => {
|
||
const titles: Record<number, string> = {
|
||
1: '选择仓库',
|
||
2: '选择库区',
|
||
3: '选择库位',
|
||
};
|
||
return titles[scope.value] || '选择';
|
||
});
|
||
|
||
// 获取列标题
|
||
const getColumnLabel = () => {
|
||
const labels: Record<number, string> = {
|
||
1: '仓库名称',
|
||
2: '库区名称',
|
||
3: '库位名称',
|
||
};
|
||
return labels[scope.value] || '名称';
|
||
};
|
||
|
||
// 打开弹窗
|
||
const open = async (data: {
|
||
scope: number;
|
||
selectedIds: string[];
|
||
selectedNames: string[];
|
||
warehouseIds?: string[];
|
||
zoneIds?: string[];
|
||
}) => {
|
||
scope.value = data.scope;
|
||
parentWarehouseIds.value = data.warehouseIds || [];
|
||
parentZoneIds.value = data.zoneIds || [];
|
||
|
||
// 恢复已选项
|
||
selectedItems.value = data.selectedIds.map((id, idx) => ({
|
||
id,
|
||
name: data.selectedNames[idx] || id,
|
||
}));
|
||
|
||
searchKeyword.value = '';
|
||
pageNum.value = 1;
|
||
visible.value = true;
|
||
|
||
await loadData();
|
||
};
|
||
|
||
// 加载数据
|
||
const loadData = async () => {
|
||
loading.value = true;
|
||
try {
|
||
let res: any;
|
||
const params = {
|
||
keyword: searchKeyword.value,
|
||
pageNum: pageNum.value,
|
||
pageSize: pageSize.value,
|
||
};
|
||
|
||
if (scope.value === 1) {
|
||
// 加载仓库
|
||
res = await listWarehouses(params);
|
||
const list = res.data?.list || [];
|
||
tableData.value = list.map((item: any) => ({
|
||
id: item.id,
|
||
name: item.warehouseName || item.name,
|
||
code: item.warehouseCode || item.code,
|
||
}));
|
||
total.value = res.data?.total || 0;
|
||
} else if (scope.value === 2) {
|
||
// 加载库区
|
||
if (parentWarehouseIds.value.length === 0) {
|
||
tableData.value = [];
|
||
total.value = 0;
|
||
return;
|
||
}
|
||
const allZones: any[] = [];
|
||
let totalCount = 0;
|
||
for (const warehouseId of parentWarehouseIds.value) {
|
||
res = await listZones({ ...params, warehouseId });
|
||
const list = res.data?.list || res.data?.items || res.data || [];
|
||
const zones = (Array.isArray(list) ? list : []).map((item: any) => ({
|
||
id: item.id || item._id,
|
||
name: item.zoneName || item.name,
|
||
code: item.zoneCode || item.code,
|
||
}));
|
||
allZones.push(...zones);
|
||
totalCount += res.data?.total || zones.length;
|
||
}
|
||
tableData.value = allZones;
|
||
total.value = totalCount;
|
||
} else if (scope.value === 3) {
|
||
// 加载库位
|
||
if (parentZoneIds.value.length === 0) {
|
||
tableData.value = [];
|
||
total.value = 0;
|
||
return;
|
||
}
|
||
const allLocations: any[] = [];
|
||
let totalCount = 0;
|
||
for (const zoneId of parentZoneIds.value) {
|
||
res = await listLocations({ ...params, zoneId });
|
||
const list = res.data?.list || res.data?.items || res.data || [];
|
||
const locations = (Array.isArray(list) ? list : []).map((item: any) => ({
|
||
id: item.id || item._id,
|
||
name: item.locationName || item.name,
|
||
code: item.locationCode || item.code,
|
||
}));
|
||
allLocations.push(...locations);
|
||
totalCount += res.data?.total || locations.length;
|
||
}
|
||
tableData.value = allLocations;
|
||
total.value = totalCount;
|
||
}
|
||
|
||
// 恢复选中状态
|
||
setTimeout(() => {
|
||
tableData.value.forEach((row: any) => {
|
||
if (selectedItems.value.some(item => item.id === row.id)) {
|
||
tableRef.value?.toggleRowSelection(row, true);
|
||
}
|
||
});
|
||
}, 0);
|
||
} catch (error) {
|
||
console.error('加载数据失败:', error);
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
// 搜索
|
||
const handleSearch = () => {
|
||
pageNum.value = 1;
|
||
loadData();
|
||
};
|
||
|
||
// 分页大小变化
|
||
const handleSizeChange = () => {
|
||
pageNum.value = 1;
|
||
loadData();
|
||
};
|
||
|
||
// 页码变化
|
||
const handleCurrentChange = () => {
|
||
loadData();
|
||
};
|
||
|
||
// 选择变化
|
||
const handleSelectionChange = (selection: any[]) => {
|
||
// 合并当前页选中和之前选中的(去除当前页取消选中的)
|
||
const currentPageIds = tableData.value.map(item => item.id);
|
||
|
||
// 移除当前页的旧选中
|
||
const otherSelected = selectedItems.value.filter(item => !currentPageIds.includes(item.id));
|
||
|
||
// 添加当前页新选中
|
||
selectedItems.value = [...otherSelected, ...selection];
|
||
};
|
||
|
||
// 移除已选
|
||
const removeSelected = (item: any) => {
|
||
const idx = selectedItems.value.findIndex(i => i.id === item.id);
|
||
if (idx > -1) {
|
||
selectedItems.value.splice(idx, 1);
|
||
// 取消表格选中
|
||
const row = tableData.value.find(r => r.id === item.id);
|
||
if (row) {
|
||
tableRef.value?.toggleRowSelection(row, false);
|
||
}
|
||
}
|
||
};
|
||
|
||
// 关闭弹窗
|
||
const handleClose = () => {
|
||
visible.value = false;
|
||
};
|
||
|
||
// 确认选择
|
||
const handleConfirm = () => {
|
||
emit('confirm', {
|
||
selectedIds: selectedItems.value.map(item => item.id),
|
||
selectedNames: selectedItems.value.map(item => item.name),
|
||
});
|
||
handleClose();
|
||
};
|
||
|
||
defineExpose({
|
||
open,
|
||
});
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.search-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.pagination-box {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
margin-top: 15px;
|
||
}
|
||
|
||
.selected-box {
|
||
margin-top: 15px;
|
||
padding: 10px;
|
||
background: #f5f7fa;
|
||
border-radius: 4px;
|
||
|
||
.selected-label {
|
||
font-size: 14px;
|
||
color: #606266;
|
||
margin-right: 8px;
|
||
}
|
||
}
|
||
</style>
|