Pārlūkot izejas kodu

feat(图书管理): 新增批量导入相关功能

创建通用的批量导入弹窗组件,在图书管理页面新增批量更新售价、批量加入黑名单、批量加入回收书单、批量多本回收、批量暂停回收共5个批量导入操作,同时修复了原有按钮的缩进格式问题。
ylong 1 mēnesi atpakaļ
vecāks
revīzija
7261a3d998

+ 140 - 0
src/views/data/books/components/batch-import.vue

@@ -0,0 +1,140 @@
+<template>
+    <ele-modal
+        :width="460"
+        :title="title"
+        :body-style="{ paddingTop: '8px' }"
+        v-model="visible"
+    >
+        <div class="flex mb-2">
+            <div class="text-sm">请先下载'{{ templateName }}':</div>
+            <el-button
+                @click="downloadTemplate"
+                link
+                type="primary"
+            >下载{{ templateName }}</el-button>
+        </div>
+
+        <div class="flex flex-col mb-2">
+            <div class="text-sm text-yellow-500">{{ instructionTitle }}:</div>
+            <div
+                v-for="(item, index) in instructions"
+                :key="index"
+                class="text-sm"
+            >
+                {{ index + 1 }}.{{ item }}
+            </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 } from 'vue';
+    import { genFileId } from 'element-plus';
+    import { EleMessage } from 'ele-admin-plus/es';
+    import { downloadOssLink } from '@/utils/common';
+    import request from '@/utils/request';
+
+    const props = defineProps({
+        title: { type: String, default: '导入' },
+        templateName: { type: String, default: '导入模板' },
+        templateUrl: { type: String, required: true },
+        uploadUrl: { type: String, required: true },
+        instructionTitle: { type: String, default: '使用说明' },
+        instructions: {
+            type: Array,
+            default: () => [
+                '支持通过导入来批量操作',
+                '导入文件第一行需与模版完全一致'
+            ]
+        }
+    });
+
+    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);
+    }
+
+    function downloadTemplate() {
+        downloadOssLink(props.templateUrl, props.templateName);
+    }
+
+    async function importFile(file) {
+        const formData = new FormData();
+        formData.append('file', file);
+
+        const res = await request.post(props.uploadUrl, formData);
+        if (res.data.code === 200) {
+            return res.data;
+        }
+        return Promise.reject(new Error(res.data.msg));
+    }
+
+    const handleSumbit = () => {
+        if (fileList.value.length === 0) {
+            EleMessage.error('请选择文件');
+            return;
+        }
+        loading.value = true;
+        let file = fileList.value[0];
+        importFile(file.raw)
+            .then((res) => {
+                loading.value = false;
+                EleMessage.success('操作成功,请前往导出记录下载');
+                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>

+ 144 - 1
src/views/data/books/index.vue

@@ -49,7 +49,7 @@
                 >
                     根据ISBN导出
                 </el-button>
-                <el-button
+                 <el-button
                     type="info"
                     plain
                     v-permission="'data:books:importLog'"
@@ -67,6 +67,53 @@
                 >
                     导出记录
                 </el-button>
+                <div class="w-full mt-2">
+                    <el-button
+                        type="warning"
+                        plain
+                        v-permission="'data:books:importBatchUpdatePrice'"
+                        @click="handleBatchUpdatePrice"
+                        :icon="UploadOutlined"
+                    >
+                        批量更新售价
+                    </el-button>
+                    <el-button
+                        type="danger"
+                        plain
+                        v-permission="'data:books:importBatchBlacklist'"
+                        @click="handleBatchBlacklist"
+                        :icon="UploadOutlined"
+                    >
+                        批量加入黑名单
+                    </el-button>
+                    <el-button
+                        type="primary"
+                        plain
+                        v-permission="'data:books:importBatchAddRecycle'"
+                        @click="handleBatchAddRecycle"
+                        :icon="UploadOutlined"
+                    >
+                        批量加入回收书单
+                    </el-button>
+                    <el-button
+                        type="primary"
+                        plain
+                        v-permission="'data:books:importBatchAddRecycleMulti'"
+                        @click="handleBatchAddRecycleMulti"
+                        :icon="UploadOutlined"
+                    >
+                        批量加入回收书单(多本回收)
+                    </el-button>
+                    <el-button
+                        type="warning"
+                        plain
+                        v-permission="'data:books:importBatchPauseRecycle'"
+                        @click="handleBatchPauseRecycle"
+                        :icon="UploadOutlined"
+                    >
+                        批量暂停回收
+                    </el-button>
+                </div>
             </template>
             <template #cover="{ row }">
                 <el-image
@@ -110,6 +157,71 @@
         <upload-image ref="uploadImageRef" @done="reload()" />
         <book-export-log ref="exportLogRef" />
         <books-export-isbn ref="exportIsbnRef" v-model="showExportByIsbn" />
+        <batch-import
+            v-model="showBatchUpdatePrice"
+            title="批量更新售价"
+            template-name="批量更新售价模板"
+            template-url="https://shuhi.oss-cn-qingdao.aliyuncs.com/default/sell_price_batch_import_template.xlsx"
+            upload-url="/book/bookInfo/importBatchUpdatePrice"
+            :instructions="[
+                '支持通过导入来批量更新售价',
+                '文件ISBN不存在或者错误,会自动过滤掉',
+                '导入文件第一行需与模版完全一致'
+            ]"
+            @done="reload()"
+        />
+        <batch-import
+            v-model="showBatchBlacklist"
+            title="批量加入黑名单"
+            template-name="批量操作模板"
+            template-url="https://shuhi.oss-cn-qingdao.aliyuncs.com/default/recycle_discount_batch_import_template.xlsx"
+            upload-url="/book/bookInfo/importBatchBlacklist"
+            :instructions="[
+                '支持通过导入来批量加入黑名单',
+                '文件ISBN不存在或者错误,会自动过滤掉',
+                '导入文件第一行需与模版完全一致'
+            ]"
+            @done="reload()"
+        />
+        <batch-import
+            v-model="showBatchAddRecycle"
+            title="批量加入回收书单"
+            template-name="批量操作模板"
+            template-url="https://shuhi.oss-cn-qingdao.aliyuncs.com/default/recycle_discount_batch_import_template.xlsx"
+            upload-url="/book/bookInfo/importBatchAddRecycle"
+            :instructions="[
+                '支持通过导入来批量加入回收书单',
+                '文件ISBN不存在或者错误,会自动过滤掉',
+                '导入文件第一行需与模版完全一致'
+            ]"
+            @done="reload()"
+        />
+        <batch-import
+            v-model="showBatchAddRecycleMulti"
+            title="批量加入回收书单(多本回收)"
+            template-name="批量操作模板"
+            template-url="https://shuhi.oss-cn-qingdao.aliyuncs.com/default/recycle_discount_batch_import_template.xlsx"
+            upload-url="/book/bookInfo/importBatchAddRecycleMulti"
+            :instructions="[
+                '支持通过导入来批量加入回收书单,recycle_max_per_order=10',
+                '文件ISBN不存在或者错误,会自动过滤掉',
+                '导入文件第一行需与模版完全一致'
+            ]"
+            @done="reload()"
+        />
+        <batch-import
+            v-model="showBatchPauseRecycle"
+            title="批量暂停回收"
+            template-name="批量操作模板"
+            template-url="https://shuhi.oss-cn-qingdao.aliyuncs.com/default/recycle_discount_batch_import_template.xlsx"
+            upload-url="/book/bookInfo/importBatchPauseRecycle"
+            :instructions="[
+                '支持通过导入来批量暂停回收',
+                '文件ISBN不存在或者错误,会自动过滤掉',
+                '导入文件第一行需与模版完全一致'
+            ]"
+            @done="reload()"
+        />
         <book-import-log ref="importLogRef" />
     </ele-page>
 </template>
