Sfoglia il codice sorgente

修改bug+实现导入导出功能

LiuShu_0203 6 giorni fa
parent
commit
07a5548bcb

+ 2 - 2
.env.production

@@ -6,8 +6,8 @@ VITE_APP_ENV = 'production'
 
 # 若依管理系统/生产环境
 # VITE_APP_BASE_API = '/prod-api'
-VITE_APP_BASE_API = 'https://jg-boot-171804-5-1367995226.sh.run.tcloudbase.com'
-# VITE_APP_BASE_API = 'https://jg-boot-181206-5-1373037829.sh.run.tcloudbase.com'
+# VITE_APP_BASE_API = 'https://jg-boot-171804-5-1367995226.sh.run.tcloudbase.com'
+VITE_APP_BASE_API = 'https://jg-boot-181206-5-1373037829.sh.run.tcloudbase.com'
 
 
 

+ 3 - 2
package.json

@@ -23,7 +23,7 @@
     "axios": "0.27.2",
     "echarts": "5.4.3",
     "element-plus": "2.4.3",
-    "file-saver": "2.0.5",
+    "file-saver": "^2.0.5",
     "fuse.js": "6.6.2",
     "js-cookie": "3.0.5",
     "jsencrypt": "3.3.2",
@@ -32,7 +32,8 @@
     "pinia": "2.1.7",
     "vue": "3.3.9",
     "vue-cropper": "1.1.1",
