fei
2025-11-15 e3cac4fb5b540b45d6ec1a53a534d08c03d7c174
打的修改
8个文件已修改
3个文件已添加
1719 ■■■■ 已修改文件
package-lock.json 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/system/dpdf.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/12.xls 补丁 | 查看 | 原始文档 | blame | 历史
src/views/archiveDoublePdf/index.vue 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/archiveManager/arMAutoUser.vue 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/archiveManager/archiveMaterial/index.vue 290 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/archiveManager/index.vue 1108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/archiveManager/seleUser.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/index.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/profile/userInfo.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json
@@ -10,6 +10,8 @@
      "license": "MIT",
      "dependencies": {
        "@riophae/vue-treeselect": "0.4.0",
        "@vue-office/excel": "^1.7.14",
        "@vue/composition-api": "^1.7.2",
        "axios": "0.28.1",
        "clipboard": "2.0.8",
        "core-js": "3.37.1",
@@ -29,6 +31,7 @@
        "vue": "2.6.12",
        "vue-count-to": "1.0.13",
        "vue-cropper": "0.5.5",
        "vue-demi": "^0.14.10",
        "vue-router": "3.4.9",
        "vuedraggable": "2.24.3",
        "vuex": "3.6.0"
@@ -2019,6 +2022,23 @@
      "integrity": "sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==",
      "dev": true
    },
    "node_modules/@vue-office/excel": {
      "version": "1.7.14",
      "resolved": "https://registry.npmjs.org/@vue-office/excel/-/excel-1.7.14.tgz",
      "integrity": "sha512-pVUgt+emDQUnW7q22CfnQ+jl43mM/7IFwYzOg7lwOwPEbiVB4K4qEQf+y/bc4xGXz75w1/e3Kz3G6wAafmFBFg==",
      "hasInstallScript": true,
      "license": "MIT",
      "peerDependencies": {
        "@vue/composition-api": "^1.7.1",
        "vue": "^2.0.0 || >=3.0.0",
        "vue-demi": "^0.14.6"
      },
      "peerDependenciesMeta": {
        "@vue/composition-api": {
          "optional": true
        }
      }
    },
    "node_modules/@vue/babel-helper-vue-jsx-merge-props": {
      "version": "1.4.0",
      "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz",
@@ -2656,6 +2676,15 @@
      "dev": true,
      "engines": {
        "node": ">=0.10.0"
      }
    },
    "node_modules/@vue/composition-api": {
      "version": "1.7.2",
      "resolved": "https://registry.npmjs.org/@vue/composition-api/-/composition-api-1.7.2.tgz",
      "integrity": "sha512-M8jm9J/laYrYT02665HkZ5l2fWTK4dcVg3BsDHm/pfz+MjDYwX+9FUaZyGwEyXEDonQYRCo0H7aLgdklcIELjw==",
      "license": "MIT",
      "peerDependencies": {
        "vue": ">= 2.5 < 2.7"
      }
    },
    "node_modules/@vue/preload-webpack-plugin": {
@@ -16945,6 +16974,32 @@
      "resolved": "https://registry.npmjs.org/vue-cropper/-/vue-cropper-0.5.5.tgz",
      "integrity": "sha512-5mGaBlS1EwLxUFwHHX2Q8zOZSiVfBUjOfolR+ZNKwu7Rh3u+GhwHYOyFkgZHhhoQBBNdyVB28O6W+MpMimhCbA=="
    },
    "node_modules/vue-demi": {
      "version": "0.14.10",
      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
      "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
      "hasInstallScript": true,
      "license": "MIT",
      "bin": {
        "vue-demi-fix": "bin/vue-demi-fix.js",
        "vue-demi-switch": "bin/vue-demi-switch.js"
      },
      "engines": {
        "node": ">=12"
      },
      "funding": {
        "url": "https://github.com/sponsors/antfu"
      },
      "peerDependencies": {
        "@vue/composition-api": "^1.0.0-rc.1",
        "vue": "^3.0.0-0 || ^2.6.0"
      },
      "peerDependenciesMeta": {
        "@vue/composition-api": {
          "optional": true
        }
      }
    },
    "node_modules/vue-hot-reload-api": {
      "version": "2.3.4",
      "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",
package.json
@@ -25,6 +25,8 @@
  },
  "dependencies": {
    "@riophae/vue-treeselect": "0.4.0",
    "@vue-office/excel": "^1.7.14",
    "@vue/composition-api": "^1.7.2",
    "axios": "0.28.1",
    "clipboard": "2.0.8",
    "core-js": "3.37.1",
@@ -44,6 +46,7 @@
    "vue": "2.6.12",
    "vue-count-to": "1.0.13",
    "vue-cropper": "0.5.5",
    "vue-demi": "^0.14.10",
    "vue-router": "3.4.9",
    "vuedraggable": "2.24.3",
    "vuex": "3.6.0"
src/api/system/dpdf.js
New file
@@ -0,0 +1,9 @@
import request from '@/utils/request'
// 查询【请填写功能名称】列表
export function getPdfFile(id) {
  return request({
    url: '/system/doublePdf/downLoadFile/' + id,
    method: 'get',
  })
}
src/assets/12.xls
Binary files differ
src/views/archiveDoublePdf/index.vue
New file
@@ -0,0 +1,219 @@
<template>
  <div class="app-container">
    <!-- 水平排列容器 -->
    <div style="display: flex; align-items: center; gap: 20px;">
      <!-- 上传区域 -->
      <div class="upload-container" style="width: 400px; height: 200px;">
        <el-upload
            class="upload-demo"
            ref="upload"
            :action="uploadUrl"
            :headers="uploadHeaders"
            :on-success="handleUploadSuccess"
            :on-error="handleUploadError"
            :before-upload="beforeUpload"
            accept=".pdf"
            :auto-upload="true"
            drag
          >
          <i class="el-icon-upload"></i>
          <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
          <div class="el-upload__tip" slot="tip">支持单个或批量上传,请选择PDF文件</div>
        </el-upload>
      </div>
      <!-- 下载按钮区域 - 上传前禁用,上传后可用 -->
      <div class="download-container" style="white-space: nowrap;">
        <el-button
          type="primary"
          icon="el-icon-download"
          @click="downloadFile"
          :disabled="!canDownload"
        >
          获取下载链接
        </el-button>
        <!-- 显示下载链接区域 -->
        <div v-if="showDownloadLink" style="margin-left: 10px;">
          <div style="margin-bottom: 5px;">
            <span>下载链接:</span>
          </div>
          <div style="margin-bottom: 5px;">
            <a :href="displayedDownloadLink" target="_blank" style="color: #409EFF; word-break: break-all; max-width: 400px; display: inline-block;">
              {{ displayedDownloadLink }}
            </a>
          </div>
          <div>
            <el-button
              type="text"
              icon="el-icon-copy-document"
              @click="copyLink"
              size="small"
            >
              复制
            </el-button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import { getToken } from '@/utils/auth'
import { getPdfFile } from '@/api/system/dpdf'
export default {
  name: "DoublePdf",
  data() {
    return {
      fid: '',
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      // 显示搜索条件
      showSearch: true,
      // 总条数
      total: 0,
      // 【请填写功能名称】表格数据
      categoryList: [],
      // 弹出层标题
      title: "",
      // 是否显示弹出层
      open: false,
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        numb: null,
        nname: null
      },
      // 表单参数
      form: {},
      // 表单校验
      rules: {
        numb: [
          { required: true, message: '请输入编号', trigger: 'blur' }
        ],
        nname: [
          { required: true, message: '请输入名称', trigger: 'blur' }
        ]
      },
      // 文件上传相关
      uploadUrl: process.env.VUE_APP_BASE_API + "/system/doublePdf/upload", // 上传接口
      uploadHeaders: {
        Authorization: 'Bearer ' + getToken()
      },
      // 文件下载相关
      canDownload: false, // 是否可以下载
      uploadedFileName: '', // 已上传的文件名
      downloadUrl: '', // 下载链接
      showDownloadLink: false, // 是否显示下载链接
      displayedDownloadLink: '' // 显示的下载链接
    }
  },
  created() {
    this.getList()
  },
  methods: {
    /** 查询【请填写功能名称】列表 */
    getList() {
      this.loading = true
    },
    /** 上传前检查 */
    beforeUpload(file) {
      const isPdf = file.type === 'application/pdf'
      const isLt50M = file.size / 1024 / 1024 < 50
      if (!isPdf) {
        this.$message.error('上传文件只能是 PDF 格式!')
      }
      if (!isLt50M) {
        this.$message.error('上传文件大小不能超过 50MB!')
      }
      return isPdf && isLt50M
    },
    /** 上传成功处理 */
    handleUploadSuccess(response) {
      console.log(response)
      console.log("-------------")
      if (response.code === 200) {
        this.$message.success('上传成功')
        // 设置下载相关信息
        this.canDownload = true
        // 假设响应中包含文件名和下载链接
        this.fid = (response.data.data)
        // this.uploadedFileName = response.fileName || '上传的文件'
        // this.downloadUrl = response.downloadUrl || (process.env.VUE_APP_BASE_API + "/system/doublePdf/download?fileId=" + response.fileId)
        // // 可以在这里刷新列表数据
        // // this.getList()
      } else {
        this.$message.error(response.msg || '上传失败')
      }
    },
    /** 获取下载链接 */
    downloadFile() {
      // 发起ajax请求获取下载链接
      getPdfFile(this.fid).then(response => {
        console.log(response)
          // 确保response.data.data是字符串类型
          const dataStr = String(response.data.data)
          alert(dataStr)
          if(dataStr.includes("任务尚未结束,无法获取文件")) {
            this.$message.success("文件没有处理完,稍等片刻")
            return;
          }
        if (response.code === 200 && response.data) {
          // 假设响应中包含下载链接
          this.displayedDownloadLink = response.data.data || response.data.downloadUrl || response.data
          this.showDownloadLink = true
          this.$message.success('下载链接已获取')
        } else {
          this.$message.error('获取下载链接失败: ' + (response.msg || '未知错误'))
        }
      }).catch(error => {
        console.error('获取下载链接失败:', error)
        this.$message.error('获取下载链接失败,请重试')
      })
    },
    /** 复制链接到剪贴板 */
    copyLink() {
      if (this.displayedDownloadLink) {
        // 创建临时文本区域
        const textarea = document.createElement('textarea')
        textarea.value = this.displayedDownloadLink
        textarea.style.position = 'fixed'
        textarea.style.opacity = '0'
        document.body.appendChild(textarea)
        textarea.select()
        try {
          document.execCommand('copy')
          this.$message.success('链接已复制到剪贴板')
        } catch (err) {
          this.$message.error('复制失败,请手动复制')
          console.error('Copy error:', err)
        } finally {
          document.body.removeChild(textarea)
        }
      }
    },
    /** 上传失败处理 */
    handleUploadError(err) {
      this.$message.error('上传失败,请重试')
      console.error('Upload error:', err)
    }
  }
}
    /** 提交按钮 */
