瀏覽代碼

add 库位管理 & 库位订单

haveyou 1 年之前
父節點
當前提交
b29782de97

+ 94 - 0
src/views/recycleService/location/components/location-edit.vue

@@ -0,0 +1,94 @@
+<!-- 搜索表单 -->
+<template>
+  <simple-form-modal
+    :title="title"
+    :items="formItems"
+    ref="editRef"
+    :baseUrl="baseUrl"
+    @success="(data) => emit('success', data)"
+  ></simple-form-modal>
+</template>
+
+<script setup>
+  import { reactive, ref, defineEmits, getCurrentInstance } from 'vue';
+  import { useFormData } from '@/utils/use-form-data';
+  import SimpleFormModal from '@/components/CommonPage/SimpleFormModal.vue';
+  const { proxy } = getCurrentInstance();
+
+  //获取仓库
+  const godownList = ref([]);
+  const title = ref('新增库位');
+  const emit = defineEmits(['success']);
+
+  const formItems = computed(() => {
+    return [
+      { type: 'input', label: '库位编号', prop: 'code', required: true },
+      {
+        type: 'select',
+        label: '仓库名称',
+        prop: 'godownId',
+        required: true,
+        options: godownList.value.map((d) => {
+          return { label: d.name, value: d.id };
+        }),
+        props: {
+          placeholder: '请选择或输入搜索',
+          filterable: true,
+          remote: true,
+          remoteMethod: (query) => {
+            if (!query) {
+              return [];
+            }
+            return getStoreList(query).then((res) => {
+              if (res.data.code == 200) {
+                return res.data.data.map((v) => ({
+                  label: v.name,
+                  value: v.id
+                }));
+              } else {
+                return [];
+              }
+            });
+          }
+        }
+      },
+      {
+        type: 'dictRadio',
+        label: '状态',
+        prop: 'useStatus',
+        props: { code: 'use_status' },
+        required: true
+      },
+      { type: 'textarea', label: '备注', prop: 'remark' }
+    ];
+  });
+  //默认值
+  const baseUrl = reactive({
+    add: '/baseinfo/godownposition/add',
+    update: '/baseinfo/godownposition/edit'
+  });
+  const formData = ref({ useStatus: '1' });
+
+  const editRef = ref(null);
+
+  function handleOpen(data) {
+    if (data) {
+      title.value = '编辑库位';
+      data.useStatus = data.useStatus + '';
+    } else {
+      title.value = '新增库位';
+    }
+    let name = data?.godownName || '';
+    formData.value = Object.assign(formData.value, data || {});
+    getStoreList(name).then((res) => {
+      godownList.value = res.data.data;
+    });
+    editRef.value?.handleOpen(formData.value);
+  }
+
+  function getStoreList(name = '') {
+    return proxy.$http.post(`/baseinfo/godown/searchGodown?name=${name}`);
+  }
+
+  defineExpose({ handleOpen });
+</script>

+ 125 - 0
src/views/recycleService/location/components/location-import.vue