-    "vue-router": "4.2.5"
+    "vue-router": "4.2.5",
+    "xlsx": "^0.18.5"
   },
   "devDependencies": {
     "@vitejs/plugin-vue": "4.5.0",

+ 2 - 2
src/utils/mysql.js

@@ -1,8 +1,8 @@
 import cloudbase from '@cloudbase/js-sdk';
 
 const apps = cloudbase.init({
-  env: 'cloud1-6g98iw7i28b01747',
-  // env: 'honghgaier-5guiffgcf17a2eea',
+  // env: 'cloud1-6g98iw7i28b01747',
+  env: 'honghgaier-5guiffgcf17a2eea',
   traceUser: true
 });
 

+ 4 - 4
src/views/message/index.vue

@@ -326,7 +326,7 @@
         </el-form-item>
         <!-- 文件上传 -->
         <el-form-item
-          v-if="form.tag_id !== 'BULYF5VJ9W' && !tuwenadd"
+          v-if="form.tag_id !== 'C05HFKY0YY' && !tuwenadd"
           label="文件上传"
           prop="url"
         >
@@ -392,13 +392,13 @@ const coverList = ref([]);
 const tuwenadd = ref(false);
 const isEdit = ref(false);
 
-// 动手动脑图库对应 tag_id 为 'BULYBZSEY8', 全脑对应 'BULYF5VJ9W'
+// 动手动脑图库对应 tag_id 为 'C05HDKNBDS', 全脑对应 'C05HFKY0YY'
 const showDan = computed(() => {
-  return form.value.tag_id === "BULYBZSEY8";
+  return form.value.tag_id === "C05HDKNBDS";
 });
 const showLevel = computed(() => {
   return (
-    form.value.tag_id === "BULYBZSEY8" || form.value.tag_id === "BULYF5VJ9W"
+    form.value.tag_id === "C05HDKNBDS" || form.value.tag_id === "C05HFKY0YY"
   );
 });
 

+ 91 - 0
src/views/payment/index.vue

@@ -26,6 +26,18 @@
               </el-form-item>
             </el-form>
 
+            <!-- 批量操作 -->
+            <el-row :gutter="10" class="mb8">
+              <el-col :span="1.5">
+                <el-button
+                    type="success"
+                    plain
+                    icon="Upload"
+                    @click="handleImport1"
+                >导出</el-button>
+              </el-col>
+            </el-row>
+
             <el-table v-loading="loading" :data="userList">
               <el-table-column type="index" label="序号" width="60" align="center"
                 :index="(index) => (queryParams.pageNum - 1) * queryParams.pageSize + index + 1" />
@@ -72,6 +84,18 @@
               </el-form-item>
             </el-form>
 
+            <!-- 批量操作 -->
+            <el-row :gutter="10" class="mb8">
+              <el-col :span="1.5">
+                <el-button
+                    type="success"
+                    plain
+                    icon="Upload"
+                    @click="handleImport2"
+                >导出</el-button>
+              </el-col>
+            </el-row>
+
             <el-table v-loading="loading" :data="userList">
               <el-table-column type="index" label="序号" width="60" align="center"
                 :index="(index) => (queryParams.pageNum - 1) * queryParams.pageSize + index + 1" />
@@ -94,6 +118,8 @@
 <script setup>
 import { callCloudFunction } from "@/api/cloud";
 import { ref, reactive, toRefs, getCurrentInstance, watch } from "vue";
+import * as XLSX from "xlsx";
+import { saveAs } from "file-saver";
 
 const { proxy } = getCurrentInstance();
 
@@ -201,6 +227,71 @@ function resetQuery2() {
   getList2();
 }
 
+/** 年级导出 */
+async function handleImport1() {
+  try {
+    loading.value = true;
+    // 获取所有年级统计数据,不分页
+    const res = await callCloudFunction("database", {
+      page: "payment",
+      action: "findPaymentAll",
+      data: buildParams(),
+    });
+
+    console.log(res, 'resresresresresresres');
+
+    const data = res.records.map(item => ({
+      "学校名称": item.schoolName,
+      "年级": item.grade === 0 ? '小班' : item.grade === 1 ? '中班' : item.grade === 2 ? '大班' : '未知',
+      "已付款人数": item.paidCount,
+      "未付款人数": item.unpaidCount,
+      "已付款金额": item.paidMoney,
+      "未付款金额": item.unpaidMoney,
+    }));
+
+    const ws = XLSX.utils.json_to_sheet(data);
+    const wb = XLSX.utils.book_new();
+    XLSX.utils.book_append_sheet(wb, ws, "年级统计");
+    const wbout = XLSX.write(wb, { bookType: "xlsx", type: "array" });
+    saveAs(new Blob([wbout], { type: "application/octet-stream" }), "年级统计.xlsx");
+  } catch (err) {
+    console.error("导出失败", err);
+  } finally {
+    loading.value = false;
+  }
+}
+
+/** 学校导出 */
+async function handleImport2() {
+  try {
+    loading.value = true;
+    // 获取所有学校统计数据,不分页
+    const res = await callCloudFunction("database", {
+      page: "payment",
+      action: "findPaymentAllBySchool",
+      data: buildParams(),
+    });
+
+    const data = res.records.map(item => ({
+      "学校名称": item.schoolName,
+      "已付款人数": item.paidCount,
+      "未付款人数": item.unpaidCount,
+      "已付款金额": item.paidMoney,
+      "未付款金额": item.unpaidMoney,
+    }));
+
+    const ws = XLSX.utils.json_to_sheet(data);
+    const wb = XLSX.utils.book_new();
+    XLSX.utils.book_append_sheet(wb, ws, "学校统计");
+    const wbout = XLSX.write(wb, { bookType: "xlsx", type: "array" });
+    saveAs(new Blob([wbout], { type: "application/octet-stream" }), "学校统计.xlsx");
+  } catch (err) {
+    console.error("导出失败", err);
+  } finally {
+    loading.value = false;
+  }
+}
+
 /** 监听 tab 切换,自动刷新 */
 watch(activeName, (val) => {
   queryParams.value.pageNum = 1;

+ 120 - 40
src/views/system/school/index.vue

@@ -47,9 +47,9 @@
       <el-col :span="1.5">
         <el-button type="primary" plain icon="Plus" @click="handleAdd">新增</el-button>
       </el-col>
-      <!-- <el-col :span="1.5">
+      <el-col :span="1.5">
         <el-button type="info" plain icon="Upload" @click="handleImport">导入</el-button>
-      </el-col> -->
+      </el-col>
       <!-- <el-col :span="1.5">
         <el-button type="success" plain icon="Download" @click="handleExport">导出</el-button>
       </el-col> -->
@@ -61,6 +61,7 @@
       <el-table-column type="selection" width="50" align="center" />
       <el-table-column type="index" label="序号" width="60" align="center"
         :index="(index) => (queryParams.pageNum - 1) * queryParams.pageSize + index + 1" />
+      <el-table-column label="学校ID" align="center" prop="_id" />
       <el-table-column label="学校名称" align="center" prop="name" />
       <el-table-column label="联系人" align="center" prop="contacts" />
       <el-table-column label="电话" align="center" prop="phone" />
@@ -115,28 +116,26 @@
 
     <!-- 导入弹窗 -->
     <el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
-      <el-upload
-        ref="uploadRef"
-        :limit="1"
-        accept=".xlsx, .xls"
-        :headers="upload.headers"
-        :action="upload.url"
-        :disabled="upload.isUploading"
-        :on-progress="handleFileUploadProgress"
-        :on-success="handleFileSuccess"
-        :auto-upload="false"
-        drag
-      >
-        <el-icon class="el-icon--upload"><upload-filled /></el-icon>
-        <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
-      </el-upload>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button type="primary" @click="submitFileForm">确 定</el-button>
-          <el-button @click="upload.open = false">取 消</el-button>
-        </div>
-      </template>
-    </el-dialog>
+         <el-upload
+            ref="uploadRef"
+            :limit="1"
+            accept=".xlsx, .xls"
+            :disabled="upload.isUploading"
+            :auto-upload="true"
+            :before-upload="beforeUpload"
+            :on-remove="handleRemove"
+            drag
+         >
+            <el-icon class="el-icon--upload"><upload-filled /></el-icon>
+            <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+            <template #tip>
+               <div class="el-upload__tip text-center">
+                  <span>仅允许导入xls、xlsx格式文件。</span>
+                  <el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
+               </div>
+            </template>
+         </el-upload>
+      </el-dialog>
   </div>
 </template>
 
@@ -160,10 +159,17 @@ const schoolList = ref([]);
 
 /*** 用户导入参数 */
 const upload = reactive({
+  // 是否显示弹出层(用户导入)
   open: false,
-  title: "导入数据",
+  // 弹出层标题(用户导入)
+  title: "",
+  // 是否禁用上传
   isUploading: false,
+  // 是否更新已经存在的用户数据
+  // updateSupport: 0,
+  // 设置上传的请求头部
   headers: { Authorization: "Bearer " + getToken() },
+  // 上传的地址
   url: import.meta.env.VITE_APP_BASE_API + "/system/user/importData"
 });
 
@@ -275,30 +281,104 @@ function handleSelectionChange(selection) {
   multiple.value = !selection.length;
 }
 
+
+/** 下载模板操作 */
+async function importTemplate() {
+  try {
+    // 获取存储中的模板文件下载地址
+    const res = await proxy.$apps
+      // .storage()
+      .getTempFileURL({
+        fileList: ["cloud://honghgaier-5guiffgcf17a2eea.686f-honghgaier-5guiffgcf17a2eea-1373037829/schoolmoban.xlsx"], // 云存储里的路径
+      });
+
+      console.log(res.fileList[0].tempFileURL, 'res.fileList[0].tempFileURL');
+
+    const fileUrl = res.fileList[0].tempFileURL;
+    // 触发浏览器下载
+    const a = document.createElement("a");
+    a.href = fileUrl;
+    a.download = `system_teacher_${Date.now()}.xlsx`;
+    a.click();
+  } catch (err) {
+    proxy.$modal.msgError("下载失败");
+  }
+};
+
+/** 上传前处理(改用腾讯云存储) */
+async function beforeUpload(file) {
+  upload.isUploading = true;
+  try {
+    // 1. file 就是原生 File 对象
+    console.log(file, '上传文件对象');
+    if (!(file instanceof File)) {
+      throw new Error('请选择本地 Excel 文件上传');
+    }
+
+    // 2. 安全文件名
+    const safeFileName = file.name.replace(/[^a-zA-Z0-9_.-]/g, '_');
+    const cloudPath = `excel/teacher/${Date.now()}_${safeFileName}`;
+
+    // 3. 上传文件
+    const res = await proxy.$apps.uploadFile({
+      cloudPath,
+      filePath: file
+    });
+    console.log('上传成功 fileID:', res.fileID);
+
+    // 4. 调用云函数解析
+    const cloudResult = await proxy.$apps.callFunction({
+      name: "schoolexcel",
+      data: { fileID: res.fileID }
+    });
+    console.log('云函数返回:', cloudResult);
+
+    const fnResult = cloudResult.result || cloudResult;
+    if (fnResult.code === 0) {
+      proxy.$modal.msgSuccess('导入成功');
+      upload.open = false;
+      getList();
+    } else {
+      proxy.$modal.msgError('导入失败:' + (fnResult.msg || '未知错误'));
+    }
+  } catch (err) {
+    console.error('上传失败:', err);
+    proxy.$modal.msgError('上传失败: ' + err.message);
+  } finally {
+    upload.isUploading = false;
+  }
+  return false; // 阻止默认上传
+}
+
 /** 导入 */
 function handleImport() {
   upload.title = "导入学校数据";
   upload.open = true;
 }
 
-/** 上传进度 */
-function handleFileUploadProgress() {
-  upload.isUploading = true;
+/** 移除文件 */
+function handleRemove(file) {
+  console.log("文件移除:", file);
 }
 
-/** 上传成功 */
-function handleFileSuccess(response, file) {
-  upload.open = false;
-  upload.isUploading = false;
-  proxy.$refs["uploadRef"].handleRemove(file);
-  proxy.$alert(response.msg, "导入结果");
-  getList();
-}
+// /** 上传进度 */
+// function handleFileUploadProgress() {
+//   upload.isUploading = true;
+// }
 
-/** 提交上传文件 */
-function submitFileForm() {
-  proxy.$refs["uploadRef"].submit();
-}
+// /** 上传成功 */
+// function handleFileSuccess(response, file) {
+//   upload.open = false;
+//   upload.isUploading = false;
+//   proxy.$refs["uploadRef"].handleRemove(file);
+//   proxy.$alert(response.msg, "导入结果");
+//   getList();
+// }
+
+// /** 提交上传文件 */
+// function submitFileForm() {
+//   proxy.$refs["uploadRef"].submit();
+// }
 
 /** 提交保存 */
 function submitForm() {

+ 98 - 43
src/views/system/teacher/index.vue

@@ -36,19 +36,17 @@
               plain
               icon="Plus"
               @click="handleAdd"
-              v-hasPermi="['system:user:add']"
               >新增</el-button
             >
           </el-col>
-          <!-- <el-col :span="1.5">
-                  <el-button
-                     type="info"
-                     plain
-                     icon="Upload"
-                     @click="handleImport"
-                     v-hasPermi="['system:user:import']"
-                  >导入</el-button>
-          </el-col> -->
+          <el-col :span="1.5">
+            <el-button
+                type="info"
+                plain
+                icon="Upload"
+                @click="handleImport"
+            >导入</el-button>
+          </el-col>
           <right-toolbar
             v-model:showSearch="showSearch"
             @queryTable="getList"
@@ -131,32 +129,21 @@
             ref="uploadRef"
             :limit="1"
             accept=".xlsx, .xls"
-            :headers="upload.headers"
-            :action="upload.url + '?updateSupport=' + upload.updateSupport"
             :disabled="upload.isUploading"
-            :on-progress="handleFileUploadProgress"
-            :on-success="handleFileSuccess"
-            :auto-upload="false"
+            :auto-upload="true"
+            :before-upload="beforeUpload"
+            :on-remove="handleRemove"
             drag
          >
             <el-icon class="el-icon--upload"><upload-filled /></el-icon>
             <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
             <template #tip>
                <div class="el-upload__tip text-center">
-                  <div class="el-upload__tip">
-                     <el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
-                  </div>
                   <span>仅允许导入xls、xlsx格式文件。</span>
                   <el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
                </div>
             </template>
          </el-upload>
-         <template #footer>
-            <div class="dialog-footer">
-               <el-button type="primary" @click="submitFileForm">确 定</el-button>
-               <el-button @click="upload.open = false">取 消</el-button>
-            </div>
-         </template>
       </el-dialog>
   </div>
 </template>
@@ -183,7 +170,7 @@ const upload = reactive({
   // 是否禁用上传
   isUploading: false,
   // 是否更新已经存在的用户数据
-  updateSupport: 0,
+  // updateSupport: 0,
   // 设置上传的请求头部
   headers: { Authorization: "Bearer " + getToken() },
   // 上传的地址
@@ -285,26 +272,94 @@ function handleImport() {
   upload.open = true;
 };
 /** 下载模板操作 */
-function importTemplate() {
-  proxy.download("system/user/importTemplate", {
-  }, `user_template_${new Date().getTime()}.xlsx`);
+async function importTemplate() {
+  try {
+    // 获取存储中的模板文件下载地址
+    const res = await proxy.$apps
+      // .storage()
+      .getTempFileURL({
+        fileList: ["cloud://honghgaier-5guiffgcf17a2eea.686f-honghgaier-5guiffgcf17a2eea-1373037829/teachermoban.xlsx"], // 云存储里的路径
+      });
+
+      console.log(res.fileList[0].tempFileURL, 'res.fileList[0].tempFileURL');
+
+    const fileUrl = res.fileList[0].tempFileURL;
+    // 触发浏览器下载
+    const a = document.createElement("a");
+    a.href = fileUrl;
+    a.download = `system_teacher_${Date.now()}.xlsx`;
+    a.click();
+  } catch (err) {
+    proxy.$modal.msgError("下载失败");
+  }
 };
-/**文件上传中处理 */
-const handleFileUploadProgress = (event, file, fileList) => {
+
+/** 上传前处理(改用腾讯云存储) */
+async function beforeUpload(file) {
   upload.isUploading = true;
-};
-/** 文件上传成功处理 */
-const handleFileSuccess = (response, file, fileList) => {
-  upload.open = false;
-  upload.isUploading = false;
-  proxy.$refs["uploadRef"].handleRemove(file);
-  proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
-  getList();
-};
-/** 提交上传文件 */
-function submitFileForm() {
-  proxy.$refs["uploadRef"].submit();
-};
+  try {
+    // 1. file 就是原生 File 对象
+    console.log(file, '上传文件对象');
+    if (!(file instanceof File)) {
+      throw new Error('请选择本地 Excel 文件上传');
+    }
+
+    // 2. 安全文件名
+    const safeFileName = file.name.replace(/[^a-zA-Z0-9_.-]/g, '_');
+    const cloudPath = `excel/teacher/${Date.now()}_${safeFileName}`;
+
+    // 3. 上传文件
+    const res = await proxy.$apps.uploadFile({
+      cloudPath,
+      filePath: file
+    });
+    console.log('上传成功 fileID:', res.fileID);
+
+    // 4. 调用云函数解析
+    const cloudResult = await proxy.$apps.callFunction({
+      name: "teacherexcel",
+      data: { fileID: res.fileID }
+    });
+    console.log('云函数返回:', cloudResult);
+
+    const fnResult = cloudResult.result || cloudResult;
+    if (fnResult.code === 0) {
+      proxy.$modal.msgSuccess('导入成功');
+      upload.open = false;
+      getList();
+    } else {
+      proxy.$modal.msgError('导入失败:' + (fnResult.msg || '未知错误'));
+    }
+  } catch (err) {
+    console.error('上传失败:', err);
+    proxy.$modal.msgError('上传失败: ' + err.message);
+  } finally {
+    upload.isUploading = false;
+  }
+  return false; // 阻止默认上传
+}
+
+
+/** 移除文件 */
+function handleRemove(file) {
+  console.log("文件移除:", file);
+}
+// /**文件上传中处理 */
+// const handleFileUploadProgress = (event, file, fileList) => {
+//   upload.isUploading = true;
+// };
+// /** 文件上传成功处理 */
+// const handleFileSuccess = (response, file, fileList) => {
+//   upload.open = false;
+//   upload.isUploading = false;
+//   proxy.$refs["uploadRef"].handleRemove(file);
+//   proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
+//   getList();
+// };
+// /** 提交上传文件 */
+// function submitFileForm() {
+//   proxy.$refs["uploadRef"].submit();
+// };
 
 getSchoolList();
 getList();

+ 1 - 1
vite.config.js

@@ -33,7 +33,7 @@ export default defineConfig(({ mode, command }) => {
         '/dev-api': {
           // target: 'https://jg-boot-172002-8-1368022779.sh.run.tcloudbase.com',
           // target: 'http://192.168.50.126:8080',
-          target: 'https://jg-boot-171804-5-1367995226.sh.run.tcloudbase.com',
+          target: 'https://	jg-boot-181206-5-1373037829.sh.run.tcloudbase.com',
           changeOrigin: true,
           rewrite: (p) => p.replace(/^\/dev-api/, '')
         }