</script>
src/views/archiveManager/arMAutoUser.vue
@@ -48,7 +48,7 @@
          v-hasPermi="['system:role:remove']"
        >批量取消授权</el-button>
      </el-col> -->
      <el-col :span="1.5">
    <!--  <el-col :span="1.5">
        <el-button
          type="warning"
          plain
@@ -56,7 +56,7 @@
          size="mini"
          @click="handleClose"
        >关闭</el-button>
      </el-col>
      </el-col> -->
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>
@@ -148,7 +148,7 @@
  created() {
    const roleId = this.$route.params && this.$route.params.roleId
    const recordId = this.$route.params && this.$route.params.archiveRecordsId
    if (roleId && recordId) {
      this.queryParams.roleId = roleId
      // 进行类型转换
@@ -164,7 +164,7 @@
      // 创建一个新对象,将字符串类型的archiveRecordsId转换为数字类型
      const params = {
        ...this.queryParams,
        archiveRecordsId: !isNaN(Number(this.queryParams.archiveRecordsId)) ?
        archiveRecordsId: !isNaN(Number(this.queryParams.archiveRecordsId)) ?
                    Number(this.queryParams.archiveRecordsId) : 0
      }
      listArchiverecordstouser(params).then(response => {
@@ -176,8 +176,9 @@
    /** 打开授权用户表弹窗 */
    openSelectUser() {
      // 确保传递给select组件的recordId是数字类型
      const recordId = !isNaN(Number(this.queryParams.archiveRecordsId)) ?
      const recordId = !isNaN(Number(this.queryParams.archiveRecordsId)) ?
                    Number(this.queryParams.archiveRecordsId) : 0
      this.$refs.select.show(recordId)
    },
    // 返回按钮
src/views/archiveManager/archiveMaterial/index.vue
@@ -2,8 +2,17 @@
    <div class="app-container">
         <h2 class="title-border">添加案卷详细记录</h2>
 <el-form ref="form" :model="form" :rules="rules" label-width="100px">
   <el-row>
     <el-col :span="8">
           <el-form-item label="文件编号" prop="documentNumber">
             <el-input ref="documentNumberInput" v-model="form.documentNumber" placeholder="请输入文件编号" />
           </el-form-item>
           </el-col>
   </el-row>
    <el-row>
    <el-col :span="8">
    <el-col :span="16">
          <el-form-item label="责任者" prop="creator">
            <el-input v-model="form.creator" placeholder="请输入责任者" />
          </el-form-item>
@@ -11,7 +20,7 @@
  </el-row>
  <el-row>
  <el-col :span="8">
  <el-col :span="16">
          <el-form-item label="文件题名" prop="title">
            <el-input v-model="form.title" placeholder="请输入文件题名" />
          </el-form-item>
@@ -42,20 +51,56 @@
              :value="item.value"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="页号"
        <el-form-item label="页号"
          prop="pageNumber"
          v-if=" form.fileStyle !== '其他材料'">
      <el-col :span="8">
  <el-input
    v-model.number="form.pageNumber"
    placeholder="请输入页号(数字类型)"
    type="number"
  <el-input
    v-model.number="form.pageNumber"
    placeholder="请输入页号(数字类型)"
    type="number"
    min="1"
    oninput="value=value.replace(/[^\d]/g,'')" style="width: 450px;"
  /></el-col>
</el-form-item>
          <el-form-item label="备注" prop="remarks">
              <el-col :span="8">
            <el-input v-model="form.remarks" placeholder="请输入备注" />
                </el-col>
          </el-form-item>
    <el-form-item label="公开属性" prop="publicity">
          <el-select
            v-model="form.publicity"
            placeholder="请选择公开属性"
            clearable
          >
            <el-option
              v-for="item in publicityOptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
        </el-form-item>
    <el-form-item label="是否为附件" prop="isAttachment">
          <el-select
            v-model="form.isAttachment"
            placeholder="请选择是否为附件"
          >
            <el-option
              v-for="item in isAttachmentOptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
        </el-form-item>
          <el-form-item label="所处阶段" prop="stage">
          <el-select
            v-model="form.stage"
@@ -69,13 +114,14 @@
            />
          </el-select>
        </el-form-item>
          <el-form-item label="公开属性" prop="publicity">
   <el-form-item label="密级" prop="securityLevel">
          <el-select
            v-model="form.publicity"
            placeholder="请选择公开属性"
            v-model="form.securityLevel"
            placeholder="请选择密级"
          >
            <el-option
              v-for="item in publicityOptions"
              v-for="item in securityLevelOptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
@@ -83,19 +129,6 @@
          </el-select>
        </el-form-item>
          <el-form-item label="是否为附件" prop="isAttachment">
          <el-select
            v-model="form.isAttachment"
            placeholder="请选择是否为附件"
          >
            <el-option
              v-for="item in isAttachmentOptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
        </el-form-item>
          <el-form-item label="保管期限" prop="retentionPeriod">
          <el-select
@@ -112,19 +145,7 @@
        </el-form-item>
      <!-- 对话框表单中的密级 -->
      <el-form-item label="密级" prop="securityLevel">
          <el-select
            v-model="form.securityLevel"
            placeholder="请选择密级"
          >
            <el-option
              v-for="item in securityLevelOptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
        </el-form-item>
@@ -200,6 +221,16 @@
        </el-col>
        <el-col :span="1.5">
          <el-button
            type="warning"
            plain
            icon="el-icon-download"
            size="mini"
            @click="handleExport"
          >导出</el-button>
        </el-col>
        <el-col :span="1.5">
          <el-button
            type="danger"
            plain
            icon="el-icon-delete"
@@ -209,16 +240,7 @@
            v-hasPermi="['system:materials:remove']"
          >删除</el-button>
        </el-col>
        <el-col :span="1.5">
          <el-button
            type="warning"
            plain
            icon="el-icon-download"
            size="mini"
            @click="handleExport"
            v-hasPermi="['system:materials:export']"
          >导出</el-button>
        </el-col>
          <el-col :span="1.5">
          <el-button
            type="success"
@@ -236,8 +258,8 @@
      class="upload-demo"
      ref="upload"
      :action="uploadUrl"
         :headers="headers"
               :data="uploadParams"
      :headers="headers"
      :data="uploadParams"
      :on-success="handleSuccess"
      :on-error="handleError"
      :on-preview="handlePreview"
@@ -288,7 +310,33 @@
      <el-table v-loading="loading" :data="materialsList" @selection-change="handleSelectionChange">
        <el-table-column type="selection" width="55" align="center" />
        <el-table-column label="操作" align="center" width="160" class-name="small-padding ">
              <template slot-scope="scope">
                <el-button
                  size="mini"
                  type="text"
                  icon="el-icon-edit"
                  @click="handleUpdate(scope.row)"
                  v-hasPermi="['system:materials:edit']"
                >修改</el-button>
        <!--        <el-button
                  size="mini"
                  type="text"
                  icon="el-icon-edit"
                  @click="handleUpdate(scope.row)"
                  v-hasPermi="['system:materials:edit']"
                >导入附件</el-button> -->
                <el-button
                  size="mini"
                  type="text"
                  icon="el-icon-delete"
                  @click="handleDelete(scope.row)"
                  v-hasPermi="['system:materials:remove']"
                >删除</el-button>
              </template>
            </el-table-column>
                <el-table-column label="文件材料序号" align="center" prop="fileNumber" />
        <el-table-column label="文件编号" align="center" prop="documentNumber" />
        <el-table-column label="责任者" align="center" prop="creator" />
        <el-table-column label="文件题名" align="center" prop="title" />
@@ -297,15 +345,18 @@
            <span>{{ parseTime(scope.row.date, '{y}-{m}-{d}') }}</span>
          </template>
        </el-table-column>
        <el-table-column label="页号" sortable align="center" prop="pageNumber"  />
                <el-table-column label="页次" sortable align="center" prop="pageOrder"  />
        <el-table-column label="页号" sortable align="center" prop="pageNumber"  />
        <el-table-column label="备注" align="center" prop="remarks" />
                <el-table-column label="页次" sortable align="center" prop="pageOrder"  />
        <el-table-column label="所处阶段" sortable align="center" prop="stage" />
        <el-table-column label="公开属性" sortable align="center" prop="publicity" />
        <el-table-column label="所处阶段" sortable align="center" width="180" prop="stage" />
        <el-table-column label="公开属性" sortable align="center" width="180" prop="publicity" />
        <el-table-column label="是否为附图及附件" align="center" prop="isAttachment" />
        <el-table-column label="保管期限" align="center" prop="retentionPeriod" />
        <el-table-column label="密级" align="center" prop="securityLevel" />
        <el-table-column label="材料类型" align="fileStyle" prop="fileStyle" />
        <el-table-column label="是否涉密及敏感信息" align="center" prop="isSensitive" />
        <el-table-column label="是否注销" align="center" prop="isCanceled" />
        <el-table-column label="格式" align="center" prop="format" />
@@ -317,31 +368,8 @@
        <el-table-column label="大小" align="center" prop="fileSize" />
        <el-table-column label="附件及历史发文号" align="center" prop="attachmentHistoryNumbers" />
        <el-table-column label="操作" align="center" width="160" class-name="small-padding ">
          <template slot-scope="scope">
            <el-button
              size="mini"
              type="text"
              icon="el-icon-edit"
              @click="handleUpdate(scope.row)"
              v-hasPermi="['system:materials:edit']"
            >修改</el-button>
            <el-button
              size="mini"
              type="text"
              icon="el-icon-edit"
              @click="handleUpdate(scope.row)"
              v-hasPermi="['system:materials:edit']"
            >导入附件</el-button>
            <el-button
              size="mini"
              type="text"
              icon="el-icon-delete"
              @click="handleDelete(scope.row)"
              v-hasPermi="['system:materials:remove']"
            >删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
@@ -354,18 +382,22 @@
      <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
        <el-form ref="forms" :model="forms" :rules="rules" label-width="100px">
          <el-form-item label="文件编号" prop="documentNumber">
              <el-input v-model="forms.documentNumber" placeholder="请输入文件编号" />
            </el-form-item>
           <el-row>
           <el-col :span="8">
           <el-col :span="16">
               <el-form-item label="责任者" prop="creator">
                   <el-input v-model="forms.creator" placeholder="请输入责任者" />
                 </el-form-item>
           </el-col>
               <el-col :span="8">
                 <el-form-item label="文件题名" prop="title">
                   <el-input v-model="forms.title" placeholder="请输入文件题名" />
                 </el-form-item>
           </el-col>
           </el-row>
           <el-form-item label="文件题名" prop="title">
             <el-input v-model="forms.title" placeholder="请输入文件题名" />
           </el-form-item>
                 <el-form-item label="日期" prop="date">
                  <el-input v-model="forms.date" placeholder="请输入日期 (格式:yyyy-mm-dd)" style="width: 200px;" />
                   <!-- <el-date-picker clearable
@@ -394,7 +426,9 @@
          </el-select>
        </el-form-item>
       <el-form-item label="备注" prop="remarks">
              <el-input v-model="forms.remarks" placeholder="请输入备注" />
            </el-form-item>
                 <el-form-item label="所处阶段" prop="stage">
                 <el-select
                   v-model="forms.stage"
@@ -706,7 +740,7 @@
      uploadUrl:  process.env.VUE_APP_BASE_API +'/system/materials/upload/'+this.recordId, // 替换为实际的上传接口
           headers: {
        Authorization: 'Bearer ' + getToken()
      },
      },
      uploadParams: {
        type: 'material' // 自定义上传类型
      },
@@ -722,8 +756,9 @@
        securityLevelOptions: [
          { value: '秘密', label: '秘密' },
          { value: '普通', label: '普通' },
                    { value: '该页另存', label: '该页另存' }
           { value: '该页另存', label: '该页另存' },
          { value: '内部用途', label: '内部用途' },
          { value: '内部用图', label: '内部用图' },
        ],
          // 添加保管期限选项
          retentionPeriodOptions: [
@@ -813,19 +848,22 @@
        // 表单校验
        rules: {
          creator: [
            { required: true, message: "创建者不能为空", trigger: "blur" }
           // { required: true, message: "创建者不能为空", trigger: "blur" }
          ],
          title: [
            { required: true, message: "案卷题名不能为空", trigger: "blur" }
          ],
          date: [
            { required: true, message: "日期不能为空", trigger: "blur" },
          //  { required: true, message: "日期不能为空", trigger: "blur" },
            { pattern: /^\d{4}-\d{2}-\d{2}$/, message: "日期格式必须为yyyy-mm-dd", trigger: "blur" }
          ],
          pageNumber: [
            { required: true, message: "页号不能为空", trigger: "blur" },
            { type: 'number', message: '页号必须为数字类型', trigger: ['blur', 'change'] },
            { pattern: /^\d+$/, message: '请输入正整数', trigger: 'blur' }
          ],
          stage: [
            { required: true, message: "所处阶段不能为空", trigger: "change" }
          ]
        }
      }
@@ -1069,11 +1107,69 @@
        console.log(this.form)
        this.form.recordId = this.recordId
        console.log("==============+++++++++++++++============")
        // 保存当前表单中需要保留的字段值
        const preservedValues = {
          documentNumber: this.form.documentNumber,
          creator: this.form.creator,
          title: this.form.title,
          date: this.form.date,
          pageNumber: this.form.pageNumber,
          remarks: this.form.remarks,
          stage: this.form.stage // 所处阶段保持跟上一条一致
        };
        this.$refs.form.validate(valid => {
        if (valid) {
        addMaterials(this.form).then(response => {
          this.$modal.msgSuccess("新增成功")
        //  this.opens = false
          // 创建新表单,保留特定字段,重置其他字段
          this.form = {
            materialId: null,
            recordId: this.recordId,
            // 保留的字段
            documentNumber: preservedValues.documentNumber,
            creator: preservedValues.creator,
            title: preservedValues.title,
            date: preservedValues.date,
            pageNumber: preservedValues.pageNumber,
            remarks: preservedValues.remarks,
            stage: preservedValues.stage,
            // 重置为空的字段
            publicity: null,
            // 重置为默认值的字段
            fileStyle: '文字材料', // 材料类型默认为文字材料
            securityLevel: '普通', // 密级默认为普通
            isAttachment: '否', // 是否为附件默认为否
            isSensitive: '否', // 是否敏感默认为否
            isCanceled: '否', // 是否注销默认为否
            // 其他需要重置的字段
            isDiagram: '否',
            retentionPeriod: '永久',
            format: null,
            sizeType: null,
            horizontalResolution: null,
            verticalResolution: null,
            width: null,
            height: null,
            fileSize: null,
            attachmentHistoryNumbers: null,
            createdAt: null,
            updatedAt: null
          };
          // 保存成功后,光标跳回文件编号输入框
          this.$nextTick(() => {
            this.$refs.documentNumberInput.focus();
          });
          this.getList()
        })
        }
        else
        {
          this.$message.error('请填写所有必填字段');
        }
        })
      },
      /** 提交按钮 */
@@ -1083,6 +1179,10 @@
            if (this.forms.materialId != null) {
              updateMaterials(this.forms).then(response => {
                this.$modal.msgSuccess("修改成功")
                // 修改成功后,光标跳回文件编号输入框
                this.$nextTick(() => {
                  this.$refs.documentNumberInput.focus();
                });
                this.open = false
                this.getList()
              })
@@ -1153,7 +1253,7 @@
    },
     /** 取消授权按钮操作 */
    cancelAuthUser(row) {
      const recordId =  this.recordId
      const recordId =  this.recordId
      var userId = store.state.user.id
      var _this = this
      // alert(userId)
@@ -1174,7 +1274,7 @@
        _this.$router.push('/archiveManager/infoManager')
        _this.$modal.msgSuccess("取消授权成功")
      })
      }).catch(() => {})
    },
    }