@@ -0,0 +1,125 @@
+<template>
+  <ele-modal
+    :width="460"
+    title="库位导入"
+    :body-style="{ paddingTop: '8px' }"
+    v-model="visible"
+  >
+    <div class="flex mb-2">
+      <div class="text-sm">请先下载导入模板:</div>
+      <el-button
+        @click="
+          downloadOssLink(
+            'https://shuhi.oss-cn-qingdao.aliyuncs.com/default/book_info_export_template.xlsx',
+            '库位导入模板'
+          )
+        "
+        link
+        type="primary"
+        >下载库位导入模板</el-button
+      >
+    </div>
+
+    <div class="flex flex-col mb-2">
+      <div class="text-sm text-yellow-500">库位导入规则:</div>
+      <div class="text-sm">1.文件格式必须与模板完全一致!</div>
+      <div class="text-sm">2.同一库位编码的多次导入,会覆盖之前的信息。</div>
+    </div>
+    <div v-loading="loading" class="user-import-upload">
+      <el-upload
+        v-model:file-list="fileList"
+        ref="uploadRef"
+        action=""
+        accept=".xls,.xlsx"
+        :before-upload="doUpload"
+        :auto-upload="false"
+        :limit="1"
+        :on-exceed="handleExceed"
+      >
+        <el-button type="primary" class="mr-2">选择文件</el-button>
+        <ele-text type="placeholder">只能上传 xls、xlsx 文件</ele-text>
+      </el-upload>
+    </div>
+
+    <template #footer>
+      <el-button @click="visible = false">关闭</el-button>
+      <el-button type="primary" @click="handleSumbit" :loading="loading"
+        >导入</el-button
+      >
+    </template>
+  </ele-modal>
+</template>
+
+<script setup>
+  import { ref, h } from 'vue';
+  import { genFileId } from 'element-plus';
+  import { ElMessageBox } from 'element-plus/es';
+  import { EleMessage } from 'ele-admin-plus/es';
+  import { CloudUploadOutlined } from '@/components/icons';
+  import { downloadOssLink } from '@/utils/common';
+  import request from '@/utils/request';
+
+  const emit = defineEmits(['done']);
+
+  /** 弹窗是否打开 */
+  const visible = defineModel({ type: Boolean });
+
+  /** 导入请求状态 */
+  const loading = ref(false);
+
+  const uploadRef = ref(null);
+  const fileList = ref([]);
+
+  function handleExceed(files) {
+    uploadRef.value?.clearFiles();
+    const file = files[0];
+    file.uid = genFileId();
+    uploadRef.value?.handleStart(file);
+  }
+
+  //导入图书
+  async function importLocation(file) {
+    const formData = new FormData();
+    formData.append('file', file);
+
+    const res = await request.post('/baseinfo/godownposition/import', formData);
+    if (res.data.code === 200) {
+      return res.data.msg;
+    }
+    return Promise.reject(new Error(res.data.msg));
+  }
+
+  /** 提交 */
+  const handleSumbit = () => {
+    loading.value = true;
+    let file = fileList.value[0];
+    importLocation(file.raw)
+      .then((msg) => {
+        loading.value = false;
+        ElMessage.success(msg);
+        visible.value = false;
+        emit('done');
+      })
+      .catch((e) => {
+        loading.value = false;
+      });
+  };
+
+  /** 上传 */
+  const doUpload = (file) => {
+    if (
+      ![
+        'application/vnd.ms-excel',
+        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
+      ].includes(file.type)
+    ) {
+      EleMessage.error('只能选择 excel 文件');
+      return false;
+    }
+    if (file.size / 1024 / 1024 > 10) {
+      EleMessage.error('大小不能超过 10MB');
+      return false;
+    }
+    return false;
+  };
+</script>

+ 81 - 0
src/views/recycleService/location/components/location-search.vue

@@ -0,0 +1,81 @@
+<!-- 搜索表单 -->
+<template>
+  <ele-card :body-style="{ paddingBottom: '8px' }">
+    <ProSearch
+      :items="formItems"
+      ref="searchRef"
+      @search="search"
+      :initKeys="initKeys"
+    ></ProSearch>
+  </ele-card>
+</template>
+
+<script setup>
+  import { reactive, ref, defineEmits } from 'vue';
+  import ProSearch from '@/components/CommonPage/ProSearch2.vue';
+
+  let { proxy } = getCurrentInstance();
+  const emit = defineEmits(['search']);
+  const godownList = ref([]);
+  const formItems = computed(() => {
+    return [
+      {
+        type: 'select',
+        label: '仓库名称',
+        prop: 'godownId',
+        options: godownList.value.map((d) => {
+          return { label: d.name, value: d.id };
+        }),
+        props: {
+          placeholder: '请选择或输入搜索',
+          filterable: true,
+          remote: true,
+          remoteMethod: (query) => {
+            console.log('query', query);
+            if (!query) {
+              return [];
+            }
+            return getStoreList(query).then((res) => {
+              if (res.data.code == 200) {
+                return res.data.data.map((v) => ({
+                  label: v.name,
+                  value: v.id
+                }));
+              } else {
+                return [];
+              }
+            });
+          }
+        }
+      },
+      { type: 'input', label: '库位编码', prop: 'code' },
+      {
+        type: 'dictSelect',
+        label: '状态',
+        prop: 'useStatus',
+        props: {
+          code: 'use_status'
+        }
+      }
+    ];
+  });
+
+  function getStoreList(name="") {
+    return proxy.$http.post(`/baseinfo/godown/searchGodown?name=${name}`);
+  }
+  getStoreList().then((res) => {
+    godownList.value = res.data.data;
+  });
+
+  const initKeys = reactive({
+    godownId: '',
+    useStatus: '',
+    code: ''
+  });
+
+  const searchRef = ref(null);
+  /** 搜索 */
+  const search = (data) => {
+    emit('search', { ...data });
+  };
+</script>