@@ -135,6 +247,7 @@
     import bookExportLog from '@/views/data/books/components/book-export-log.vue';
     import bookImportLog from '@/views/data/books/components/book-import-log.vue';
     import booksExportIsbn from '@/views/data/books/components/books-export-isbn.vue';
+import batchImport from '@/views/data/books/components/batch-import.vue';
     import { useDictData } from '@/utils/use-dict-data';
 
     defineOptions({ name: 'RecycleOrderCancelled' });
@@ -254,4 +367,34 @@
     function handleExportByIsbn() {
         showExportByIsbn.value = true;
     }
+
+    //批量更新售价
+    const showBatchUpdatePrice = ref(false);
+    function handleBatchUpdatePrice() {
+        showBatchUpdatePrice.value = true;
+    }
+
+    //批量加入黑名单
+    const showBatchBlacklist = ref(false);
+    function handleBatchBlacklist() {
+        showBatchBlacklist.value = true;
+    }
+
+    //批量加入回收书单
+    const showBatchAddRecycle = ref(false);
+    function handleBatchAddRecycle() {
+        showBatchAddRecycle.value = true;
+    }
+
+    //批量加入回收书单(多本回收)
+    const showBatchAddRecycleMulti = ref(false);
+    function handleBatchAddRecycleMulti() {
+        showBatchAddRecycleMulti.value = true;
+    }
+
+    //批量暂停回收
+    const showBatchPauseRecycle = ref(false);
+    function handleBatchPauseRecycle() {
+        showBatchPauseRecycle.value = true;
+    }
 </script>