src/views/archiveManager/index.vue
@@ -287,6 +287,78 @@
    <el-table v-loading="loading" :data="recordsList" @selection-change="handleSelectionChange">
      <el-table-column type="selection" width="55" align="center" />
            <el-table-column label="操作" align="center" width="190" class-name="small-padding fixed-width">
              <template slot-scope="scope">
                <el-button
                v-if="scope.row.ownData"
                  size="mini"
                  type="text"
                  icon="el-icon-edit"
                  @click="handleInfo(scope.row)"
                  v-hasPermi="['system:records:edit']"
                >案卷详细信息</el-button>
                <el-button
                  size="mini"
                  type="text"
                  icon="el-icon-edit"
                  @click="handleCheck(scope.row)"
                >查看</el-button>
                <el-button
                  size="mini"
                  type="text"
                  icon="el-icon-printer"
                  @click="viewAndPrintExcel(scope.row)"
                  title="查看并打印Excel文件"
                >打印预整理清单</el-button>
          <el-button
                v-if="userId!==1&&scope.row.ownData"
                  size="mini"
                  type="text"
                  icon="el-icon-edit"
                  @click="handleSubmit(scope.row)"
                  v-hasPermi="['system:records:edit']"
                >提交案卷</el-button>
                <el-button
                v-if="scope.row.ownData||userId===1"
                  size="mini"
                  type="text"
                  icon="el-icon-edit"
                  @click="handleUpdate(scope.row)"
                  v-hasPermi="['system:records:edit']"
                >修改</el-button>
                <el-button
                  size="mini"
                  type="text"
                  icon="el-icon-delete"
                  @click="handleDelete(scope.row)"
                  v-hasPermi="['system:records:remove']"
                >删除</el-button>
                <el-dropdown size="mini" v-hasPermi="['system:records:remove']" @command="(command) => handleCommand(command, scope.row)">
                  <el-button size="mini" type="text" icon="el-icon-d-arrow-right">更多</el-button>
                  <el-dropdown-menu slot="dropdown">
                    <el-dropdown-item command="handleAuthUser" icon="el-icon-user"
                  >分配用户</el-dropdown-item>
                  </el-dropdown-menu>
                </el-dropdown>
              </template>
            </el-table-column>
            <el-table-column label="状态" align="center" prop="recordStatus" width="120">
              <template slot-scope="scope">
                <el-button :type="scope.row.recordStatus === '未录入' ? 'danger' : 'success'" size="mini">
                  {{ scope.row.recordStatus }}
                </el-button>
              </template>
            </el-table-column>