+ 208 - 0
src/views/recycleService/location/index.vue

@@ -0,0 +1,208 @@
+<template>
+  <ele-page flex-table>
+    <location-search @search="reload"></location-search>
+
+    <common-table ref="pageRef" :pageConfig="pageConfig" :columns="columns">
+      <template #toolbar>
+        <el-button
+          type="primary"
+          plain
+          :icon="PlusOutlined"
+          v-permission="'recycleService:location:add'"
+          @click="handleUpdate()"
+        >
+          新增库位
+        </el-button>
+        <el-button
+          type="danger"
+          plain
+          :icon="DeleteOutlined"
+          v-permission="'recycleService:location:batchDelete'"
+          @click="handleBatchDelete()"
+        >
+          批量删除
+        </el-button>
+        <el-button
+          type="danger"
+          plain
+          :icon="DeleteOutlined"
+          v-permission="'recycleService:location:batchDisabled'"
+          @click="handleBatchDelete()"
+        >
+          批量停用
+        </el-button>
+        <el-button
+          type="primary"
+          plain
+          v-permission="'recycleService:location:import'"
+          @click="handleImportExcel"
+          :icon="UploadOutlined"
+        >
+          导入
+        </el-button>
+        <el-button
+          type="success"
+          plain
+          v-permission="'recycleService:location:export'"
+          @click="handleExportExcel"
+          :icon="DownloadOutlined"
+        >
+          导出
+        </el-button>
+      </template>
+      <template #picture="{ row }">
+        <el-image
+          style="width: 80px; height: 100px"
+          fit="cover"
+          src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
+        />
+      </template>
+
+      <template #useStatus="{ row }">
+        <dict-data code="use_status" type="tag" :model-value="row.useStatus" />
+      </template>
+
+      <template #action="{ row }">
+        <div>
+          <el-button
+            type="primary"
+            link
+            v-permission="'recycleService:location:update'"
+            @click="handleUpdate(row)"
+          >
+            编辑
+          </el-button>
+          <el-button
+            :type="row.useStatus == 0 ? 'success' : 'danger'"
+            link
+            v-permission="'recycleService:location:changeStatus'"
+            @click="handleUseStatus(row)"
+          >
+            {{ row.useStatus == 0 ? '启用' : '停用' }}
+          </el-button>
+          <el-button
+            type="danger"
+            link
+            v-permission="'recycleService:location:delete'"
+            @click="handleBatchDelete(row)"
+          >
+            删除
+          </el-button>
+        </div>
+      </template>
+    </common-table>
+
+    <location-edit ref="editRef" @success="reload()"></location-edit>
+    <location-import v-model="importShow" @success="reload()"></location-import>
+  </ele-page>
+</template>
+
+<script setup>
+  import { ref, reactive } from 'vue';
+  import { ElMessageBox } from 'element-plus/es';
+  import { EleMessage } from 'ele-admin-plus/es';
+  import {
+    PlusOutlined,
+    DeleteOutlined,
+    DownloadOutlined,
+    UploadOutlined
+  } from '@/components/icons';
+  import CommonTable from '@/components/CommonPage/CommonTable.vue';
+  import locationSearch from '@/views/recycleService/location/components/location-search.vue';
+  import locationEdit from '@/views/recycleService/location/components/location-edit.vue';
+  import locationImport from '@/views/recycleService/location/components/location-import.vue'
+  import { useDictData } from '@/utils/use-dict-data';
+  import { useRouter } from 'vue-router';
+  import request from '@/utils/request';
+
+  defineOptions({ name: 'locationList' });
+  const [schoolLevelDicts, UseStatusDicts] = useDictData([
+    'school_level',
+    'use_status'
+  ]);
+
+  /** 表格列配置 */
+  const columns = ref([
+    {
+      type: 'selection',
+      columnKey: 'selection',
+      width: 50,
+      align: 'center',
+      fixed: 'left'
+    },
+    { label: '库位编码', prop: 'code', align: 'center', minWidth: 140 },
+    { label: '仓库', prop: 'godownName', align: 'center' },
+    {
+      label: '状态',
+      prop: 'useStatus',
+      align: 'center',
+      slot: 'useStatus',
+      formatter: (row) =>
+        UseStatusDicts.value.find((d) => d.dictValue == row.useStatus)
+          ?.dictLabel
+    },
+    { label: '备注', prop: 'remark', align: 'center', minWidth: 180 },
+    {
+      columnKey: 'action',
+      label: '操作',
+      width: 240,
+      align: 'center',
+      slot: 'action'
+    }
+  ]);
+
+  let router = useRouter();
+  /** 页面组件实例 */
+  const pageRef = ref(null);
+
+  const pageConfig = reactive({
+    pageUrl: '/baseinfo/godownposition/pagelist',
+    exportUrl: '/baseinfo/godownposition/export',
+    fileName: '库位管理',
+    cacheKey: 'locationTable'
+  });
+
+  //刷新表格
+  function reload(where) {
+    pageRef.value?.reload(where);
+  }
+
+  //批量删除
+  function handleBatchDelete(row) {
+    let selections = row ? [row] : pageRef.value?.getSelections();
+    let ids = selections.map((item) => item.id).join(',');
+    let url = `/baseinfo/godownposition/delete?id=${ids}`;
+    pageRef.value?.operatBatch({
+      title: '确认删除?',
+      method: 'delete',
+      url,
+      row
+    });
+  }
+  //导出excel
+  function handleExportExcel() {
+    pageRef.value?.exportData('库位管理');
+  }
+  //导入excel
+  const importShow = ref(false);
+  function handleImportExcel() {
+    importShow.value = true;
+  }
+  //启用/停用
+  function handleUseStatus(row) {
+    let message =
+      row.useStatus == 1 ? '确定停用该库位信息?' : '确认启用该库位信息?';
+    let data = JSON.parse(JSON.stringify(row));
+    data.useStatus = row.useStatus == 1 ? 0 : 1;
+    pageRef.value?.messageBoxConfirm({
+      message,
+      fetch: () => request.post('/baseinfo/godownposition/edit', data)
+    });
+  }
+
+  //编辑页面
+  const editRef = ref(null);
+  function handleUpdate(row) {
+    editRef.value?.handleOpen(row);
+  }
+</script>

