| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- <template>
- <van-popup v-model:show="visible" position="bottom" style="height: 60%">
- <div style="display: flex; flex-direction: column; height: 100%;">
- <div
- style="display: flex; justify-content: space-between; align-items: center; padding: 10px 16px; border-bottom: 1px solid #ebedf0;"
- >
- <div style="font-size: 16px; font-weight: 500;">{{ title }}</div>
- <van-icon name="cross" @click="close" />
- </div>
- <van-search v-model="searchKey" :placeholder="searchPlaceholder" @search="onSearch" />
- <div style="flex: 1; overflow-y: auto;">
- <van-radio-group v-model="selectedId">
- <van-cell-group>
- <van-cell v-for="item in columns" :key="item.value" clickable @click="selectItem(item)">
- <template #title>
- <van-radio :name="item.value">{{ item.text }}</van-radio>
- </template>
- </van-cell>
- </van-cell-group>
- </van-radio-group>
- <div v-if="loading" style="text-align: center; color: #969799; padding: 10px 0;">加载中...</div>
- <van-empty v-if="columns.length === 0 && !loading" description="暂无数据" />
- </div>
- <div
- style="padding: 10px 16px; border-top: 1px solid #ebedf0; display: flex; flex-direction: column; gap: 8px;"
- >
- <van-button type="default" block :disabled="noMore || loading" @click="loadMore">
- {{ noMore ? '没有更多数据' : '加载更多' }}
- </van-button>
- <van-button type="primary" block @click="confirm">确认</van-button>
- </div>
- </div>
- </van-popup>
- </template>
- <script setup>
- import { showFailToast } from 'vant';
- import { ajaxApiGet } from '../common/utils';
- import { processException } from '../common/Common.js';
- import { ref, watch, computed, onMounted, defineProps, defineEmits, defineExpose } from 'vue';
- const props = defineProps({
- show: {
- type: Boolean,
- default: false,
- },
- positionType: {
- type: String,
- default: 'transfer', // 'transfer' 或 'idle'
- validator: value => ['transfer', 'idle'].includes(value),
- },
- isUser: {
- type: String,
- default: 'stockIn',
- },
- defaultSelectedId: {
- type: [String, Number],
- default: '',
- },
- pageSize: {
- type: Number,
- default: 20,
- },
- isCanSelect: {
- type: Boolean,
- default: true,
- },
- });
- const emit = defineEmits([
- 'update:show',
- 'confirm',
- 'close',
- ]);
- // 内部状态
- const visible = ref(props.show);
- const searchKey = ref('');
- const columns = ref([]);
- const currentPage = ref(1);
- const loading = ref(false);
- const noMore = ref(false);
- const selectedId = ref(props.defaultSelectedId);
- const selectedItem = ref(null);
- // 计算属性
- const title = computed(() =>{
- const titleMap = {
- stockIn: {
- transfer: '选择中转区货位',
- idle: '选择入库货位',
- },
- stockOut: {
- transfer: '选择中转区货位',
- using: '选择出库货位',
- },
- };
- return titleMap[props.isUser]?.[props.positionType] || '选择货位';
- });
- const searchPlaceholder = computed(() => `请输入${title.value}关键词`);
- const apiUrl = computed(() => {
- const apiMap = {
- stockIn: {
- transfer: '/api/positionResource/getTransferPosition',
- idle: '/api/positionResource/getIdlePosition',
- },
- stockOut: {
- transfer: '/api/positionResource/getTransferPosition',
- using: '/api/positionResource/getOccupiedPosition',
- },
- };
- console.log(props.isUser, props.positionType);
- return apiMap[props.isUser]?.[props.positionType] || '';
- });
- // 监听props变化
- watch(() => props.show, newVal => {
- visible.value = newVal;
- if (newVal && columns.value.length === 0) {
- getPositionData();
- }
- });
- // 同步内部visible变化到外部
- watch(() => visible.value, newVal => {
- emit('update:show', newVal);
- });
- // 同步默认选中ID
- watch(() => props.defaultSelectedId, newVal => {
- selectedId.value = newVal;
- });
- // 搜索
- const onSearch = () => {
- getPositionData(true);
- };
- // 加载更多
- const loadMore = () => {
- if (loading.value || noMore.value) return;
- currentPage.value++;
- getPositionData(false);
- };
- // 获取货位数据
- const getPositionData = (isReset = true) => {
- if (isReset) {
- currentPage.value = 1;
- columns.value = [];
- noMore.value = false;
- }
- loading.value = true;
- const start = (currentPage.value - 1) * props.pageSize;
- const length = props.pageSize;
- const filter = searchKey.value;
- ajaxApiGet(`${apiUrl.value}?start=${start}&length=${length}&filter=${filter}`).then(
- success => {
- loading.value = false;
- const { errorCode, errorMessage, datas, total } = success;
- if (errorCode === 0) {
- if (datas && datas.length) {
- const newColumns = datas.map(item => ({
- text: item.name,
- value: item.id,
- id: item.id,
- name: item.name,
- no: item.no,
- }));
- columns.value = [...columns.value, ...newColumns];
- noMore.value = columns.value.length >= total;
- } else {
- noMore.value = true;
- }
- } else {
- noMore.value = true;
- showFailToast(errorMessage);
- }
- },
- error => {
- loading.value = false;
- processException(error);
- },
- );
- };
- // 选择项
- const selectItem = item => {
- selectedId.value = item.value;
- selectedItem.value = item;
- };
- // 确认选择
- const confirm = () => {
- if (selectedId.value) {
- const selected = columns.value.find(item => item.value === selectedId.value);
- if (selected) {
- emit('confirm', selected);
- }
- }
- close();
- };
- // 关闭弹窗
- const close = () => {
- visible.value = false;
- emit('close');
- };
- // 清除选中
- const clearSelected = () => {
- selectedId.value = '';
- selectedItem.value = null;
- };
- // 组件挂载时,如果show为true则加载数据
- onMounted(() => {
- if (props.show) {
- getPositionData();
- }
- });
- defineExpose({
- clearSelected,
- });
- </script>
- <style scoped>
- /* 使用深度选择器确保 van-search 样式不受影响 */
- :deep(.van-search) {
- padding: 8px 12px;
- }
- :deep(.van-cell) {
- align-items: center;
- }
- :deep(.van-radio) {
- margin-right: 0;
- }
- :deep(.van-button) {
- margin-bottom: 8px;
- }
- </style>
|