<!--      <el-table-column label="${comment}" align="center" prop="id" /> -->
      <el-table-column label="档案号" align="center" prop="recordId" />
      <el-table-column label="发文号" align="center" prop="inquiryNumber" />
@@ -313,69 +385,7 @@
      <el-table-column label="缩微号" align="center" prop="microfilmNumber" />
      <el-table-column label="备注" align="center" prop="remarks" />
      <el-table-column label="历史相关发文号" align="center" prop="historicalReferenceNumber" />
           <el-table-column label="状态" align="center" prop="recordStatus">
  <template slot-scope="scope">
    <el-button :type="scope.row.recordStatus === '未录入' ? 'danger' : 'success'" size="mini">
      {{ scope.row.recordStatus }}
    </el-button>
  </template>
</el-table-column>
      <el-table-column label="操作" align="center" width="190" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button
          v-if="scope.row.ownData"
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleInfo(scope.row)"
            v-hasPermi="['system:records:edit']"
          >案卷详细信息</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleCheck(scope.row)"
          >查看</el-button>
    <el-button
          v-if="userId!==1&&scope.row.ownData"
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleSubmit(scope.row)"
            v-hasPermi="['system:records:edit']"
          >提交案卷</el-button>
          <el-button
          v-if="scope.row.ownData||userId===1"
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['system:records:edit']"
          >修改</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['system:records:remove']"
          >删除</el-button>
          <el-dropdown size="mini" v-hasPermi="['system:records:remove']" @command="(command) => handleCommand(command, scope.row)">
            <el-button size="mini" type="text" icon="el-icon-d-arrow-right">更多</el-button>
            <el-dropdown-menu slot="dropdown">
              <el-dropdown-item command="handleAuthUser" icon="el-icon-user"
            >分配用户</el-dropdown-item>
            </el-dropdown-menu>
          </el-dropdown>
        </template>
      </el-table-column>
    </el-table>
    <pagination