+ 76 - 0
src/views/recycleService/locationOrder/components/location-order-search.vue

@@ -0,0 +1,76 @@
+<!-- 搜索表单 -->
+<template>
+  <ele-card :body-style="{ paddingBottom: '8px' }">
+    <ProSearch
+      :items="formItems"
+      ref="searchRef"
+      @search="search"
+      :initKeys="initKeys"
+    ></ProSearch>
+  </ele-card>
+</template>
+
+<script setup>
+  import { reactive, ref, defineEmits } from 'vue';
+  import ProSearch from '@/components/CommonPage/ProSearch2.vue';
+
+  let { proxy } = getCurrentInstance();
+  const emit = defineEmits(['search']);
+  const godownList = ref([]);
+  const formItems = computed(() => {
+    return [
+      {
+        type: 'select',
+        label: '仓库名称',
+        prop: 'godownId',
+        options: godownList.value.map((d) => {
+          return { label: d.name, value: d.id };
+        }),
+        props: {
+          placeholder: '请选择或输入搜索',
+          filterable: true,
+          remote: true,
+          remoteMethod: (query) => {
+            console.log('query', query);
+            if (!query) {
+              return [];
+            }
+            return getStoreList(query).then((res) => {
+              if (res.data.code == 200) {
+                return res.data.data.map((v) => ({
+                  label: v.name,
+                  value: v.id
+                }));
+              } else {
+                return [];
+              }
+            });
+          }
+        }
+      },
+      { type: 'input', label: '库位编码', prop: 'code' },
+      { type: 'input', label: '订单编号', prop: 'orderCode' },
+      { type: 'input', label: '物流编号', prop: 'logisticsCode' },
+    ];
+  });
+
+  function getStoreList(name = '') {
+    return proxy.$http.post(`/baseinfo/godown/searchGodown?name=${name}`);
+  }
+  getStoreList().then((res) => {
+    godownList.value = res.data.data;
+  });
+
+  const initKeys = reactive({
+    godownId: '',
+    logisticsCode: '',
+    code: '',
+    orderCode: '',
+  });
+
+  const searchRef = ref(null);
+  /** 搜索 */
+  const search = (data) => {
+    emit('search', { ...data });
+  };
+</script>

+ 69 - 0
src/views/recycleService/locationOrder/index.vue

@@ -0,0 +1,69 @@
+<template>
+  <ele-page flex-table>
+    <location-order-search @search="reload"></location-order-search>
+
+    <common-table ref="pageRef" :pageConfig="pageConfig" :columns="columns">
+      <template #toolbar>
+        <el-button
+          type="success"
+          plain
+          v-permission="'recycleService:locationOrder:export'"
+          @click="handleExportExcel"
+          :icon="DownloadOutlined"
+        >
+          导出
+        </el-button>
+      </template>
+      <template #picture="{ row }">
+        <el-image
+          style="width: 80px; height: 100px"
+          fit="cover"
+          src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
+        />
+      </template>
+    </common-table>
+  </ele-page>
+</template>
+
+<script setup>
+  import { ref, reactive } from 'vue';
+  import { DownloadOutlined } from '@/components/icons';
+  import CommonTable from '@/components/CommonPage/CommonTable.vue';
+  import locationOrderSearch from '@/views/recycleService/locationOrder/components/location-order-search.vue';
+  import request from '@/utils/request';
+
+  defineOptions({ name: 'locationOrderList' });
+
+
+  /** 表格列配置 */
+  const columns = ref([
+    { type: 'index', width: 50, label: '#' },
+    { label: '仓库名称', prop: 'godownName', align: 'center' },
+    { label: '库位编码', prop: 'code', align: 'center', minWidth: 140 },
+    { label: '订单编号', prop: 'orderCode', align: 'center' },
+    { label: '物流编号', prop: 'logisticsCode', align: 'center' },
+    { label: '数量', prop: 'number', align: 'center' },
+    { label: '不良数量', prop: 'nonumber', align: 'center' },
+    { label: '验货时间', prop: 'createTime', align: 'center', minWidth: 180 },
+  ]);
+
+  /** 页面组件实例 */
+  const pageRef = ref(null);
+
+  const pageConfig = reactive({
+    pageUrl: '/baseinfo/godownposition/pagelist',
+    exportUrl: '/baseinfo/godownposition/export',
+    fileName: '库位订单',
+    cacheKey: 'locationOrderTable'
+  });
+
+  //刷新表格
+  function reload(where) {
+    pageRef.value?.reload(where);
+  }
+
+  //导出excel
+  function handleExportExcel() {
+    pageRef.value?.exportData('库位订单');
+  }
+</script>