@@ -385,6 +395,86 @@
      :limit.sync="queryParams.pageSize"
      @pagination="getList"
    />
    <!-- Excel操作弹窗 -->
    <el-dialog :visible.sync="excelDialogVisible" class="excel-dialog-overlay" @click="closeExcelDialog">
      <div class="excel-dialog" @click.stop>
        <div class="excel-dialog-header">
          <h3>Excel操作</h3>
                    <p class="excel-dialog-tip">请选择以下操作处理Excel文件</p>
          <!-- <button class="excel-dialog-close" @click="closeExcelDialog">×</button> -->
        </div>
        <div class="excel-dialog-body">
          <!-- 选择框区域 -->
          <div class="excel-options-section">
            <!-- 二维码选择 -->
            <div class="option-group">
              <label class="option-label">
                <input type="checkbox" v-model="excelOptions.includeQrCode">
                <span>包含二维码</span>
              </label>
            </div>
            <!-- 注解选择 -->
            <div class="option-group">
              <div class="option-label">选择注解:</div>
              <div class="annotation-options">
                <label v-for="i in 5" :key="i" class="annotation-checkbox">
                  <input type="checkbox" v-model="excelOptions.selectedAnnotations" :value="i">
                  <span>注解{{ i }}</span>
                </label>
              </div>
            </div>
            <!-- 签名栏选择 -->
            <div class="option-group">
              <div class="option-label">签名栏设置:</div>
              <div class="signature-options">
                <div class="signature-checkboxes">
                  <label v-for="i in 3" :key="i" class="signature-checkbox">
                    <input type="checkbox" v-model="excelOptions.selectedSignatures" :value="i">
                    <span>签名栏{{ i }}</span>
                  </label>
                </div>
<!--                <p v-if="excelOptions.selectedSignatures.length === 0" class="option-tip">请选择至少一个签名栏</p> -->
              </div>
            </div>
          </div>
        </div>
    <!-- 优化的Excel预览组件 -->
    <div class="excel-preview-wrapper" v-if="excelDialogVisible">
      <!-- 主要预览组件 -->
      <vue-office-excel
        v-if="currentExcelUrl"
        :src="currentExcelUrl"
        style="width: 100%; height: 500px;"
        @rendered="renderedHandler"
        @error="handleExcelRenderError"
      />
      <!-- 错误提示 -->
      <div v-if="excelRenderError" class="excel-error-message">
        <h3>Excel渲染失败</h3>
        <p>{{ excelRenderError }}</p>
        <p>请尝试以下解决方案:</p>
        <ul>
          <li>下载文件后使用Excel软件打开</li>
          <li>确认文件格式是否为支持的.xls或.xlsx</li>
        </ul>
        <button class="excel-dialog-btn download-btn" @click="handleDownloadClick">下载Excel文件</button>
      </div>
    </div>
        <div class="excel-dialog-footer">
          <button class="excel-dialog-btn preview-btn" @click="handlePreviewClick">预览</button>
          <button class="excel-dialog-btn print-btn" @click="handlePrintClick">打印</button>
          <button class="excel-dialog-btn download-btn" @click="handleDownloadClick">下载</button>
        </div>
      </div>
    </el-dialog>
    <!--  查看数据按钮  -->
    <el-dialog :title="title" :visible.sync="open_check" width="800px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
@@ -696,14 +786,24 @@
</template>
<script>
import VueOfficeExcel from '@vue-office/excel'
import '@vue-office/excel/lib/index.css'
import axios from 'axios'
import { getToken } from '@/utils/auth'
import { enload, batchSubmitRecords,updateStatusById,listRecords,getMaxId, getRecords, delRecords, addRecords, updateRecords } from "@/api/system/records"
import { listAllCategory } from "@/api/system/category"
import { listAllProjectName } from "@/api/system/projectName"
import { listPlaceName, listAllPlaceName } from "@/api/system/placeName"
export default {
     components: {
        VueOfficeExcel
    },
  name: "Records",
  data() {
    return {
                  excel: 'http://localhost:8080/profile/upload/12.xls', //设置文档网络地址,可以是相对地址
      open_check: false,
      securityLevelOptions: [
        { value: '秘密', label: '秘密' },
@@ -774,6 +874,21 @@
        remarks: null,
        historicalReferenceNumber: null
      },
    // Excel弹窗相关数据
    excelDialogVisible: false,
    currentExcelUrl: '',
    currentExcelBlob: null,
    currentExcelId: '',
    excelLoading: false,
    excelPreviewTimeout: null,
    excelRenderError: '', // Excel渲染错误信息
    // Excel选项配置
    excelOptions: {
      includeQrCode: false, // 是否包含二维码
      selectedAnnotations: [], // 选中的注解列表
      includeSignature: false, // 是否包含签名栏
      signaturePosition: 'bottom' // 签名栏位置:top/bottom
    },
      // 表单参数
      form: {
        recordSeq: '',
@@ -802,7 +917,8 @@
    }
  },
  created() {
    // 监听ESC键关闭弹窗
    document.addEventListener('keydown', this.handleKeyDown);
    this.userId = this.$store.state.user.id;
// 获取用户角色
@@ -832,7 +948,42 @@
      immediate: true
    }
  },
  beforeDestroy() {
    // 清理事件监听器和定时器
    document.removeEventListener('keydown', this.handleKeyDown);
    if (this.excelPreviewTimeout) {
      clearTimeout(this.excelPreviewTimeout);
    }
    if (this.currentExcelUrl) {
      URL.revokeObjectURL(this.currentExcelUrl);
    }
  },
  methods: {
      /** Excel渲染完成处理 */
        renderedHandler() {
            console.log("Excel渲染完成");
            this.excelLoading = false;
            this.excelRenderError = '';
        },
        /** Excel渲染错误处理 */
        handleExcelRenderError(error) {
            console.error("Excel渲染失败:", error);
            this.excelLoading = false;
            // 设置友好的错误信息
            if (error && error.message) {
                this.excelRenderError = error.message;
            } else {
                this.excelRenderError = '无法渲染此Excel文件,可能是文件格式不兼容或已损坏。';
            }
            // 尝试备选方案
            this.tryAlternativePreview();
        },
        /** 尝试备选预览方案 */
        tryAlternativePreview() {
            console.log('尝试备选预览方案...');
        },
    getId()
    {
      var _this = this;
@@ -849,19 +1000,19 @@
    // 生成案卷题名
    generateCaseTitle() {
      let titleParts = [];
      if (this.form.constructionUnit) {
        titleParts.push(this.form.constructionUnit);
      }
      if (this.form.constructionAddress) {
        titleParts.push(this.form.constructionAddress);
      }
      if (this.form.projectName) {
        titleParts.push(this.form.projectName);
      }
      this.form.caseTitle = titleParts.join('');
    },
    async getZoneOptions()
@@ -1057,7 +1208,7 @@
  .map(item => item.id);
          // 检查选中的ID是否全部属于自己
    const invalidIds = this.ids.filter(id => !myValidIds.includes(id));
    if (invalidIds.length > 0) {
      this.$message.error(`包含无权操作的ID: ${invalidIds.join(',')}`);
      return false;
@@ -1166,10 +1317,518 @@
    },
    /** 导出按钮操作 */
    handleExport() {
      this.download('system/records/export', {
        ...this.queryParams
      }, `records_${new Date().getTime()}.xlsx`)
      // 检查是否有选中的记录
      //alert(234)
     // alert(this.ids.length)
      if (this.ids && this.ids.length > 0) {
        // 只导出选中的记录
        this.download('system/records/export', {
          ids: this.ids.join(',') // 将选中的ID数组转换为逗号分隔的字符串
        }, `records_${new Date().getTime()}.xlsx`)
      } else {
        // 没有选中记录时,导出符合查询条件的所有记录
        this.download('system/records/export', {
          ...this.queryParams
        }, `records_${new Date().getTime()}.xlsx`)
      }
    },
    /** 查看并打印Excel文件 */
    viewAndPrintExcel(row) {
      const id = row.id || this.ids;
      this.$modal.loading('正在获取Excel文件,请稍候...');
      // 从后端获取Excel文件
      this.getExcelFile(id).then(blob => {
        this.$modal.closeLoading();
        // 保存文件信息并显示静态弹窗
        this.currentExcelUrl = URL.createObjectURL(blob);
        this.currentExcelBlob = blob;
        this.currentExcelId = id;
        this.excelDialogVisible = true;
        this.excelLoading = false;
      }).catch(error => {
        this.$modal.closeLoading();
        console.error('获取Excel文件失败:', error);
        this.$modal.msgError('获取Excel文件失败,请稍后重试');
      });
    },
    /** 处理键盘事件 - ESC键关闭弹窗 */
    handleKeyDown(e) {
      if (e.key === 'Escape' && this.excelDialogVisible) {
        this.closeExcelDialog();
      }
    },
    /** 关闭Excel弹窗并清理资源 */
    closeExcelDialog() {
      this.excelDialogVisible = false;
      if (this.excelPreviewTimeout) {
        clearTimeout(this.excelPreviewTimeout);
        this.excelPreviewTimeout = null;
      }
      if (this.currentExcelUrl) {
        URL.revokeObjectURL(this.currentExcelUrl);
        this.currentExcelUrl = '';
      }
      this.currentExcelBlob = null;
    },
    /** 处理预览按钮点击 */
    handlePreviewClick() {
      this.excelLoading = true;
      this.excelRenderError = '';
      // 重置vue-office-excel组件
      if (this.currentExcelUrl) {
        const tempUrl = this.currentExcelUrl;
        this.currentExcelUrl = '';
        this.$nextTick(() => {
          this.currentExcelUrl = tempUrl;
        });
      }
      const previewContainer = this.$refs.excelPreviewContainer;
      // 清空预览容器
      if (previewContainer) {
        previewContainer.innerHTML = '';
        // 尝试使用微软Office Online预览服务作为备选
        const officeOnlineUrl = `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(this.currentExcelUrl)}`;
        // 创建iframe用于预览
        const iframe = document.createElement('iframe');
        iframe.style.width = '100%';
        iframe.style.height = '100%';
        iframe.style.border = 'none';
        iframe.style.minHeight = '600px';
        iframe.src = officeOnlineUrl;
        iframe.title = 'Excel文件预览';
        // 创建加载指示器
        const loadingIndicator = document.createElement('div');
        loadingIndicator.className = 'loading-indicator';
        loadingIndicator.innerHTML = `
          <div class="loading-spinner"></div>
          <p>正在加载Excel文件,请稍候...</p>
        `;
        previewContainer.style.position = 'relative';
        previewContainer.appendChild(iframe);
        previewContainer.appendChild(loadingIndicator);
        // 监听iframe加载完成
        iframe.onload = () => {
          this.excelLoading = false;
          if (loadingIndicator && loadingIndicator.parentNode) {
            loadingIndicator.parentNode.removeChild(loadingIndicator);
          }
        };
        // 设置超时处理
        this.excelPreviewTimeout = setTimeout(() => {
          this.excelLoading = false;
          if (loadingIndicator && loadingIndicator.parentNode) {
            loadingIndicator.parentNode.removeChild(loadingIndicator);
          }
          // 尝试直接预览作为备选方案
          previewContainer.innerHTML = '';
          const directIframe = document.createElement('iframe');
          directIframe.style.width = '100%';
          directIframe.style.height = '100%';
          directIframe.style.border = 'none';
          directIframe.style.minHeight = '600px';
          directIframe.src = this.currentExcelUrl;
          const fallbackMessage = document.createElement('div');
          fallbackMessage.textContent = 'Office预览服务加载超时,正在尝试直接预览...';
          fallbackMessage.style.cssText = `
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            padding: 10px;
            background-color: #f56c6c;
            color: white;
            text-align: center;
            font-size: 14px;
            z-index: 10;
          `;
          previewContainer.appendChild(directIframe);
          previewContainer.appendChild(fallbackMessage);
          directIframe.onload = () => {
            if (fallbackMessage && fallbackMessage.parentNode) {
              fallbackMessage.parentNode.removeChild(fallbackMessage);
            }
          };
          // 二次超时处理
          setTimeout(() => {
            if (directIframe && directIframe.parentNode) {
              directIframe.parentNode.removeChild(directIframe);
            }
            const errorMessage = document.createElement('div');
            errorMessage.innerHTML = `
              <div style="text-align: center; padding: 40px;">
                <h3 style="color: #f56c6c; margin-bottom: 20px;">预览失败</h3>
                <p style="color: #666; margin-bottom: 30px;">无法在浏览器中预览Excel文件,请点击下方按钮下载文件后查看。</p>
                <button onclick="this.parentNode.parentNode.querySelector('.download-btn').click()"
                        style="padding: 8px 20px; background: #409EFF; color: white; border: none; border-radius: 4px; cursor: pointer;">
                  下载Excel文件
                </button>
              </div>
            `;
            previewContainer.appendChild(errorMessage);
          }, 20000);
        }, 25000);
      }
    },
    /** 处理打印按钮点击 */
    handlePrintClick() {
      if (this.currentExcelUrl) {
        // 创建一个临时iframe用于打印
        const iframe = document.createElement('iframe');
        iframe.style.position = 'absolute';
        iframe.style.left = '-9999px';
        iframe.style.top = '-9999px';
        iframe.style.width = '210mm';
        iframe.style.height = '297mm';
        document.body.appendChild(iframe);
        // 监听iframe加载完成
        iframe.onload = () => {
          try {
            // 执行打印
            iframe.contentWindow.print();
            // 设置超时移除iframe
            setTimeout(() => {
              if (iframe && iframe.parentNode) {
                iframe.parentNode.removeChild(iframe);
              }
            }, 1000);
          } catch (error) {
            console.error('Print error:', error);
            // 打印失败时,提示用户下载
            this.$confirm('打印失败,是否下载文件?', '提示', {
              confirmButtonText: '下载',
              cancelButtonText: '取消',
              type: 'warning'
            }).then(() => {
              this.handleDownloadClick();
            });
            // 移除iframe
            if (iframe && iframe.parentNode) {
              iframe.parentNode.removeChild(iframe);
            }
          }
        };
        // 设置iframe的src
        iframe.src = this.currentExcelUrl;
      }
    },
    /** 处理下载按钮点击 */
    handleDownloadClick() {
      if (this.currentExcelUrl) {
        // 获取用户选择的选项
        const { includeQrCode, selectedAnnotations } = this.excelOptions;
        console.log('下载选项:', { includeQrCode, selectedAnnotations });
        // 创建下载链接,文件名包含选项信息
        const link = document.createElement('a');
        link.href = this.currentExcelUrl;
        // 构建文件名,包含选项信息
        let filename = `record_${this.currentExcelId}`;
        if (includeQrCode) filename += '_qr';
        if (selectedAnnotations.length > 0) {
          filename += `_anno${selectedAnnotations.join('')}`;
        }
        filename += `_${new Date().getTime()}.xlsx`;
        link.download = filename;
        // 触发下载
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        // 提示下载成功
        this.$modal.msgSuccess('Excel文件下载成功');
      }
    },
    /** 检查浏览器是否可能支持直接预览Excel */
    canPreviewExcelInBrowser() {
      // 现在我们使用微软Office Online服务,对所有现代浏览器都支持预览
      return true;
    },
    /** 处理Excel预览超时情况 */
    handleExcelPreviewTimeout(url, iframe) {
      // 超时后,提供在新窗口打开的选项
      this.$confirm('Excel文件加载超时,是否在新窗口中打开?', '提示', {
        confirmButtonText: '打开',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        window.open(url, '_blank');
      }).finally(() => {
        // 清理:移除iframe并释放URL对象
        this.cleanupExcelPreview(iframe, url);
      });
    },
    /** 清理Excel预览相关资源 */
    cleanupExcelPreview(iframe, url) {
      setTimeout(() => {
        if (iframe && iframe.parentNode) {
          document.body.removeChild(iframe);
        }
        if (url) {
          URL.revokeObjectURL(url);
        }
        this.excelRenderError = '';
      }, 1000);
    },
    /** 从后端获取Excel文件 */
    getExcelFile(id) {
      return new Promise((resolve, reject) => {
        // 首先尝试使用axios(项目已有的HTTP客户端)
        try {
          axios({
            method: 'Post',
            url: `${process.env.VUE_APP_BASE_API}/system/records/model`,
            params: { id: id },
            responseType: 'blob',
            headers: { 'Authorization': 'Bearer ' + getToken() }
          }).then(response => {
            resolve(response.data);
          }).catch(error => {
            // 如果axios失败,回退到XMLHttpRequest
            console.warn('Axios请求失败,尝试使用XMLHttpRequest:', error);
            this.fallbackGetExcelFile(id, resolve, reject);
          });
        } catch (e) {
          // 捕获任何意外错误
          reject(new Error('获取Excel文件时发生错误: ' + e.message));
        }
      });
    },
    /** 备用的XMLHttpRequest获取Excel文件方法 */
    fallbackGetExcelFile(id, resolve, reject) {
      try {
        const xhr = new XMLHttpRequest();
        // 尝试不同的API路径,以适应可能的后端实现
        const apiUrls = [
          `${process.env.VUE_APP_BASE_API}/system/records/model?id=${id}`,
          `${process.env.VUE_APP_BASE_API}/system/records/model`
        ];
        let currentUrlIndex = 0;
        const tryNextUrl = () => {
          if (currentUrlIndex >= apiUrls.length) {
            reject(new Error('所有API路径都请求失败'));
            return;
          }
          const url = apiUrls[currentUrlIndex];
          currentUrlIndex++;
          xhr.open('GET', url, true);
          xhr.setRequestHeader('Authorization', 'Bearer ' + getToken());
          xhr.responseType = 'blob';
          xhr.onload = () => {
            if (xhr.status === 200) {
              // 验证返回的是否为有效的Blob
              if (xhr.response instanceof Blob && xhr.response.size > 0) {
                resolve(xhr.response);
              } else {
                console.warn('返回的数据不是有效的Blob,尝试下一个URL');
                tryNextUrl();
              }
            } else if (currentUrlIndex < apiUrls.length) {
              console.warn(`URL ${url} 请求失败 (${xhr.status}),尝试下一个URL`);
              tryNextUrl();
            } else {
              reject(new Error(`请求失败: ${xhr.status} ${xhr.statusText}`));
            }
          };
          xhr.onerror = () => {
            if (currentUrlIndex < apiUrls.length) {
              console.warn(`URL ${url} 网络错误,尝试下一个URL`);
              tryNextUrl();
            } else {
              reject(new Error('网络错误,无法获取Excel文件'));
            }
          };
          // 设置请求超时
          xhr.timeout = 30000; // 30秒
          xhr.ontimeout = () => {
            if (currentUrlIndex < apiUrls.length) {
              console.warn(`URL ${url} 请求超时,尝试下一个URL`);
              tryNextUrl();
            } else {
              reject(new Error('请求超时,无法获取Excel文件'));
            }
          };
          xhr.send();
        };
        // 开始尝试第一个URL
        tryNextUrl();
      } catch (e) {
        reject(new Error('XMLHttpRequest请求失败: ' + e.message));
      }
    },
    /** 批量打印Excel文件 */
    batchPrintExcel() {
      if (this.ids.length === 0) {
        this.$modal.msgWarning('请选择要打印的记录');
        return;
      }
      this.$confirm(`确定要打印选中的 ${this.ids.length} 条记录吗?`, '批量打印确认', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        // 对于批量操作,我们可以选择:
        // 1. 如果后端支持批量导出,直接调用批量导出接口
        this.$modal.loading('正在准备批量打印文件,请稍候...');
        // 尝试批量导出API
        this.getBatchExcelFile(this.ids).then(blob => {
          this.$modal.closeLoading();
          this.handleExcelBlobForPrint(blob, 'batch_records');
        }).catch(error => {
          this.$modal.closeLoading();
          console.warn('批量导出失败,尝试逐个处理:', error);
          // 2. 如果批量导出失败,提示用户逐个打印或下载
          this.$confirm('批量打印失败,是否下载所有文件?', '提示', {
            confirmButtonText: '下载',
            cancelButtonText: '取消',
            type: 'warning'
          }).then(() => {
            // 逐个下载文件
            this.downloadMultipleExcelFiles(this.ids);
          }).catch(() => {});
        });
      }).catch(() => {});
    },
    /** 获取批量Excel文件 */
    getBatchExcelFile(ids) {
      return new Promise((resolve, reject) => {
        try {
          if (typeof axios !== 'undefined') {
            axios({
              method: 'get',
              url: `${process.env.VUE_APP_BASE_API}/system/records/batchExport`,
              params: { ids: ids.join(',') },
              responseType: 'blob',
              headers: { 'Authorization': 'Bearer ' + this.getToken() }
            }).then(response => {
              resolve(response.data);
            }).catch(reject);
          } else {
            reject(new Error('批量导出功能不可用'));
          }
        } catch (e) {
          reject(e);
        }
      });
    },
    /** 处理Excel Blob用于打印 */
    handleExcelBlobForPrint(blob, prefix = 'record') {
      const url = URL.createObjectURL(blob);
      if (this.canPreviewExcelInBrowser()) {
        const iframe = document.createElement('iframe');
        iframe.style.display = 'none';
        document.body.appendChild(iframe);
        iframe.src = url;
        const timeoutId = setTimeout(() => {
          this.handleExcelPreviewTimeout(url, iframe);
        }, 5000);
        iframe.onload = () => {
          clearTimeout(timeoutId);
          this.$confirm('Excel文件已准备好,是否打印?', '打印确认', {
            confirmButtonText: '打印',
            cancelButtonText: '取消',
            type: 'warning'
          }).then(() => {
            try {
              iframe.contentWindow.print();
            } catch (e) {
              this.$modal.msgWarning('打印失败,将在新窗口打开文件');
              window.open(url, '_blank');
            } finally {
              this.cleanupExcelPreview(iframe, url);
            }
          }).catch(() => {
            window.open(url, '_blank');
            this.cleanupExcelPreview(iframe, url);
          });
        };
      } else {
        // 直接下载
        const link = document.createElement('a');
        link.href = url;
        link.download = `${prefix}_${new Date().getTime()}.xlsx`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);
      }
    },
    /** 下载多个Excel文件 */
    downloadMultipleExcelFiles(ids) {
      // 这里简单处理为逐个下载
      // 实际应用中可能需要考虑性能优化或使用压缩包
      ids.forEach((id, index) => {
        // 使用setTimeout避免浏览器限制同时下载
        setTimeout(() => {
          this.getExcelFile(id).then(blob => {
            const url = URL.createObjectURL(blob);
            const link = document.createElement('a');
            link.href = url;
            link.download = `record_${id}_${new Date().getTime()}.xlsx`;
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
            URL.revokeObjectURL(url);
          });
        }, index * 1000); // 每个文件间隔1秒下载
      });
    },
    // /** 获取Token的辅助方法 */
    // getToken() {
    //   return localStorage.getItem('token') || '';
    // },
         /** 导入模板下载操作*/
    handleExportTemplate()
    {
@@ -1218,7 +1877,7 @@
  });
    }
  }
@@ -1247,4 +1906,309 @@
::v-deep .el-table__body-wrapper::-webkit-scrollbar-thumb:hover {
  background: #2f91ec;
}
/* Excel操作弹窗样式 */
.excel-dialog-overlay {
  position: fixed;
  top: 0;
  left: 0;
}
/* Excel预览包装器样式 */
.excel-preview-wrapper {
  width: 100%;
  height: 100%;
  min-height: 500px;
  display: flex;
  flex-direction: column;
  background: #f9f9f9;
  border-radius: 8px;
  padding: 10px;
  margin-bottom: 20px;
}
/* Excel错误提示样式 */
.excel-error-message {
  background: #fff2f0;
  border: 1px solid #ffccc7;
  border-radius: 8px;
  padding: 20px;
  margin: 10px 0;
  color: #f56c6c;
}
.excel-error-message h3 {
  margin-top: 0;
  margin-bottom: 10px;
  color: #f56c6c;
  font-size: 16px;
}
.excel-error-message p {
  margin: 8px 0;
  line-height: 1.5;
}
.excel-error-message ul {
  text-align: left;
  margin: 10px 0;
}
.excel-error-message li {
  margin: 5px 0;
  padding-left: 5px;
}
/* 优化按钮样式 */
.excel-dialog-btn.download-btn {
  background: #67c23a;
  border-color: #67c23a;
  color: white;
  margin-top: 10px;
}
.excel-dialog-btn.download-btn:hover {
  background: #85ce61;
  border-color: #85ce61;
}
/* 选择框区域样式 */
.excel-options-section {
  background: #f5f7fa;
  border-radius: 8px;
  padding: 20px;
  margin-bottom: 20px;
}
/* 选项组样式 */
.option-group {
  margin-bottom: 15px;
}
.option-group:last-child {
  margin-bottom: 0;
}
/* 选项标签样式 */
.option-label {
  display: flex;
  align-items: center;
  font-size: 14px;
  color: #303133;
  cursor: pointer;
  margin-bottom: 10px;
}
/* 复选框样式 */
.option-label input[type="checkbox"] {
  width: 16px;
  height: 16px;
  margin-right: 8px;
  cursor: pointer;
}
/* 注解选项容器 */
.annotation-options {
  display: flex;
  flex-wrap: wrap;
  gap: 15px;
  margin-top: 10px;
}
/* 注解复选框样式 */
.annotation-checkbox {
  display: flex;
  align-items: center;
  font-size: 14px;
  color: #303133;
  cursor: pointer;
  background: white;
  padding: 8px 12px;
  border-radius: 4px;
  border: 1px solid #dcdfe6;
  transition: all 0.3s;
}
.annotation-checkbox:hover {
  border-color: #c0c4cc;
  background: #fafafa;
}
.annotation-checkbox input[type="checkbox"] {
  width: 14px;
  height: 14px;
  margin-right: 6px;
  cursor: pointer;
}
.excel-dialog {
  background-color: white;
  border-radius: 8px;
  width: 96%;
  max-width: 900px;
  max-height: 100vh;
  overflow: hidden;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
  animation: slideUp 0.3s ease;
  display: flex;
  flex-direction: column;
}
.excel-dialog-header {
  padding: 20px 24px;
  border-bottom: 1px solid #e9ecef;
  display: flex;
  justify-content: space-between;
  align-items: center;
  background-color: #f8f9fa;
}
.excel-dialog-header h3 {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
  color: #333;
}
.excel-dialog-close {
  background: none;
  border: none;
  font-size: 24px;
  cursor: pointer;
  color: #666;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 4px;
  transition: all 0.2s;
}
.excel-dialog-close:hover {
  background-color: #e9ecef;
  color: #333;
}
.excel-dialog-body {
  padding: 24px;
  flex: 1;
  overflow-y: auto;
}
.excel-preview-container {
  width: 100%;
  height: 400px;
  border: 1px solid #ddd;
  border-radius: 4px;
  margin-bottom: 16px;
  position: relative;
  background-color: #f8f9fa;
  overflow: hidden;
}
.loading-indicator {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: rgba(255, 255, 255, 0.9);
  color: #666;
  z-index: 10;
}
.loading-spinner {
  width: 40px;
  height: 40px;
  border: 3px solid #f3f3f3;
  border-top: 3px solid #2f91ec;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-bottom: 10px;
}
.excel-dialog-tip {
  margin: 0;
  color: #666;
  text-align: center;
  font-size: 14px;
}
.excel-dialog-footer {
  padding: 16px 24px;
  border-top: 1px solid #e9ecef;
  display: flex;
  justify-content: center;
  gap: 16px;
  background-color: #f8f9fa;
}
.excel-dialog-btn {
  padding: 10px 24px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  transition: all 0.2s;
  min-width: 100px;
}
.preview-btn {
  background-color: #2f91ec;
  color: white;
}
.preview-btn:hover {
  background-color: #1c7ed6;
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(47, 145, 236, 0.3);
}
.print-btn {
  background-color: #20c997;
  color: white;
}
.print-btn:hover {
  background-color: #17a589;
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(32, 201, 151, 0.3);
}
.download-btn {
  background-color: #6c757d;
  color: white;
}
.download-btn:hover {
  background-color: #5a6268;
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(108, 117, 125, 0.3);
}
@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes slideUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
</style>
src/views/archiveManager/seleUser.vue
@@ -111,7 +111,7 @@
      }
      this.queryParams.roleId = this.roleId
      this.queryParams.archiveRecordsId = this.archiveRecordsId
      this.getList()
      this.visible = true
    },
@@ -146,15 +146,15 @@
    handleSelectUser() {
      const archiveRecordsId = this.queryParams.archiveRecordsId
      const userIds = this.userIds.join(",")
      if (userIds == "") {
        this.$modal.msgError("请选择要分配的用户")
        return
      }
      // 创建转换后的参数,确保recordId是数字类型
      const convertedRecordId = !isNaN(Number(archiveRecordsId)) ? Number(archiveRecordsId) : 0
      authRecordToUser({ recordId: convertedRecordId, userIds: userIds }).then(res => {
        if(res.code === 200) {
          this.$modal.msgSuccess(res.msg)
src/views/system/user/index.vue
@@ -99,8 +99,8 @@
      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
        <el-row>
          <el-col :span="12">
            <el-form-item label="用户昵称" prop="nickName">
              <el-input v-model="form.nickName" placeholder="请输入用户昵称" maxlength="30" />
            <el-form-item label="用户姓名" prop="userName">
              <el-input v-model="form.userName" placeholder="请输入用户姓名" maxlength="30" />
            </el-form-item>
          </el-col>
@@ -165,7 +165,7 @@
              </el-select>
            </el-form-item>
          </el-col> -->
        </el-row>
        <el-row>
          <el-col :span="24">
@@ -490,6 +490,8 @@
    },
    /** 提交按钮 */
    submitForm: function() {
      console.log(this.form)
      console.log("---------")
      this.$refs["form"].validate(valid => {
        if (valid) {
          if (this.form.userId != undefined) {
src/views/system/user/profile/userInfo.vue
@@ -1,8 +1,8 @@
<template>
  <el-form ref="form" :model="form" :rules="rules" label-width="80px">
    <el-form-item label="用户名称" prop="nickName">
    <el-form-item label="用户昵称" prop="nickName">
      <el-input v-model="form.nickName" maxlength="30" />
    </el-form-item>
    </el-form-item>
    <el-form-item label="手机号码" prop="phonenumber">
      <el-input v-model="form.phonenumber" maxlength="11" />
    </el-form-item>
@@ -40,7 +40,7 @@
          { required: true, message: "用户昵称不能为空", trigger: "blur" }
        ],
        email: [
          { required: true, message: "邮箱地址不能为空", trigger: "blur" },
         // { required: true, message: "邮箱地址不能为空", trigger: "blur" },
          {
            type: "email",
            message: "请输入正确的邮箱地址",
@@ -48,7 +48,7 @@
          }
        ],
        phonenumber: [
          { required: true, message: "手机号码不能为空", trigger: "blur" },
         // { required: true, message: "手机号码不能为空", trigger: "blur" },
          {
            pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
            message: "请输入正确的手机号码",