spring
2025-02-21 1309d6a2a55fc1cf07de57da3891a8d3230b2db8
管理体系文件控制搬迁完成
已修改10个文件
已添加18个文件
4217 ■■■■■ 文件已修改
package.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/index.html 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/cnas/systemManagement/documentControl.js 277 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/stamps/主任.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/stamps/储能.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/stamps/射频.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/stamps/技术负责人.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/stamps/电力.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/stamps/综合室.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/stamps/装备.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/stamps/质量负责人.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/stamps/通信.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Preview/filePreview.vue 229 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/UpPdfStamp/index.vue 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/components/Sidebar/Item.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/tagsView.js 238 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/CNAS/systemManagement/documentControl/components/ControlledFileApplication.vue 571 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/CNAS/systemManagement/documentControl/components/DistributionCollectionRecord.vue 622 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/CNAS/systemManagement/documentControl/components/FileChangeRequest.vue 706 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/CNAS/systemManagement/documentControl/components/FileList.vue 436 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/CNAS/systemManagement/documentControl/components/FileObsoletionRequest.vue 496 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/CNAS/systemManagement/documentControl/index.vue 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/inspectionTask/inspection.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/materialOrderComponents/materialOrder/filesLookVisible.vue 73 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/structural/premises/index.vue 176 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/menu/index.vue 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vue.config.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json
@@ -37,6 +37,9 @@
  },
  "dependencies": {
    "@riophae/vue-treeselect": "0.4.0",
    "@vue-office/docx": "^1.6.3",
    "@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",
@@ -49,6 +52,7 @@
    "js-beautify": "1.13.0",
    "js-cookie": "3.0.1",
    "jsencrypt": "3.0.0-rc.1",
    "jspdf": "^3.0.0",
    "mammoth": "^1.9.0",
    "nprogress": "0.2.0",
    "print-js": "^1.6.0",
@@ -60,6 +64,7 @@
    "vue-barcode": "^1.3.0",
    "vue-count-to": "1.0.13",
    "vue-cropper": "0.5.5",
    "vue-demi": "^0.14.10",
    "vue-meta": "2.4.0",
    "vue-qr": "^4.0.9",
    "vue-router": "3.4.9",
public/index.html
@@ -227,4 +227,5 @@
  </body>
  <script src="<%= BASE_URL %>luckysheet/plugins/js/plugin.js"></script>
  <script src="<%= BASE_URL %>luckysheet/luckysheet.umd.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js"></script>
</html>
src/api/cnas/systemManagement/documentControl.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,277 @@
// ä½“系文件控制相关接口
import request from "@/utils/request";
// æ–‡ä»¶æ¸…单-附件上传
export function uploadFileManageDocumentList(data) {
  return request({
    url: "/manageDocumentList/uploadFileManageDocumentList",
    method: "post",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    data: data,
  });
}
//文件清单-列表
export function pageManageDocumentList(query) {
  return request({
    url: "/manageDocumentList/pageManageDocumentList",
    method: "get",
    params: query,
  });
}
//文件清单-删除
export function delManageDocumentList(query) {
  return request({
    url: "/manageDocumentList/delManageDocumentList",
    method: "delete",
    params: query,
  });
}
// æ–‡ä»¶æ¸…单-编辑
export function doManageDocumentList(data) {
  return request({
    url: "/manageDocumentList/doManageDocumentList",
    method: "post",
    data: data,
  });
}
// æ–‡ä»¶å—控-新增
export function addManageDocumentControlled(data) {
  return request({
    url: "/manageDocumentControlled/addManageDocumentControlled",
    method: "post",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    data: data,
  });
}
// æ–‡ä»¶å—控-修改
export function doManageDocumentControlled(data) {
  return request({
    url: "/manageDocumentControlled/doManageDocumentControlled",
    method: "post",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    data: data,
  });
}
//文件受控-获取pdf文件流
export function checkManageDocumentControlledPdf(query) {
  return request({
    url: "/manageDocumentControlled/checkManageDocumentControlledPdf",
    method: "get",
    headers: {
      responseType: "blob",
    },
    params: query,
  });
}
// æ–‡ä»¶å—控-审核
export function checkManageDocumentControlled(data) {
  return request({
    url: "/manageDocumentControlled/checkManageDocumentControlled",
    method: "post",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    data: data,
  });
}
//文件受控-删除
export function delManageDocumentControlled(query) {
  return request({
    url: "/manageDocumentControlled/delManageDocumentControlled",
    method: "delete",
    params: query,
  });
}
//文件受控-列表
export function pageManageDocumentControlled(query) {
  return request({
    url: "/manageDocumentControlled/pageManageDocumentControlled",
    method: "get",
    params: query,
  });
}
//文件作废-列表
export function pageManageDocumentCancel(query) {
  return request({
    url: "/manageDocumentCancel/pageManageDocumentCancel",
    method: "get",
    params: query,
  });
}
// æ–‡ä»¶å‘放回收-导出
export function exportManageDocumentIssueRecycle(query) {
  return request({
    url: "/manageDocumentIssueRecycle/exportManageDocumentIssueRecycle",
    method: "get",
    params: query,
  });
}
// æ–‡ä»¶å‘放回收-新增
export function addManageDocumentIssueRecycle(data) {
  return request({
    url: "/manageDocumentIssueRecycle/addManageDocumentIssueRecycle",
    method: "post",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    data: data,
  });
}
// æ–‡ä»¶å‘放回收-编辑
export function doManageDocumentIssueRecycle(data) {
  return request({
    url: "/manageDocumentIssueRecycle/doManageDocumentIssueRecycle",
    method: "post",
    data: data,
  });
}
// æ–‡ä»¶å‘放回收-审核
export function checkManageDocumentIssueRecycle(data) {
  return request({
    url: "/manageDocumentIssueRecycle/checkManageDocumentIssueRecycle",
    method: "post",
    data: data,
  });
}
// æ–‡ä»¶å‘放回收-列表
export function pageManageDocumentIssueRecycle(query) {
  return request({
    url: "/manageDocumentIssueRecycle/pageManageDocumentIssueRecycle",
    method: "get",
    params: query,
  });
}
//文件发放回收-列表
export function delManageDocumentIssueRecycle(query) {
  return request({
    url: "/manageDocumentIssueRecycle/delManageDocumentIssueRecycle",
    method: "delete",
    params: query,
  });
}
// æ–‡ä»¶å˜æ›´-导出
export function exportManageDocumentAlter(query) {
  return request({
    url: "/manageDocumentAlter/exportManageDocumentAlter",
    method: "get",
    headers: { responseType: "blob" },
    params: query,
  });
}
// æ–‡ä»¶å˜æ›´-新增
export function addManageDocumentAlter(data) {
  return request({
    url: "/manageDocumentAlter/addManageDocumentAlter",
    method: "post",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    data: data,
  });
}
// æ–‡ä»¶å˜æ›´-编辑
export function doManageDocumentAlter(data) {
  return request({
    url: "/manageDocumentAlter/doManageDocumentAlter",
    method: "post",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    data: data,
  });
}
// æ–‡ä»¶å˜æ›´-转换流
export function checkManageDocumentAlterPdf(query) {
  return request({
    url: "/manageDocumentAlter/checkManageDocumentAlterPdf",
    method: "get",
    headers: { responseType: "blob" },
    params: query,
  });
}
// æ–‡ä»¶å˜æ›´-审核
export function checkManageDocumentAlter(data) {
  return request({
    url: "/manageDocumentAlter/checkManageDocumentAlter",
    method: "post",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    data: data,
  });
}
//文件变更-删除
export function delManageDocumentAlter(query) {
  return request({
    url: "/manageDocumentAlter/delManageDocumentAlter",
    method: "delete",
    params: query,
  });
}
// æ–‡ä»¶å˜æ›´-列表
export function pageManageDocumentAlter(query) {
  return request({
    url: "/manageDocumentAlter/pageManageDocumentAlter",
    method: "get",
    params: query,
  });
}
// æ–‡ä»¶ä½œåºŸ-新增
export function addManageDocumentCancel(data) {
  return request({
    url: "/manageDocumentCancel/addManageDocumentCancel",
    method: "post",
    data: data,
  });
}
// æ–‡ä»¶ä½œåºŸ-编辑
export function doManageDocumentCancel(data) {
  return request({
    url: "/manageDocumentCancel/doManageDocumentCancel",
    method: "post",
    data: data,
  });
}
// æ–‡ä»¶ä½œåºŸ-审核
export function checkManageDocumentCancel(data) {
  return request({
    url: "/manageDocumentCancel/checkManageDocumentCancel",
    method: "post",
    data: data,
  });
}
//文件作废-导出
export function exportManageDocumentCancel(query) {
  return request({
    url: "/manageDocumentCancel/exportManageDocumentCancel",
    method: "get",
    headers: { responseType: "blob" },
    params: query,
  });
}
//文件作废-删除
export function delManageDocumentCancel(query) {
  return request({
    url: "/manageDocumentCancel/delManageDocumentCancel",
    method: "delete",
    params: query,
  });
}
src/assets/stamps/Ö÷ÈÎ.png
src/assets/stamps/´¢ÄÜ.png
src/assets/stamps/É䯵.png
src/assets/stamps/¼¼Êõ¸ºÔðÈË.png
src/assets/stamps/µçÁ¦.png
src/assets/stamps/×ÛºÏÊÒ.png
src/assets/stamps/×°±¸.png
src/assets/stamps/ÖÊÁ¿¸ºÔðÈË.png
src/assets/stamps/ͨÐÅ.png
src/components/Preview/filePreview.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,229 @@
<template>
  <div>
    <div v-if="isImage">
      <img :src="imgUrl" alt="Image Preview" />
    </div>
    <div v-if="isPdf">
      <object :data="fileUrl" type="application/pdf" width="100%" height="750px">
        <p>您的浏览器不支持 PDF é¢„览。<a :href="fileUrl">下载 PDF æ–‡ä»¶</a></p>
      </object>
    </div>
    <div v-if="isDoc">
      <p v-if="!isDocShow">文档无法直接预览,请下载查看。</p>
      <a :href="fileUrl" v-if="!isDocShow">下载文件</a>
      <vue-office-docx v-else :src="fileUrl" style="height: 100vh;" @rendered="renderedHandler" @error="errorHandler" />
    </div>
    <div v-if="isXls">
      <p v-if="!isDocShow">文档无法直接预览,请下载查看。</p>
      <a :href="fileUrl" v-if="!isDocShow">下载文件</a>
      <vue-office-excel v-else :src="fileUrl" :options="options" style="height: 100vh;" @rendered="renderedHandler"
        @error="errorHandler" />
    </div>
    <div v-if="isZipOrRar">
      <p>压缩文件无法直接预览,请下载查看。</p>
      <a :href="fileUrl">下载文件</a>
    </div>
    <div v-if="isCsv">
      <p v-if="csvList.length == 0">CSV æ–‡ä»¶æ— æ³•直接预览,请下载查看。</p>
      <a :href="fileUrl" v-if="csvList.length == 0">下载文件</a>
      <el-tabs type="border-card" v-if="csvList.length > 0" tab-position="bottom">
        <el-tab-pane :label="item.sheetName" v-for="(item, index) in csvList" :key="index">
          <el-table :data="item.tableData" height="75vh">
            <el-table-column :label="m.label" :prop="m.prop" v-for="(m, i) in item.column" :key="i" min-width="120px"
              show-overflow-tooltip>
              <template slot-scope="scope" slot="header">
                <div>
                  <el-tooltip :content="m.label" placement="top">
                    <div class="oneLine">
                      <span>{{ m.label }}</span>
                    </div>
                  </el-tooltip>
                </div>
              </template>
            </el-table-column>
          </el-table>
        </el-tab-pane>
      </el-tabs>
    </div>
    <div v-if="!isSupported">
      <p>不支持的文件格式</p>
    </div>
  </div>
</template>
<script>
import VueOfficeDocx from '@vue-office/docx'
//引入相关样式
import '@vue-office/docx/lib/index.css'
import VueOfficeExcel from '@vue-office/excel'
//引入相关样式
import '@vue-office/excel/lib/index.css'
export default {
  components: {
    VueOfficeDocx,
    VueOfficeExcel,
  },
  props: {
    fileUrl: {
      type: String,
      required: true
    },
    currentFile: {
      type: Object,
      required: true
    },
  },
  data() {
    return {
      isDocShow: true,
      options: {
        xls: false,       //预览xlsx文件设为false;预览xls文件设为true
        minColLength: 0,  // excel最少渲染多少列,如果想实现xlsx文件内容有几列,就渲染几列,可以将此值设置为0.
        minRowLength: 0,  // excel最少渲染多少行,如果想实现根据xlsx实际函数渲染,可以将此值设置为0.
        widthOffset: 10,  //如果渲染出来的结果感觉单元格宽度不够,可以在默认渲染的列表宽度上再加 Npx宽
        heightOffset: 10, //在默认渲染的列表高度上再加 Npx高
        beforeTransformData: (workbookData) => { return workbookData }, //底层通过exceljs获取excel文件内容,通过该钩子函数,可以对获取的excel文件内容进行修改,比如某个单元格的数据显示不正确,可以在此自行修改每个单元格的value值。
        transformData: (workbookData) => { return workbookData }, //将获取到的excel数据进行处理之后且渲染到页面之前,可通过transformData对即将渲染的数据及样式进行修改,此时每个单元格的text值就是即将渲染到页面上的内容
      },
      csvList: [],//csv文件数据
      imgUrl: ''
    }
  },
  computed: {
    isImage() {
      let state = /\.(jpg|jpeg|png|gif)$/i.test(this.fileUrl)
      this.imgUrl = this.fileUrl
      if (state) {
        this.imgUrl = this.fileUrl.replaceAll('word', 'img')
      }
      return state;
    },
    isPdf() {
      return /\.pdf$/i.test(this.fileUrl);
    },
    isDoc() {
      return /\.(doc|docx)$/i.test(this.fileUrl);
    },
    isXls() {
      let state = /\.(xls|xlsx)$/i.test(this.fileUrl)
      if (state) {
        if (/\.(xlsx)$/i.test(this.fileUrl)) {
          this.options.xls = false
        } else {
          this.options.xls = true
        }
      }
      return state;
    },
    isZipOrRar() {
      return /\.(zip|rar)$/i.test(this.fileUrl);
    },
    isCsv() {
      let state = /\.csv$/i.test(this.fileUrl)
      if (state) {
        this.loadCSVData();
        // this.main()
      }
      return state;
    },
    isSupported() {
      return this.isImage || this.isPdf || this.isDoc || this.isZipOrRar || this.isCsv || this.isXls;
    }
  },
  methods: {
    renderedHandler() {
      console.log("渲染完成")
      this.isDocShow = true
      this.resetStyle()
    },
    errorHandler() {
      console.log("渲染失败")
      this.isDocShow = false
    },
    async loadCSVData() {
      this.$axios.post(this.$api.insOrderPlan.preview, {
        id: this.currentFile.id,
      }).then(res => {
        let arr = res.data
        arr = arr.map(m => {
          let obj = {
            sheetName: m.sheetName,
            tableData: [],
            column: []
          }
          obj.tableData = this.formatCSVToTable(m.content.replaceAll('null', ' '))
          // .replaceAll('MIN','=MIN').replaceAll('MAX','=MAX').replaceAll('AVERAGE','=AVERAGE')
          for (let item in obj.tableData[0]) {
            obj.column.push({
              label: item,
              prop: item,
            })
          }
          return obj
        })
        this.csvList = arr
      }).catch(err => {
        console.log(err)
      })
    },
    formatCSVToTable(str) {
      const result = [];
      const jsonObj = str.split("\n");
      let arrHeader = [];
      for (const i in jsonObj) {
        if (typeof jsonObj[i] === 'string' && jsonObj[i].length > 0) {
          const row = `${jsonObj[i]}`;
          if (row.trim().length > 0) {
            const kv = jsonObj[i].split(',');
            if (i == 0) {
              // èŽ·å–column表头
              arrHeader = kv;
            } else {
              const obj = {};
              for (let index = 0; index < arrHeader.length; index++) {
                // ç»„装表格数据
                const name = String(arrHeader[index]);
                if (!arrHeader[index]) continue
                if (!obj[name]) {
                  try {
                    if (kv[index]) {
                      obj[name] = String(kv[index]);
                    } else {
                      obj[name] = '';
                    }
                  } catch (err) {
                    obj[name] = '';
                  }
                }
              }
              result.push(obj);
            }
          }
        }
      }
      return result
    },
    resetStyle() {
      const elements = document.querySelectorAll('[style*="pt"]');
      for (const element of elements) {
        const style = element.getAttribute('style');
        if (!!style) {
          element.setAttribute('style', style.replace(/pt/g, 'px'));
        }
      }
    },
  }
}
</script>
<style scoped>
img {
  max-width: 100%;
}
.oneLine {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
</style>
src/components/UpPdfStamp/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,165 @@
<template>
  <div class="up-pdf-stamp">
    <div class="work" style="display: flex;justify-content: space-between;align-items: center;margin-bottom: 10px;"
      v-if="isUpFile">
      <input type="file" @change="handleFileUpload" accept="application/pdf" ref="fileInput" />
    </div>
    <p style="color: red;font-size: 12px;margin: 16px 0;" v-if="canvasNumPages > 0">提示:在文件范围内,单击鼠标盖章,双击鼠标已盖好章处可删除当前章</p>
    <canvas ref="pdfCanvas" @click="e => handleCanvasClick(e, index)" style="border: 1px solid #000;"
      @dblclick="e => removeStamp(e, index)" v-for="(item, index) in canvasNumPages" :key="index"></canvas>
  </div>
</template>
<script>
import jsPDF from "jspdf";
export default {
  props: ['isUpFile'],
  data() {
    return {
      pdfDoc: null, // å­˜å‚¨ä¸Šä¼ çš„ PDF æ•°æ®
      stamps: [], // è®°å½•盖章的位置
      contextList: [],//canvas列表
      canvasNumPages: 0,// å­˜å‚¨ PDF æ€»é¡µæ•°
      stampWidth: 120, // ç›–章宽度
      stampHeight: 80, // ç›–章高度
      stampsName: '',
      stampsList: ['主任', '质量负责人', '技术负责人', '综合室', '通信', '电力', '装备', '储能', '射频'],
      fileName: '文件名'
    };
  },
  methods: {
    handleFileUpload(event) {
      const file = event.target.files[0];
      if (file.size > 20 * 1024 * 1024) {
        this.$refs.fileInput.value = ""; // æ¸…空文件输入框内容
        return this.$message.error('文件大小不能超过20M')
      }
      this.lookFile(file)
    },
    lookFile(file, currentStamp) {
      this.fileName = file.name
      if (currentStamp) {
        this.stampsName = currentStamp
      } else {
        const index = this.stampsList.indexOf(m => file.name.includes(m))
        if (index > -1) {
          this.stampsName = this.stampsList[index]
        } else {
          this.stampsName = '综合室'
        }
      }
      if (file && file.type === 'application/pdf') {
        const reader = new FileReader();
        reader.onload = (e) => {
          const typedArray = new Uint8Array(e.target.result);
          this.loadPDF(typedArray);
        };
        reader.readAsArrayBuffer(file);
      } else {
        this.$message.error('请选择 PDF æ–‡ä»¶');
      }
    },
    loadPDF(typedArray) {
      pdfjsLib.getDocument(typedArray).promise.then(pdfDoc_ => {
        this.pdfDoc = pdfDoc_;
        this.canvasNumPages = this.pdfDoc._pdfInfo.numPages
        this.stamps = []
        this.contextList = []
        for (let i = 1; i <= this.canvasNumPages; i++) {
          this.$nextTick(() => {
            this.renderPage(i); // æ¸²æŸ“页面
          });
          this.stamps.push([])
        }
      });
    },
    // æ¸²æŸ“指定页面
    renderPage(pageNum) {
      this.pdfDoc.getPage(pageNum).then(page => {
        const canvas = this.$refs.pdfCanvas[pageNum - 1];
        this.contextList.push(canvas.getContext("2d"))
        const viewport = page.getViewport({ scale: 1.5 });
        canvas.width = viewport.width;
        canvas.height = viewport.height;
        page.render({
          canvasContext: this.contextList[pageNum - 1],
          viewport: viewport
        }).promise.then(() => {
          this.stamps[pageNum - 1].forEach(m => {
            this.drawStamps(m.x, m.y, pageNum - 1)
          })
        });
      });
    },
    // å•击--添加章
    handleCanvasClick(event, i) {
      const x = event.offsetX;
      const y = event.offsetY;
      const index = this.stamps[i].findIndex(stamp => {
        let x0 = x - stamp.x;
        let y0 = y - stamp.y;
        return x0 > 0 && x0 < this.stampWidth && y0 > 0 && y0 < this.stampHeight;
      });
      if (index > -1) return;
      this.drawStamps(x, y, i)
      this.stamps[i].push({ x, y });
    },
    // åŒå‡»--删除盖章
    removeStamp(event, i) {
      const x = event.offsetX;
      const y = event.offsetY;
      // æŸ¥æ‰¾è¢«åŒå‡»çš„ç›–ç« 
      const index = this.stamps[i].findIndex(stamp => {
        let x0 = x - stamp.x;
        let y0 = y - stamp.y;
        return x0 > 0 && x0 < this.stampWidth && y0 > 0 && y0 < this.stampHeight;
      });
      if (index === -1) return;
      this.stamps[i].splice(index, 1); // åˆ é™¤æŒ‡å®šçš„ç›–ç« 
      this.contextList[i].clearRect(0, 0, this.contextList[i].width, this.contextList[i].height);
      this.renderPage(i + 1)
    },
    // æ¸²æŸ“ç« 
    drawStamps(x, y, index) {
      var img = new Image();
      console.log(this.stampsName)
      // è®¾ç½®å›¾ç‰‡æº
      img.src = require("@/assets/stamps/" + this.stampsName + ".png"); // æ›¿æ¢ä¸ºä½ çš„图片链接
      let that = this
      img.onload = function () {
        // å›¾ç‰‡åŠ è½½å®ŒæˆåŽï¼Œå°†å›¾ç‰‡ç»˜åˆ¶åˆ°canvas上
        that.contextList[index].drawImage(img, x, y, that.stampWidth, that.stampHeight);
      };
    },
    // ç”Ÿæˆ PDF çš„函数
    async generatePDF() {
      if (this.contextList.length === 0) {
        this.$message({ message: '请先上传PDF文件', type: 'error' });
        this.$emit('uploadPDFErr')
        return false
      }
      const pdf = new jsPDF("p", "mm", "a4");
      for (let i = 0; i < this.contextList.length; i++) {
        console.log(2222, this.contextList.length, this.$refs.pdfCanvas[i])
        const imgData = this.$refs.pdfCanvas[i].toDataURL('image/jpeg', 1.0);
        const pdfWidth = pdf.internal.pageSize.getWidth();
        const pdfHeight = (this.$refs.pdfCanvas[i].height * pdfWidth) / this.$refs.pdfCanvas[i].width;
        pdf.addImage(imgData, "JPEG", 0, 0, pdfWidth, pdfHeight); // å°†å›¾ç‰‡æ·»åŠ åˆ° PDF
        if (i !== this.contextList.length - 1) {
          pdf.addPage(); // æ·»åŠ æ–°çš„ä¸€é¡µ
        }
      }
      // å°† PDF æ–‡ä»¶ä¿å­˜æˆ–上传
      const pdfOutput = pdf.output('blob'); // èŽ·å– PDF æ–‡ä»¶çš„ Blob å¯¹è±¡
      // ä¸Šä¼ åˆ°åŽç«¯
      return this.$emit('uploadPDF', pdfOutput, this.fileName)
    },
  }
}
</script>
<style scoped></style>
src/layout/components/Sidebar/Item.vue
@@ -17,11 +17,12 @@
    const vnodes = []
    if (icon) {
      vnodes.push(<svg-icon icon-class={icon}/>)
      vnodes.push(<svg-icon icon-class={icon} />)
    }
    if (title) {
      if (title.length > 5) {
        // vnodes.push(<el-tooltip class="item" effect="dark" content={(title)} placement="top-end"><span slot='title' title={(title)}>{(title)}</span></el-tooltip>)
        vnodes.push(<span slot='title' title={(title)}>{(title)}</span>)
      } else {
        vnodes.push(<span slot='title'>{(title)}</span>)
src/main.js
@@ -65,7 +65,7 @@
};
Vue.prototype.javaApi = process.env.VUE_APP_BASE_API
  ? process.env.VUE_APP_BASE_API
  : "http://192.168.0.170:8002";
  : "http://192.168.1.36:8002";
Vue.prototype.checkPermi = checkPermi;
// å…¨å±€ç»„件挂载
src/store/modules/tagsView.js
@@ -1,228 +1,234 @@
const state = {
  visitedViews: [],
  cachedViews: [],
  iframeViews: []
}
  iframeViews: [],
};
const mutations = {
  ADD_IFRAME_VIEW: (state, view) => {
    if (state.iframeViews.some(v => v.path === view.path)) return
    if (state.iframeViews.some((v) => v.path === view.path)) return;
    state.iframeViews.push(
      Object.assign({}, view, {
        title: view.meta.title || 'no-name'
        title: view.meta.title || "no-name",
      })
    )
    );
  },
  ADD_VISITED_VIEW: (state, view) => {
    if (state.visitedViews.some(v => v.path === view.path)) return
    if (state.visitedViews.some((v) => v.path === view.path)) return;
    state.visitedViews.push(
      Object.assign({}, view, {
        title: view.meta.title || 'no-name'
        title: view.meta.title || "no-name",
      })
    )
    );
  },
  ADD_CACHED_VIEW: (state, view) => {
    if (state.cachedViews.includes(view.name)) return
    if (state.cachedViews.includes(view.name)) return;
    if (view.meta && !view.meta.noCache) {
      state.cachedViews.push(view.name)
      state.cachedViews.push(view.name);
    }
  },
  DEL_VISITED_VIEW: (state, view) => {
    for (const [i, v] of state.visitedViews.entries()) {
      if (v.path === view.path) {
        state.visitedViews.splice(i, 1)
        break
        state.visitedViews.splice(i, 1);
        break;
      }
    }
    state.iframeViews = state.iframeViews.filter(item => item.path !== view.path)
    state.iframeViews = state.iframeViews.filter(
      (item) => item.path !== view.path
    );
  },
  DEL_IFRAME_VIEW: (state, view) => {
    state.iframeViews = state.iframeViews.filter(item => item.path !== view.path)
    state.iframeViews = state.iframeViews.filter(
      (item) => item.path !== view.path
    );
  },
  DEL_CACHED_VIEW: (state, view) => {
    const index = state.cachedViews.indexOf(view.name)
    index > -1 && state.cachedViews.splice(index, 1)
    const index = state.cachedViews.indexOf(view.name);
    index > -1 && state.cachedViews.splice(index, 1);
  },
  DEL_OTHERS_VISITED_VIEWS: (state, view) => {
    state.visitedViews = state.visitedViews.filter(v => {
      return v.meta.affix || v.path === view.path
    })
    state.iframeViews = state.iframeViews.filter(item => item.path === view.path)
    state.visitedViews = state.visitedViews.filter((v) => {
      return v.meta.affix || v.path === view.path;
    });
    state.iframeViews = state.iframeViews.filter(
      (item) => item.path === view.path
    );
  },
  DEL_OTHERS_CACHED_VIEWS: (state, view) => {
    const index = state.cachedViews.indexOf(view.name)
    const index = state.cachedViews.indexOf(view.name);
    if (index > -1) {
      state.cachedViews = state.cachedViews.slice(index, index + 1)
      state.cachedViews = state.cachedViews.slice(index, index + 1);
    } else {
      state.cachedViews = []
      state.cachedViews = [];
    }
  },
  DEL_ALL_VISITED_VIEWS: state => {
  DEL_ALL_VISITED_VIEWS: (state) => {
    // keep affix tags
    const affixTags = state.visitedViews.filter(tag => tag.meta.affix)
    state.visitedViews = affixTags
    state.iframeViews = []
    const affixTags = state.visitedViews.filter((tag) => tag.meta.affix);
    state.visitedViews = affixTags;
    state.iframeViews = [];
  },
  DEL_ALL_CACHED_VIEWS: state => {
    state.cachedViews = []
  DEL_ALL_CACHED_VIEWS: (state) => {
    state.cachedViews = [];
  },
  UPDATE_VISITED_VIEW: (state, view) => {
    for (let v of state.visitedViews) {
      if (v.path === view.path) {
        v = Object.assign(v, view)
        break
        v = Object.assign(v, view);
        break;
      }
    }
  },
  DEL_RIGHT_VIEWS: (state, view) => {
    const index = state.visitedViews.findIndex(v => v.path === view.path)
    const index = state.visitedViews.findIndex((v) => v.path === view.path);
    if (index === -1) {
      return
      return;
    }
    state.visitedViews = state.visitedViews.filter((item, idx) => {
      if (idx <= index || (item.meta && item.meta.affix)) {
        return true
        return true;
      }
      const i = state.cachedViews.indexOf(item.name)
      const i = state.cachedViews.indexOf(item.name);
      if (i > -1) {
        state.cachedViews.splice(i, 1)
        state.cachedViews.splice(i, 1);
      }
      if(item.meta.link) {
        const fi = state.iframeViews.findIndex(v => v.path === item.path)
        state.iframeViews.splice(fi, 1)
      if (item.meta.link) {
        const fi = state.iframeViews.findIndex((v) => v.path === item.path);
        state.iframeViews.splice(fi, 1);
      }
      return false
    })
      return false;
    });
  },
  DEL_LEFT_VIEWS: (state, view) => {
    const index = state.visitedViews.findIndex(v => v.path === view.path)
    const index = state.visitedViews.findIndex((v) => v.path === view.path);
    if (index === -1) {
      return
      return;
    }
    state.visitedViews = state.visitedViews.filter((item, idx) => {
      if (idx >= index || (item.meta && item.meta.affix)) {
        return true
        return true;
      }
      const i = state.cachedViews.indexOf(item.name)
      const i = state.cachedViews.indexOf(item.name);
      if (i > -1) {
        state.cachedViews.splice(i, 1)
        state.cachedViews.splice(i, 1);
      }
      if(item.meta.link) {
        const fi = state.iframeViews.findIndex(v => v.path === item.path)
        state.iframeViews.splice(fi, 1)
      if (item.meta.link) {
        const fi = state.iframeViews.findIndex((v) => v.path === item.path);
        state.iframeViews.splice(fi, 1);
      }
      return false
    })
  }
}
      return false;
    });
  },
};
const actions = {
  addView({ dispatch }, view) {
    dispatch('addVisitedView', view)
    dispatch('addCachedView', view)
    dispatch("addVisitedView", view);
    dispatch("addCachedView", view);
  },
  addIframeView({ commit }, view) {
    commit('ADD_IFRAME_VIEW', view)
    commit("ADD_IFRAME_VIEW", view);
  },
  addVisitedView({ commit }, view) {
    commit('ADD_VISITED_VIEW', view)
    commit("ADD_VISITED_VIEW", view);
  },
  addCachedView({ commit }, view) {
    commit('ADD_CACHED_VIEW', view)
    commit("ADD_CACHED_VIEW", view);
  },
  delView({ dispatch, state }, view) {
    return new Promise(resolve => {
      dispatch('delVisitedView', view)
      dispatch('delCachedView', view)
    return new Promise((resolve) => {
      dispatch("delVisitedView", view);
      dispatch("delCachedView", view);
      resolve({
        visitedViews: [...state.visitedViews],
        cachedViews: [...state.cachedViews]
      })
    })
        cachedViews: [...state.cachedViews],
      });
    });
  },
  delVisitedView({ commit, state }, view) {
    return new Promise(resolve => {
      commit('DEL_VISITED_VIEW', view)
      resolve([...state.visitedViews])
    })
    return new Promise((resolve) => {
      commit("DEL_VISITED_VIEW", view);
      resolve([...state.visitedViews]);
    });
  },
  delIframeView({ commit, state }, view) {
    return new Promise(resolve => {
      commit('DEL_IFRAME_VIEW', view)
      resolve([...state.iframeViews])
    })
    return new Promise((resolve) => {
      commit("DEL_IFRAME_VIEW", view);
      resolve([...state.iframeViews]);
    });
  },
  delCachedView({ commit, state }, view) {
    return new Promise(resolve => {
      commit('DEL_CACHED_VIEW', view)
      resolve([...state.cachedViews])
    })
    return new Promise((resolve) => {
      commit("DEL_CACHED_VIEW", view);
      resolve([...state.cachedViews]);
    });
  },
  delOthersViews({ dispatch, state }, view) {
    return new Promise(resolve => {
      dispatch('delOthersVisitedViews', view)
      dispatch('delOthersCachedViews', view)
    return new Promise((resolve) => {
      dispatch("delOthersVisitedViews", view);
      dispatch("delOthersCachedViews", view);
      resolve({
        visitedViews: [...state.visitedViews],
        cachedViews: [...state.cachedViews]
      })
    })
        cachedViews: [...state.cachedViews],
      });
    });
  },
  delOthersVisitedViews({ commit, state }, view) {
    return new Promise(resolve => {
      commit('DEL_OTHERS_VISITED_VIEWS', view)
      resolve([...state.visitedViews])
    })
    return new Promise((resolve) => {
      commit("DEL_OTHERS_VISITED_VIEWS", view);
      resolve([...state.visitedViews]);
    });
  },
  delOthersCachedViews({ commit, state }, view) {
    return new Promise(resolve => {
      commit('DEL_OTHERS_CACHED_VIEWS', view)
      resolve([...state.cachedViews])
    })
    return new Promise((resolve) => {
      commit("DEL_OTHERS_CACHED_VIEWS", view);
      resolve([...state.cachedViews]);
    });
  },
  delAllViews({ dispatch, state }, view) {
    return new Promise(resolve => {
      dispatch('delAllVisitedViews', view)
      dispatch('delAllCachedViews', view)
    return new Promise((resolve) => {
      dispatch("delAllVisitedViews", view);
      dispatch("delAllCachedViews", view);
      resolve({
        visitedViews: [...state.visitedViews],
        cachedViews: [...state.cachedViews]
      })
    })
        cachedViews: [...state.cachedViews],
      });
    });
  },
  delAllVisitedViews({ commit, state }) {
    return new Promise(resolve => {
      commit('DEL_ALL_VISITED_VIEWS')
      resolve([...state.visitedViews])
    })
    return new Promise((resolve) => {
      commit("DEL_ALL_VISITED_VIEWS");
      resolve([...state.visitedViews]);
    });
  },
  delAllCachedViews({ commit, state }) {
    return new Promise(resolve => {
      commit('DEL_ALL_CACHED_VIEWS')
      resolve([...state.cachedViews])
    })
    return new Promise((resolve) => {
      commit("DEL_ALL_CACHED_VIEWS");
      resolve([...state.cachedViews]);
    });
  },
  updateVisitedView({ commit }, view) {
    commit('UPDATE_VISITED_VIEW', view)
    commit("UPDATE_VISITED_VIEW", view);
  },
  delRightTags({ commit }, view) {
    return new Promise(resolve => {
      commit('DEL_RIGHT_VIEWS', view)
      resolve([...state.visitedViews])
    })
    return new Promise((resolve) => {
      commit("DEL_RIGHT_VIEWS", view);
      resolve([...state.visitedViews]);
    });
  },
  delLeftTags({ commit }, view) {
    return new Promise(resolve => {
      commit('DEL_LEFT_VIEWS', view)
      resolve([...state.visitedViews])
    })
    return new Promise((resolve) => {
      commit("DEL_LEFT_VIEWS", view);
      resolve([...state.visitedViews]);
    });
  },
}
};
export default {
  namespaced: true,
  state,
  mutations,
  actions
}
  actions,
};
src/views/CNAS/systemManagement/documentControl/components/ControlledFileApplication.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,571 @@
<template>
  <!-- æ–‡ä»¶å—控申请 -->
  <div class="controlled-file-application" style="height: 100%;">
    <div class="search">
      <div class="search_thing">
        <div class="search_label">申请文件编号:</div>
        <div class="search_input"><el-input size="small" placeholder="请输入" clearable v-model="queryParams.documentCode"
            @keyup.enter.native="refreshTable()"></el-input></div>
      </div>
      <!-- <div class="search_thing">
        <div class="search_label">申请人:</div>
        <div class="search_input"><el-input size="small" placeholder="请输入" clearable
            v-model="queryParams.createUserName" @keyup.enter.native="refreshTable()"></el-input></div>
      </div> -->
      <div class="search_thing" style="padding-left: 30px;">
        <el-button size="small" @click="refresh()">重 ç½®</el-button>
        <el-button size="small" type="primary" @click="refreshTable()">查 è¯¢</el-button>
      </div>
      <div class="btns" style="padding-left: 30px;">
        <el-button size="small" type="primary"
          @click="addDialogVisible = true, addInfo = {}, file = null">文件受控申请</el-button>
      </div>
    </div>
    <div class="table">
      <lims-table :tableData="tableData" :column="column" :page="page" :tableLoading="tableLoading"
        :height="'calc(100vh - 290px)'" @pagination="pagination"></lims-table>
    </div>
    <el-dialog title="文件受控申请" :visible.sync="addDialogVisible" width="800px" top="10vh">
      <el-row>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label"><span style="color:red;margin-right: 4px;">*</span>申请编号:</div>
            <div class="search_input"><el-input size="small" placeholder="请输入" clearable
                v-model="addInfo.documentCode"></el-input></div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">责任人:</div>
            <div class="search_input">
              <el-select v-model="addInfo.dutyUser" size="small" style="width: 100%;" filterable>
                <el-option v-for="item in personList" :key="item.value" :label="item.label" :value="item.value">
                </el-option>
              </el-select>
            </div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">文件类别:</div>
            <div class="search_input">
              <el-select v-model="addInfo.type" size="small" style="width: 100%;">
                <el-option v-for="item in fileType" :key="item.value" :label="item.label" :value="item.value">
                </el-option>
              </el-select>
            </div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">文件名称:</div>
            <div class="search_input"><el-input size="small" placeholder="请输入" clearable
                v-model="addInfo.name"></el-input></div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">文件版本:</div>
            <div class="search_input"><el-input size="small" placeholder="请输入" clearable
                v-model="addInfo.version"></el-input></div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">上传附件:</div>
            <div class="search_input"><el-upload style="margin: 8px 0 0px 50px;" action="#" :auto-upload="false"
                :multiple="false" accept='.pdf' :on-change="handleChangeUpload" v-if="addDialogVisible">
                <el-button size="small" type="primary">上传附件</el-button>
              </el-upload></div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">作者:</div>
            <div class="search_input">
              <!-- <el-input size="small" placeholder="请输入" clearable v-model="addInfo.writer"></el-input> -->
              <el-select v-model="addInfo.writer" size="small" style="width: 100%;" filterable>
                <el-option v-for="item in personList" :key="item.value" :label="item.label" :value="item.label">
                </el-option>
              </el-select>
            </div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">提交日期:</div>
            <div class="search_input">
              <el-date-picker v-model="addInfo.submitDate" type="date" size="small" placeholder="选择日期"
                format="yyyy-MM-dd" value-format="yyyy-MM-dd" style="width: 100%;">
              </el-date-picker>
            </div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">说明:</div>
            <div class="search_input"><el-input size="small" placeholder="请输入" clearable v-model="addInfo.instructions"
                type="textarea" :rows="2"></el-input></div>
          </div>
        </el-col>
      </el-row>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addDialogVisible = false">取 æ¶ˆ</el-button>
        <el-button type="primary" @click="handleAdd" :loading="addLoading">ç¡® å®š</el-button>
      </span>
    </el-dialog>
    <el-dialog title="查看附件" :visible.sync="lookDialogVisible" width="800px" top="5vh" fullscreen>
      <filePreview v-if="lookDialogVisible" :fileUrl="javaApi + '/word/' + currentInfo.url" :currentFile="{}"
        style="height: 90vh;overflow-y: auto;" />
    </el-dialog>
    <el-dialog title="审核" :visible.sync="checkDialogVisible" width="1000px" top="5vh">
      <UpPdfStamp ref="UpPdfStamp" v-if="checkDialogVisible" @uploadPDF="uploadPDF" :isUpFile="false"></UpPdfStamp>
      <span slot="footer" class="dialog-footer">
        <el-button @click="handleCheckSub('不通过')" :loading="noCheckLoading">不通过</el-button>
        <el-button type="primary" @click="handleCheckSub('通过')" :loading="checkLoading">通 è¿‡</el-button>
      </span>
    </el-dialog>
    <el-dialog title="选择受控章" :visible.sync="checkStampDialogVisible" width="600px" top="5vh">
      <div class="stamp-list">
        <img :src="require('@/assets/stamps/' + item + '.png')" alt="" v-for="(item, index) in stampsList" :key="index"
          style="width: 120px;height: 80px;margin: 6px;" class="stamp" :class="{ active: currentStamp == item }"
          @click="currentStamp = item">
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="checkStampDialogVisible = false">取 æ¶ˆ</el-button>
        <el-button type="primary" @click="handleCheck0(currentInfo)">ç¡® å®š</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
import UpPdfStamp from '@/components/UpPdfStamp/index.vue'
import filePreview from '@/components/Preview/filePreview.vue'
import limsTable from "@/components/Table/lims-table.vue";
import {
  selectUserCondition,
} from "@/api/business/inspectionTask.js";
import {
  addManageDocumentControlled,
  doManageDocumentControlled,
  checkManageDocumentControlledPdf,
  delManageDocumentControlled,
  pageManageDocumentControlled,
  checkManageDocumentControlled,
} from '@/api/cnas/systemManagement/documentControl.js'
import { mapGetters } from "vuex";
export default {
  components: {
    filePreview,
    UpPdfStamp,
    limsTable
  },
  computed: {
    ...mapGetters(["nickName"]),
  },
  data() {
    return {
      upLoading: false,
      addPower: false,
      addDialogVisible: false,
      addLoading: false,
      lookDialogVisible: false,
      checkDialogVisible: false,
      checkStampDialogVisible: false,
      addInfo: {},
      personList: [],
      fileType: [],
      file: null,
      currentInfo: {},
      checkLoading: false,
      noCheckLoading: false,
      type: '',
      stampsList: ['主任', '质量负责人', '技术负责人', '综合室', '通信', '电力', '装备', '储能', '射频'],
      currentStamp: '主任',
      queryParams: {},
      tableData: [],
      column: [
        { label: "申请文件编号", prop: "documentCode" },
        {
          label: "文件类别", prop: "type", width: "120px", dataType: "tag",
          formatData: (params) => {
            return this.fileType.find((m) => m.value == params).label;
          },
          formatType: (params) => {
            return this.fileType.find((m) => m.value == params).type;
          },
        },
        {
          label: "申请人",
          prop: "createUserName",
        },
        { label: "申请时间", prop: "createTime" },
        { label: "说明", prop: "instructions" },
        { label: "提交日期", prop: "submitDate" },
        { label: "责任人", prop: "dutyUserName" },
        {
          label: "申请状态", prop: "state", dataType: "tag",
          formatData: (params) => {
            return params;
          },
          formatType: (params) => {
            if (params == '通过') {
              return 'success'
            } else {
              return 'danger'
            }
          },
        },
        {
          dataType: "action",
          fixed: "right",
          label: "操作",
          operation: [
            {
              name: "编辑",
              type: "text",
              clickFun: (row) => {
                this.handleUpdate(row);
              },
              disabled: (row) => {
                return row.state == '通过'
              }
            },
            {
              name: "审核",
              type: "text",
              clickFun: (row) => {
                this.handleCheck(row);
              },
              disabled: (row) => {
                return !row.dutyUserName.includes(this.nickName) || row.state == '通过'
              }
            },
            {
              name: "查看附件",
              type: "text",
              clickFun: (row) => {
                this.handleLook(row);
              },
            },
            {
              name: "下载",
              type: "text",
              clickFun: (row) => {
                this.handleDown(row);
              },
            },
            {
              name: "删除",
              type: "text",
              clickFun: (row) => {
                this.handleDelete(row);
              },
              disabled: (row, index) => {
                return row.state == '通过'
              }
            },
          ],
        },
      ],
      page: {
        total: 0,
        size: 10,
        current: 0,
      },
      tableLoading: false,
    }
  },
  mounted() {
    this.getList()
    this.getAuthorizedPerson()
    this.selectEnumByCategory()
  },
  methods: {
    getList() {
      this.tableLoading = true;
      let param = { ...this.queryParams, ...this.page };
      delete param.total;
      pageManageDocumentControlled({ ...param })
        .then((res) => {
          this.tableLoading = false;
          if (res.code === 200) {
            this.tableData = res.data.records;
            this.page.total = res.data.total;
          }
        })
        .catch((err) => {
          this.tableLoading = false;
        });
    },
    pagination({ page, limit }) {
      this.page.current = page;
      this.page.size = limit;
      this.getList();
    },
    refreshTable() {
      this.page.current = 1;
      this.getList();
    },
    refresh() {
      this.queryParams = {};
      this.page.current = 1;
      this.getList();
    },
    getPower() {
      let power = JSON.parse(sessionStorage.getItem('power'))
      let up = false
      let del = false
      let add = false
      // let check = false
      for (var i = 0; i < power.length; i++) {
        if (power[i].menuMethod == 'addManageDocumentControlled') {
          up = true
        }
        if (power[i].menuMethod == 'addManageDocumentControlled') {
          add = true
        }
        if (power[i].menuMethod == 'delManageDocumentControlled') {
          del = true
        }
        // if (power[i].menuMethod == 'checkManageDocumentControlled') {
        //   check = true
        // }
      }
      // if (!check) {
      //   this.componentData.do.splice(2, 1)
      // }
      if (!del) {
        this.componentData.do.splice(1, 1)
      }
      if (!up) {
        this.componentData.do.splice(0, 1)
      }
      this.addPower = add
    },
    getAuthorizedPerson() {
      selectUserCondition().then(res => {
        let data = []
        res.data.forEach(a => {
          data.push({
            label: a.name,
            value: a.id
          })
        })
        this.personList = data
      })
    },
    selectEnumByCategory() {
      // æ–‡ä»¶ç±»åˆ«
      this.getDicts("document_type").then((response) => {
        this.fileType = this.dictToValue(response.data);
      });
    },
    // æäº¤
    handleAdd() {
      if (!this.addInfo.documentCode) return this.$message({ type: 'error', message: "请输入编号" })
      if (!this.addInfo.id) {
        // æ–°å¢ž
        let fd = new FormData();
        //文件信息中raw才是真的文件
        if (this.file) {
          fd.append("file", this.file.raw);
        }
        for (let m in this.addInfo) {
          fd.append(m, this.addInfo[m])
        }
        this.addLoading = true
        addManageDocumentControlled(fd).then(res => {
          this.addLoading = false
          if (res.code == 200) {
            this.$message({
              type: 'success',
              message: '添加成功'
            })
            this.refreshTable()
            this.addDialogVisible = false
          } else {
            this.$message({
              type: 'error',
              message: '添加失败'
            })
          }
        })
      } else {
        // ä¿®æ”¹
        let { id, documentCode, dutyUser, type, name, version, writer, submitDate, instructions } = this.addInfo
        let fd = new FormData();
        //文件信息中raw才是真的文件
        if (this.file) {
          fd.append("file", this.file.raw);
        }
        fd.append("id", id);
        fd.append("documentCode", documentCode);
        fd.append("dutyUser", dutyUser);
        fd.append("type", type);
        fd.append("name", name);
        fd.append("version", version);
        fd.append("writer", writer);
        fd.append("submitDate", submitDate);
        fd.append("instructions", instructions);
        this.addLoading = true
        doManageDocumentControlled(fd).then(res => {
          this.addLoading = false
          if (res.code == 200) {
            this.refreshTable()
            this.addDialogVisible = false
          } else {
            this.$message({
              type: 'error',
              message: '添加失败'
            })
          }
        })
      }
    },
    handleChangeUpload(file, fileLists) {
      this.file = file
      this.$set(this.addInfo, 'name', file.name)
    },
    // ç¼–辑
    handleUpdate(row) {
      this.title = '文件变更申请'
      this.addInfo = this.HaveJson(row)
      this.addDialogVisible = true
    },
    // æŸ¥çœ‹é™„ä»¶
    handleLook(row) {
      this.currentInfo = row
      this.lookDialogVisible = true
    },
    // å®¡æ ¸
    handleCheck(row) {
      this.title = '审核'
      this.currentInfo = row
      if (!row.url) return this.$message.warning('文件未上传')
      this.checkStampDialogVisible = true
    },
    handleCheck0(row) {
      this.checkStampDialogVisible = false
      this.checkDialogVisible = true
      checkManageDocumentControlledPdf({ id: row.id }).then(res => {
        const blob = new Blob([res]);
        const file = new File([blob], row.name, { type: 'application/pdf' })
        this.$refs.UpPdfStamp.lookFile(file, this.currentStamp)
      }).catch(err => {
        console.log(err)
      })
    },
    handleDown(row) {
      if (!row.url) return this.$message.warning('文件未上传')
      let url = this.javaApi + '/word/' + row.url
      this.$download.saveAs(url, row.url);
    },
    async uploadPDF(pdfBlob) {
      const formData = new FormData();
      formData.append('file', pdfBlob, this.fileName + '.pdf'); // æ–‡ä»¶å­—段
      formData.append('id', this.currentInfo.id); // æ–‡ä»¶åå­—段
      formData.append('state', this.type); // æ–‡ä»¶åå­—段
      formData.append('writer', this.currentInfo.writer); // æ–‡ä»¶åå­—段
      let res = await checkManageDocumentControlled(formData)
      this.checkLoading = false
      this.noCheckLoading = false
      if (res.code == 200) {
        this.$message({ message: '操作成功', type: 'success' });
        this.checkDialogVisible = false;
        this.refreshTable()
        return true
      } else {
        this.$message({ message: '操作失败', type: 'error' });
        return false
      }
    },
    handleCheckSub(type) {
      this.type = type
      if (type == '通过') {
        this.checkLoading = true
      } else {
        this.noCheckLoading = true
      }
      this.addLoading = true
      this.$refs['UpPdfStamp'].generatePDF()
    },
    handleDelete(row) {
      this.$confirm("是否删除该条数据?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          delManageDocumentControlled({ id: row.id }).then((res) => {
            if (res.code == 201) return;
            this.$message.success("删除成功");
            this.refresh();
          });
        })
        .catch(() => { });
    },
  }
}
</script>
<style scoped>
.title {
  height: 60px;
  line-height: 60px;
}
.search {
  background-color: #fff;
  height: 40px;
  display: flex;
  align-items: center;
  position: relative;
}
.search_thing {
  width: 350px;
  display: flex;
  align-items: center;
}
.search_label {
  width: 110px;
  font-size: 14px;
  text-align: right;
}
.search_input {
  width: calc(100% - 110px);
}
.table {
  background-color: #fff;
  height: calc(100% - 60px - 80px);
  padding: 20px;
}
.btns {
  position: absolute;
  right: 20px;
  top: 5px;
}
.stamp {
  cursor: pointer;
  border: #fff 1px solid;
}
.stamp:hover {
  border: #3A7BFA 1px solid;
  box-shadow: inset 0px 0px 15px #3A7BFA;
}
.stamp.active {
  border: #3A7BFA 1px solid;
  box-shadow: inset 0px 0px 15px #3A7BFA;
}
</style>
src/views/CNAS/systemManagement/documentControl/components/DistributionCollectionRecord.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,622 @@
<template>
  <!-- å‘放回收记录 -->
  <div class="distribution-collection-record" style="height: 100%;">
    <div class="search">
      <div class="search_thing">
        <div class="search_label">文件编号:</div>
        <div class="search_input"><el-input v-model="queryParams.documentCode" clearable placeholder="请输入" size="small"
            @keyup.enter.native="refreshTable()"></el-input></div>
      </div>
      <div class="search_thing">
        <div class="search_label">文件名称:</div>
        <div class="search_input"><el-input v-model="queryParams.name" clearable placeholder="请输入" size="small"
            @keyup.enter.native="refreshTable()"></el-input></div>
      </div>
      <div class="search_thing" style="padding-left: 30px;">
        <el-button size="small" @click="refresh()">重 ç½®</el-button>
        <el-button size="small" type="primary" @click="refreshTable()">查 è¯¢</el-button>
      </div>
      <div class="btns" style="padding-left: 30px;">
        <el-button v-if="addPower" size="small" type="primary"
          @click="addDialogVisible = true, addInfo = {}, radio = '发放'">添加发放记录</el-button>
        <el-button v-if="outPower" :loading="outLoading" size="small" type="primary" @click="handleOut">导出</el-button>
      </div>
    </div>
    <div class="table">
      <lims-table :tableData="tableData" :column="column" :page="page" :tableLoading="tableLoading"
        :height="'calc(100vh - 290px)'" @pagination="pagination"></lims-table>
    </div>
    <el-dialog :title="'添加' + radio + '记录'" :visible.sync="addDialogVisible" top="10vh" width="800px">
      <el-row>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label"><span style="color:red;margin-right: 4px;">*</span>申请编号:</div>
            <div class="search_input">
              <el-select v-model="addInfo.documentCode" :disabled="radio == '回收'" allow-create clearable filterable
                size="small" style="width: 100%;" @change="changeFileList">
                <el-option v-for="item in fileList" :key="item.documentCode" :label="item.documentCode"
                  :value="item.documentCode">
                </el-option>
              </el-select>
            </div>
          </div>
        </el-col>
        <el-col v-if="radio == '发放'" :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label"><span style="color:red;margin-right: 4px;">*</span>发放人:</div>
            <div class="search_input">
              <el-select v-model="addInfo.issueUser" filterable size="small" style="width: 100%;">
                <el-option v-for="item in personList" :key="item.value" :label="item.label" :value="item.value">
                </el-option>
              </el-select>
            </div>
          </div>
        </el-col>
        <el-col v-else :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label"><span style="color:red;margin-right: 4px;">*</span>回收人:</div>
            <div class="search_input">
              <el-select v-model="addInfo.recycleUser" filterable size="small" style="width: 100%;">
                <el-option v-for="item in personList" :key="item.value" :label="item.label" :value="item.value">
                </el-option>
              </el-select>
            </div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label"><span style="color:red;margin-right: 4px;">*</span>审批人:</div>
            <div class="search_input">
              <el-select v-model="addInfo.receiveUser" :disabled="radio != '发放'" filterable size="small"
                style="width: 100%;">
                <el-option v-for="item in personList" :key="item.value" :label="item.label" :value="item.value">
                </el-option>
              </el-select>
            </div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">文件名称:</div>
            <div class="search_input"><el-input v-model="addInfo.name" :disabled="radio == '回收'" clearable
                placeholder="请输入" size="small"></el-input></div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">文件版本:</div>
            <div class="search_input"><el-input v-model="addInfo.version" :disabled="radio == '回收'" clearable
                placeholder="请输入" size="small"></el-input></div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">文件状态:</div>
            <div class="search_input">
              <el-select v-model="addInfo.state" :disabled="radio == '回收'" size="small" style="width: 100%;">
                <el-option v-for="(item, index) in fileState" :key="index" :label="item.label"
                  :value="item.value"></el-option>
              </el-select>
            </div>
          </div>
        </el-col>
        <el-col v-if="radio == '发放'" :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">发放编号:</div>
            <div class="search_input"><el-input v-model="addInfo.issueCode" clearable placeholder="请输入"
                size="small"></el-input></div>
          </div>
        </el-col>
        <el-col v-else :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">回收编号:</div>
            <div class="search_input"><el-input v-model="addInfo.recycleCode" clearable placeholder="请输入"
                size="small"></el-input></div>
          </div>
        </el-col>
        <el-col v-if="radio == '发放'" :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">发放时间:</div>
            <div class="search_input">
              <el-date-picker v-model="addInfo.issueDate" format="yyyy-MM-dd" placeholder="选择日期" size="small"
                style="width: 100%;" type="date" value-format="yyyy-MM-dd">
              </el-date-picker>
            </div>
          </div>
        </el-col>
        <el-col v-else :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">回收时间:</div>
            <div class="search_input">
              <el-date-picker v-model="addInfo.recycleDate" format="yyyy-MM-dd" placeholder="选择日期" size="small"
                style="width: 100%;" type="date" value-format="yyyy-MM-dd">
              </el-date-picker>
            </div>
          </div>
        </el-col>
        <el-col v-if="radio == '发放'" :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">发放说明:</div>
            <div class="search_input"><el-input v-model="addInfo.issueNote" :rows="2" clearable placeholder="请输入"
                size="small" type="textarea"></el-input></div>
          </div>
        </el-col>
        <el-col v-else :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">回收说明:</div>
            <div class="search_input"><el-input v-model="addInfo.recycleNote" :rows="2" clearable placeholder="请输入"
                size="small" type="textarea"></el-input></div>
          </div>
        </el-col>
        <el-col v-if="radio == '发放'" :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">上传附件:</div>
            <div class="search_input"><el-upload :auto-upload="false" :multiple="false" :on-change="handleChangeUpload"
                accept='.pdf,.jpg,.jpeg,.png,.gif,.doc,.docx,.xls,.xlsx' action="#" style="margin: 8px 0 0px 50px;">
                <el-button size="small" type="primary">上传附件</el-button>
              </el-upload></div>
          </div>
        </el-col>
      </el-row>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addDialogVisible = false">取 æ¶ˆ</el-button>
        <el-button :loading="addLoading" type="primary" @click="handleAdd">ç¡® å®š</el-button>
      </span>
    </el-dialog>
    <el-dialog :visible.sync="lookDialogVisible" fullscreen title="查看附件" top="5vh" width="800px">
      <filePreview v-if="lookDialogVisible" :currentFile="{}" :fileUrl="javaApi + '/word/' + currentInfo.url"
        style="height: 90vh;overflow-y: auto;" />
    </el-dialog>
  </div>
</template>
<script>
import limsTable from "@/components/Table/lims-table.vue";
import filePreview from '@/components/Preview/filePreview.vue'
import {
  selectUserCondition,
} from "@/api/business/inspectionTask.js";
import {
  pageManageDocumentList,
  pageManageDocumentCancel,
  exportManageDocumentIssueRecycle,
  addManageDocumentIssueRecycle,
  doManageDocumentIssueRecycle,
  checkManageDocumentIssueRecycle,
  pageManageDocumentIssueRecycle,
  delManageDocumentIssueRecycle,
} from '@/api/cnas/systemManagement/documentControl.js'
import { mapGetters } from "vuex";
export default {
  components: {
    filePreview,
    limsTable
  },
  computed: {
    ...mapGetters(["userId"]),
  },
  data() {
    return {
      ddPower: false,
      outPower: true,
      addInfo: {},
      addPower: true,
      addLoading: false,
      addDialogVisible: false,
      outLoading: false,
      personList: [],
      fileList: [],
      fileList0: [],
      radio: '发放',
      fileState: [],
      file: null,
      currentInfo: {},
      lookDialogVisible: false,
      queryParams: {},
      tableData: [],
      column: [
        { label: "文件编号", prop: "documentCode" },
        { label: "文件名称", prop: "name" },
        {
          label: "文件版本",
          prop: "version",
        },
        { label: "文件状态", prop: "documentState" },
        { label: "发放编号", prop: "issueUserName" },
        { label: "发放人", prop: "department" },
        { label: "发放日期", prop: "issueDate" },
        { label: "回收人", prop: "recycleUserName" },
        { label: "回收日期", prop: "recycleDate" },
        {
          dataType: "action",
          fixed: "right",
          label: "操作",
          operation: [
            {
              name: "回收记录",
              type: "text",
              clickFun: (row) => {
                this.handleUpdate(row);
              }
            },
            {
              name: "删除",
              type: "text",
              clickFun: (row) => {
                this.handleDelete(row);
              },
              disabFun: (row, index) => {
                return row.documentState == '通过'
              }
            },
            {
              name: "查看附件",
              type: "text",
              clickFun: (row) => {
                this.handleLook(row);
              },
              disabFun: (row, index) => {
                return !row.url
              }
            },
            {
              name: "审核",
              type: "text",
              clickFun: (row) => {
                this.handleCheck(row);
              },
              disabFun: (row, index) => {
                return row.receiveUser != this.userId || row.documentState == '通过'
              }
            },
          ],
        },
      ],
      page: {
        total: 0,
        size: 10,
        current: 0,
      },
      tableLoading: false,
    }
  },
  mounted() {
    this.getList()
    this.getAuthorizedPerson()
    this.getFileList()
    this.getFileList0()
    this.selectEnumByCategory()
  },
  methods: {
    getPower() {
      let power = JSON.parse(sessionStorage.getItem('power'))
      let out = false
      let del = false
      let add = false
      // let check = false
      for (var i = 0; i < power.length; i++) {
        if (power[i].menuMethod == 'exportManageDocumentIssueRecycle') {
          out = true
        }
        if (power[i].menuMethod == 'addManageDocumentIssueRecycle') {
          add = true
        }
        if (power[i].menuMethod == 'delManageDocumentIssueRecycle') {
          del = true
        }
        // if (power[i].menuMethod == 'checkManageDocumentControlled') {
        //   check = true
        // }
      }
      // if (!check) {
      //   this.componentData.do.splice(2, 1)
      // }
      if (!del) {
        this.componentData.do.splice(1, 1)
      }
      if (!add) {
        this.componentData.do.splice(0, 1)
      }
      this.addPower = add
      this.outPower = out
    },
    getList() {
      this.tableLoading = true;
      let param = { ...this.queryParams, ...this.page };
      delete param.total;
      pageManageDocumentIssueRecycle({ ...param })
        .then((res) => {
          this.tableLoading = false;
          if (res.code === 200) {
            this.tableData = res.data.records;
            this.page.total = res.data.total;
          }
        })
        .catch((err) => {
          this.tableLoading = false;
        });
    },
    pagination({ page, limit }) {
      this.page.current = page;
      this.page.size = limit;
      this.getList();
    },
    refresh() {
      this.queryParams = {};
      this.page.current = 1;
      this.getList();
    },
    refreshTable() {
      this.page.current = 1;
      this.getList();
    },
    selectEnumByCategory() {
      // æ–‡ä»¶çŠ¶æ€
      this.getDicts("document_state").then((response) => {
        this.fileState = this.dictToValue(response.data);
      });
    },
    // èŽ·å–äººå‘˜åˆ—è¡¨
    getAuthorizedPerson() {
      selectUserCondition().then(res => {
        let data = []
        res.data.forEach(a => {
          data.push({
            label: a.name,
            value: a.id
          })
        })
        this.personList = data
      })
    },
    // èŽ·å–æ–‡ä»¶åˆ—è¡¨--文件清单
    getFileList() {
      pageManageDocumentList({
        current: -1,
        size: -1
      }).then(res => {
        this.fileList = res.data.records
      }).catch(err => { })
    },
    // èŽ·å–æ–‡ä»¶åˆ—è¡¨--作废文件
    getFileList0() {
      pageManageDocumentCancel({
        current: -1,
        size: -1
      }).then(res => {
        this.fileList0 = res.data.records
      }).catch(err => { })
    },
    // å¯¼å‡º
    handleOut() {
      this.outLoading = true
      // queryParams
      exportManageDocumentIssueRecycle(this.queryParams).then(res => {
        this.outLoading = false
        const blob = new Blob([res], { type: 'application/octet-stream' });
        this.$download.saveAs(blob, '发放回收记录.xlsx')
        //将Blob å¯¹è±¡è½¬æ¢æˆå­—符串
        // let reader = new FileReader();
        // reader.readAsText(blob, 'utf-8');
        // reader.onload = () => {
        //   try {
        //     let result = JSON.parse(reader.result);
        //     if (result.message) {
        //       this.$message.error(result.message);
        //     } else {
        //       const url = URL.createObjectURL(blob);
        //       const link = document.createElement('a');
        //       link.href = url;
        //       link.download = '发放回收记录.xlsx';
        //       link.click();
        //       this.$message.success('导出成功')
        //     }
        //   } catch (err) {
        //     console.log(err);
        //     const url = URL.createObjectURL(blob);
        //     const link = document.createElement('a');
        //     link.href = url;
        //     link.download = '发放回收记录.xlsx';
        //     link.click();
        //     this.$message.success('导出成功')
        //   }
        // }
      })
    },
    changeFileList(e) {
      if (e) {
        let obj = this.fileList.find(a => a.documentCode == e)
        if (obj) {
          this.addInfo.name = obj.name
          this.addInfo.version = obj.version
          this.addInfo.state = obj.state
        }
      }
    },
    // æäº¤
    handleAdd() {
      if (!this.addInfo.documentCode) {
        this.$message.error('请选择文件')
        return
      }
      if (!this.addInfo.receiveUser) {
        this.$message.error('请选择审批人')
        return
      }
      if (this.radio == '发放') {
        if (!this.addInfo.issueUser) {
          this.$message.error('请选择发放人')
          return
        }
      } else {
        if (!this.addInfo.recycleUser) {
          this.$message.error('请选择回收人')
          return
        }
      }
      this.addLoading = true;
      if (!this.addInfo.id) {
        // æ–°å¢žå‘放记录
        let fd = new FormData();
        //文件信息中raw才是真的文件
        if (this.file) {
          fd.append("file", this.file.raw);
        }
        for (let key in this.addInfo) {
          fd.append(key, this.addInfo[key])
        }
        addManageDocumentIssueRecycle(fd).then(res => {
          this.addLoading = false;
          if (res.code == 200) {
            this.$message.success('发放成功')
            this.addDialogVisible = false
            this.refreshTable()
          }
        })
      } else {
        let { documentCode, id, issueUser, recycleUser, receiveUser, name, version, documentState, issueCode, recycleCode, issueDate, recycleDate, issueNote, recycleNote } = this.addInfo
        // æ·»åŠ å›žæ”¶è®°å½•
        doManageDocumentIssueRecycle({
          documentCode,
          id,
          issueUser,
          recycleUser,
          receiveUser,
          name,
          version,
          documentState,
          issueCode,
          recycleCode,
          issueDate,
          recycleDate,
          issueNote,
          recycleNote
        }).then(res => {
          this.addLoading = false;
          if (res.code == 200) {
            this.$message.success('提交成功')
            this.addDialogVisible = false
            this.refreshTable()
          }
        })
      }
    },
    // æ·»åŠ å›žæ”¶
    handleUpdate(row) {
      this.addInfo = this.HaveJson(row)
      this.radio = '回收'
      this.addDialogVisible = true
    },
    // å®¡æ ¸
    handleCheck(row) {
      this.$confirm('是否审核通过?', '提示', {
        confirmButtonText: '通过',
        cancelButtonText: '不通过',
        type: 'warning',
        closeOnClickModal: false, // ç¦æ­¢ç‚¹å‡»é®ç½©å±‚关闭
        distinguishCancelAndClose: true,
        beforeClose: (action, instance, done) => {
          if (action === 'confirm') {
            // ç‚¹å‡»â€œç¡®å®šâ€æŒ‰é’®ï¼Œå…è®¸å…³é—­
            checkManageDocumentIssueRecycle({ id: row.id, documentState: '通过' }).then(res => {
              this.refreshTable()
              done();
              this.$message({
                type: 'success',
                message: '提交成功'
              })
            })
              .catch(err => {
              })
          } else if (action === 'cancel') {
            // ç‚¹å‡»â€œå–消”按钮,不允许关闭
            checkManageDocumentIssueRecycle({ id: row.id, documentState: '不通过' }).then(res => {
              this.refreshTable()
              done();
              this.$message({
                type: 'success',
                message: '提交成功'
              })
            })
              .catch(err => {
              })
          } else if (action === 'close') {
            // ç‚¹å‡»â€œÃ—”按钮,不允许关闭
            done();
            console.log("×按钮点击事件,不关闭弹框");
          }
        }
      })
    },
    handleChangeUpload(file, fileLists) {
      this.file = file
      this.$set(this.addInfo, 'name', file.name)
    },
    // æŸ¥çœ‹é™„ä»¶
    handleLook(row) {
      this.currentInfo = this.HaveJson(row)
      this.lookDialogVisible = true
    },
    handleDelete(row) {
      this.$confirm("是否删除该条数据?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          delManageDocumentIssueRecycle({ id: row.id }).then((res) => {
            if (res.code == 201) return;
            this.$message.success("删除成功");
            this.refresh();
          });
        })
        .catch(() => { });
    },
  }
}
</script>
<style scoped>
.title {
  height: 60px;
  line-height: 60px;
}
.search {
  background-color: #fff;
  height: 40px;
  display: flex;
  align-items: center;
  position: relative;
}
.search_thing {
  width: 350px;
  display: flex;
  align-items: center;
}
.search_label {
  width: 110px;
  font-size: 14px;
  text-align: right;
}
.search_input {
  width: calc(100% - 110px);
}
.table {
  background-color: #fff;
  height: calc(100% - 60px - 80px);
  padding: 20px;
}
.btns {
  position: absolute;
  right: 20px;
  top: 5px;
}
</style>
src/views/CNAS/systemManagement/documentControl/components/FileChangeRequest.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,706 @@
<template>
  <!-- æ–‡ä»¶å˜æ›´ç”³è¯· -->
  <div class="file-change-request" style="height: 100%;">
    <div class="search">
      <div class="search_thing">
        <div class="search_label">申请文件编号:</div>
        <div class="search_input"><el-input v-model="queryParams.code" clearable placeholder="请输入" size="small"
            @keyup.enter.native="refreshTable()"></el-input></div>
      </div>
      <div class="search_thing" style="padding-left: 30px;">
        <el-button size="small" @click="refresh()">重 ç½®</el-button>
        <el-button size="small" type="primary" @click="refreshTable()">查 è¯¢</el-button>
      </div>
      <div class="btns">
        <el-button size="small" type="primary"
          @click="addDialogVisible = true, addInfo = {}, currentFile = {}, title = '文件变更申请'">文件变更申请</el-button>
        <el-button :loading="outLoading" size="small" type="primary" @click="handleOut">导出</el-button>
      </div>
    </div>
    <div class="table">
      <!-- <ValueTable :key="upIndex" ref="ValueTable" :componentData="componentData"
        :delUrl="$api.manageDocumentList.delManageDocumentAlter"
        :url="$api.manageDocumentList.pageManageDocumentAlter" /> -->
      <lims-table :tableData="tableData" :column="column" :page="page" :tableLoading="tableLoading"
        :height="'calc(100vh - 290px)'" @pagination="pagination"></lims-table>
    </div>
    <el-dialog :title="title" :visible.sync="addDialogVisible" top="0vh" width="950px">
      <el-row>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label"><span style="color:red;margin-right: 4px;">*</span>申请编号:</div>
            <div class="search_input"><el-input v-model="addInfo.code" :disabled="title == '审核'" clearable
                placeholder="请输入" size="small"></el-input></div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">审批人:</div>
            <div class="search_input">
              <el-select v-model="addInfo.checkUser" :disabled="title == '审核'" filterable size="small"
                style="width: 100%;">
                <el-option v-for="item in personList" :key="item.value" :label="item.label" :value="item.value">
                </el-option>
              </el-select>
            </div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">期望变更时间:</div>
            <div class="search_input">
              <el-date-picker v-model="addInfo.expectAlterDate" :disabled="title == '审核'" format="yyyy-MM-dd"
                placeholder="选择日期" size="small" style="width: 100%;" type="date" value-format="yyyy-MM-dd">
              </el-date-picker>
            </div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">实际变更时间:</div>
            <div class="search_input">
              <el-date-picker v-model="addInfo.actuallyAlterDate" :disabled="title == '审核'" format="yyyy-MM-dd"
                placeholder="选择日期" size="small" style="width: 100%;" type="date" value-format="yyyy-MM-dd">
              </el-date-picker>
            </div>
          </div>
        </el-col>
        <el-col :span="24" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">选择文件:</div>
            <div class="search_input">
              <el-select v-model="addInfo.alterBeforeCode" :disabled="title == '审核'" allow-create clearable filterable
                size="small" style="width: 100%;" @change="getCurrentFile">
                <el-option v-for="item in fileList" :key="item.documentCode" :label="item.title"
                  :value="item.documentCode">
                </el-option>
              </el-select>
            </div>
          </div>
        </el-col>
        <el-col :span="24">
          <h4 class="title">当前文件信息</h4>
        </el-col>
        <el-col :span="8" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">文件编号:</div>
            <div class="search_input"><el-input v-model="currentFile.documentCode" :disabled="title == '审核'" clearable
                placeholder="请输入" size="small"></el-input></div>
          </div>
        </el-col>
        <el-col :span="8" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">文件名称:</div>
            <div class="search_input"><el-input v-model="currentFile.name" :disabled="title == '审核'" clearable
                placeholder="请输入" size="small"></el-input></div>
          </div>
        </el-col>
        <el-col :span="8" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">文件版本:</div>
            <div class="search_input"><el-input v-model="currentFile.version" :disabled="title == '审核'" clearable
                placeholder="请输入" size="small"></el-input></div>
          </div>
        </el-col>
        <el-col :span="8" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">提交人:</div>
            <div class="search_input"><el-select v-model="currentFile.createUser" disabled filterable size="small"
                style="width: 100%;">
                <el-option v-for="item in personList" :key="item.value" :label="item.label" :value="item.value">
                </el-option>
              </el-select></div>
          </div>
        </el-col>
        <!-- <el-col :span="8" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">审核人:</div>
            <div class="search_input"><el-input size="small" placeholder="请输入" clearable v-model="addInfo.version" disabled></el-input></div>
          </div>
        </el-col>
        <el-col :span="8" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">作废人:</div>
            <div class="search_input"><el-input size="small" placeholder="请输入" clearable v-model="addInfo.version" disabled></el-input></div>
          </div>
        </el-col> -->
        <el-col :span="8" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">提交时间:</div>
            <div class="search_input"><el-input v-model="currentFile.createTime" clearable disabled placeholder="请输入"
                size="small"></el-input></div>
          </div>
        </el-col>
        <el-col :span="8" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">审核时间:</div>
            <div class="search_input"><el-input v-model="currentFile.effectiveDate" clearable disabled placeholder="请输入"
                size="small"></el-input></div>
          </div>
        </el-col>
        <!-- <el-col :span="8" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">作废时间:</div>
            <div class="search_input"><el-input size="small" placeholder="请输入" clearable v-model="addInfo.version" disabled></el-input></div>
          </div>
        </el-col> -->
        <el-col :span="8" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">文件状态:</div>
            <div class="search_input">
              <el-select v-model="currentFile.state" disabled size="small" style="width: 100%;">
                <el-option v-for="item in fileState" :key="item.value" :label="item.label" :value="item.value">
                </el-option>
              </el-select>
            </div>
          </div>
        </el-col>
        <!-- <el-col :span="8" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">文件说明:</div>
            <div class="search_input"><el-input size="small" placeholder="请输入" clearable v-model="addInfo.instructions" type="textarea"
              :rows="2" disabled></el-input></div>
          </div>
        </el-col> -->
        <el-col :span="24">
          <h4 class="title">文件变更申请</h4>
        </el-col>
        <el-col :span="8" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">变更后编号:</div>
            <div class="search_input"><el-input v-model="addInfo.alterAfterCode" :disabled="title == '审核'" clearable
                placeholder="请输入" size="small"></el-input></div>
          </div>
        </el-col>
        <el-col :span="8" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">变更后名称:</div>
            <div class="search_input"><el-input v-model="addInfo.alterAfterName" :disabled="title == '审核'" clearable
                placeholder="请输入" size="small"></el-input></div>
          </div>
        </el-col>
        <el-col :span="8" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">前一版本处理:</div>
            <div class="search_input">
              <el-select v-model="addInfo.method" :disabled="title == '审核'" size="small" style="width: 100%;">
                <el-option label="作废" value="作废"></el-option>
                <el-option label="存档不可用" value="存档不可用">
                </el-option>
              </el-select>
            </div>
          </div>
        </el-col>
        <el-col :span="8" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">变更后版本:</div>
            <div class="search_input"><el-input v-model="addInfo.alterAfterVersion" :disabled="title == '审核'" clearable
                placeholder="请输入" size="small"></el-input></div>
          </div>
        </el-col>
        <el-col :span="16" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">变更说明:</div>
            <div class="search_input"><el-input v-model="addInfo.alterNote" :disabled="title == '审核'" :rows="2"
                clearable placeholder="请输入" size="small" type="textarea"></el-input></div>
          </div>
        </el-col>
        <el-col v-if="title != '审核'" :span="24" style="margin-bottom: 16px;">
          <div class="search_thing" style="width: 100%;">
            <div class="search_label">相关附件:</div>
            <div class="search_input"><el-upload v-if="addDialogVisible" :auto-upload="false" :multiple="false"
                :on-change="handleChangeUpload" accept='.pdf' action="#" style="margin: 8px 0 0px 50px;">
                <el-button size="small" type="primary">上传附件</el-button>
              </el-upload></div>
          </div>
        </el-col>
        <UpPdfStamp v-if="title == '审核' && addDialogVisible" ref="UpPdfStamp" :isUpFile="false" @uploadPDF="uploadPDF">
        </UpPdfStamp>
      </el-row>
      <span v-if="title != '审核'" slot="footer" class="dialog-footer">
        <el-button @click="addDialogVisible = false">取 æ¶ˆ</el-button>
        <el-button :loading="addLoading" type="primary" @click="handleAdd">ç¡® å®š</el-button>
      </span>
      <span v-else slot="footer" class="dialog-footer">
        <el-button :loading="noCheckLoading" @click="handleCheckSub('不通过')">不通过</el-button>
        <el-button :loading="checkLoading" type="primary" @click="handleCheckSub('通过')">通 è¿‡</el-button>
      </span>
    </el-dialog>
    <el-dialog :visible.sync="lookDialogVisible" fullscreen title="查看附件" top="5vh" width="800px">
      <filePreview v-if="lookDialogVisible" :currentFile="{}" :fileUrl="javaApi + '/word/' + currentInfo.alterAfterUrl"
        style="height: 90vh;overflow-y: auto;" />
    </el-dialog>
  </div>
</template>
<script>
import limsTable from "@/components/Table/lims-table.vue";
import UpPdfStamp from '@/components/UpPdfStamp/index.vue'
import filePreview from '@/components/Preview/filePreview.vue'
import {
  selectUserCondition,
} from "@/api/business/inspectionTask.js";
import {
  exportManageDocumentAlter,
  pageManageDocumentList,
  addManageDocumentAlter,
  doManageDocumentAlter,
  checkManageDocumentAlterPdf,
  checkManageDocumentAlter,
  delManageDocumentAlter,
  pageManageDocumentAlter,
} from '@/api/cnas/systemManagement/documentControl.js'
import { mapGetters } from "vuex";
export default {
  components: {
    limsTable,
    filePreview,
    UpPdfStamp,
  },
  computed: {
    ...mapGetters(["userId"]),
  },
  data() {
    return {
      title: '文件变更申请',
      noCheckLoading: false,
      checkLoading: false,
      addDialogVisible: false,
      addInfo: {},
      addPower: false,
      outPower: false,
      outLoading: false,
      personList: [],
      fileList: [],
      currentFile: {},
      fileState: [],
      file: null,
      addLoading: false,
      lookDialogVisible: false,
      currentInfo: {
      },
      type: null,
      fileName: null,
      queryParams: {},
      tableData: [],
      column: [
        { label: "申请编号", prop: "code" },
        { label: "申请人", prop: "createUserName", width: "120px" },
        {
          label: "变更说明",
          prop: "alterNote",
        },
        { label: "期望变更时间", prop: "expectAlterDate" },
        { label: "实际变更时间", prop: "actuallyAlterDate" },
        {
          label: "状态", prop: "state", dataType: "tag",
          formatData: (params) => {
            return params;
          },
          formatType: (params) => {
            if (params == '通过') {
              return 'success'
            } else {
              return 'danger'
            }
          },
        },
        {
          dataType: "action",
          fixed: "right",
          label: "操作",
          operation: [
            {
              name: "编辑",
              type: "text",
              clickFun: (row) => {
                this.handleUpdate(row);
              },
              disabled: (row, index) => {
                return row.state == '通过'
              }
            },
            {
              name: "删除",
              type: "text",
              clickFun: (row) => {
                this.handleDelete(row);
              },
              disabled: (row, index) => {
                return row.state == '通过'
              }
            },
            {
              name: "审核",
              type: "text",
              clickFun: (row) => {
                this.handleCheck(row);
              },
              disabled: (row, index) => {
                return row.checkUser != this.userId || row.state == '通过'
              }
            },
            {
              name: "查看附件",
              type: "text",
              clickFun: (row) => {
                this.handleLook(row);
              },
            },
            {
              name: "下载附件",
              type: "text",
              clickFun: (row) => {
                this.handleDown(row);
              },
            },
          ],
        },
      ],
      page: {
        total: 0,
        size: 10,
        current: 0,
      },
      tableLoading: false,
    }
  },
  mounted() {
    this.getList()
    this.getAuthorizedPerson()
    this.getFileList()
    this.selectEnumByCategory()
  },
  methods: {
    getPower() {
      let power = JSON.parse(sessionStorage.getItem('power'))
      let up = false
      let del = false
      let add = false
      let out = false
      for (var i = 0; i < power.length; i++) {
        if (power[i].menuMethod == 'addManageDocumentAlter') {
          up = true
        }
        if (power[i].menuMethod == 'addManageDocumentAlter') {
          add = true
        }
        if (power[i].menuMethod == 'delManageDocumentAlter') {
          del = true
        }
        if (power[i].menuMethod == 'exportManageDocumentAlter') {
          out = true
        }
      }
      if (!del) {
        this.componentData.do.splice(1, 1)
      }
      if (!up) {
        this.componentData.do.splice(0, 1)
      }
      this.addPower = add
      this.outPower = out
    },
    getList() {
      this.tableLoading = true;
      let param = { ...this.queryParams, ...this.page };
      delete param.total;
      pageManageDocumentAlter({ ...param })
        .then((res) => {
          this.tableLoading = false;
          if (res.code === 200) {
            this.tableData = res.data.records;
            this.page.total = res.data.total;
          }
        })
        .catch((err) => {
          this.tableLoading = false;
        });
    },
    pagination({ page, limit }) {
      this.page.current = page;
      this.page.size = limit;
      this.getList();
    },
    refresh() {
      this.queryParams = {};
      this.page.current = 1;
      this.getList();
    },
    refreshTable() {
      this.page.current = 1;
      this.getList();
    },
    // å¯¼å‡º
    handleOut() {
      this.outLoading = true
      exportManageDocumentAlter(this.queryParams).then(res => {
        this.outLoading = false
        const blob = new Blob([res], { type: 'application/octet-stream' });
        this.$download.saveAs(blob, '文件变更记录.xlsx')
      })
    },
    getAuthorizedPerson() {
      selectUserCondition().then(res => {
        let data = []
        res.data.forEach(a => {
          data.push({
            label: a.name,
            value: a.id
          })
        })
        this.personList = data
      })
    },
    // èŽ·å–æ–‡ä»¶åˆ—è¡¨--文件清单
    getFileList() {
      pageManageDocumentList({
        current: -1,
        size: -1
      }).then(res => {
        this.fileList = res.data.records.map(m => {
          m.title = m.documentCode + ':' + m.name
          return m
        })
      }).catch(err => { })
    },
    // å½“前文件
    getCurrentFile(e) {
      this.currentFile = this.fileList.find(m => m.documentCode == e)
      if (!this.currentFile) {
        this.currentFile = {}
      }
    },
    selectEnumByCategory() {
      // æ–‡ä»¶çŠ¶æ€
      this.getDicts("document_state").then((response) => {
        this.fileState = this.dictToValue(response.data);
      });
    },
    handleChangeUpload(file, fileLists) {
      this.file = file
      this.$set(this.addInfo, 'alterAfterName', file.name)
    },
    handleAdd() {
      if (!this.addInfo.code) return this.$message({ type: 'error', message: "请输入申请编号" })
      if (!this.addInfo.id) {
        // æ–°å¢ž
        let fd = new FormData();
        //文件信息中raw才是真的文件
        if (this.file) {
          fd.append("file", this.file.raw);
        }
        for (let m in this.addInfo) {
          fd.append(m, this.addInfo[m])
        }
        let { name, version, documentCode } = this.currentFile;
        fd.append("alterBeforeName", name);
        fd.append("alterBeforeVersion", version);
        // fd.append("alterBeforeCode",documentCode);
        this.addLoading = true
        addManageDocumentAlter(fd).then(res => {
          this.addLoading = false
          if (res.code == 200) {
            this.$message({
              type: 'success',
              message: '添加成功'
            })
            this.refreshTable()
            this.addDialogVisible = false
          } else {
            this.$message({
              type: 'error',
              message: '添加失败'
            })
          }
        })
      } else {
        // ä¿®æ”¹
        let fd = new FormData();
        //文件信息中raw才是真的文件
        if (this.file) {
          fd.append("file", this.file.raw);
        }
        let { name, version } = this.currentFile;
        fd.append("alterBeforeName", name);
        fd.append("alterBeforeVersion", version);
        let { code, checkUser, expectAlterDate, actuallyAlterDate, alterAfterCode, method, alterAfterVersion, alterNote, alterAfterName, id } = this.addInfo
        fd.append("code", code);
        fd.append("checkUser", checkUser);
        fd.append("expectAlterDate", expectAlterDate);
        fd.append("actuallyAlterDate", actuallyAlterDate);
        fd.append("alterAfterCode", alterAfterCode);
        fd.append("method", method);
        fd.append("alterAfterVersion", alterAfterVersion);
        fd.append("alterNote", alterNote);
        fd.append("alterAfterName", alterAfterName);
        fd.append("id", id);
        this.addLoading = true
        doManageDocumentAlter(fd).then(res => {
          this.addLoading = false
          if (res.code == 200) {
            this.$message({
              type: 'success',
              message: '修改成功'
            })
            this.refreshTable()
            this.addDialogVisible = false
          } else {
            this.$message({
              type: 'error',
              message: '修改失败'
            })
          }
        })
      }
    },
    // ç¼–辑
    handleUpdate(row) {
      this.title = '文件变更申请'
      this.addInfo = this.HaveJson(row)
      let alterBeforeCode = this.addInfo.alterBeforeCode
      this.getCurrentFile(alterBeforeCode)
      this.addDialogVisible = true
    },
    // é¢„览
    handleLook(row) {
      this.currentInfo = this.HaveJson(row)
      this.lookDialogVisible = true
    },
    // ä¸‹è½½é™„ä»¶
    handleDown(row) {
      if (!row.alterAfterUrl) return this.$message.warning('文件未上传')
      let url = this.javaApi + '/word/' + row.alterAfterUrl
      this.$download.saveAs(url, row.alterAfterUrl)
    },
    // æ‰“开审核弹框
    handleCheck(row) {
      this.title = '审核'
      this.fileName = row.alterAfterName
      if (!row.alterAfterUrl) return this.$message.warning('文件未上传')
      this.addInfo = this.HaveJson(row)
      let alterBeforeCode = this.addInfo.alterBeforeCode
      this.getCurrentFile(alterBeforeCode)
      this.addDialogVisible = true
      checkManageDocumentAlterPdf({ id: row.id }).then(res => {
        //
        const blob = new Blob([res]);
        const file = new File([blob], row.name, { type: 'application/pdf' })
        this.$refs.UpPdfStamp.lookFile(file)
        this.currentInfo = row
      }).catch(err => {
        console.log(err)
      })
    },
    // å®¡æ ¸ä¿å­˜
    async uploadPDF(pdfBlob) {
      const formData = new FormData();
      formData.append('file', pdfBlob, this.fileName + '.pdf'); // æ–‡ä»¶å­—段
      formData.append('id', this.currentInfo.id); // æ–‡ä»¶åå­—段
      formData.append('state', this.type); // æ–‡ä»¶åå­—段
      let res = await checkManageDocumentAlter(formData)
      this.checkLoading = false
      this.noCheckLoading = false
      if (res.code == 200) {
        this.$message({ message: '操作成功', type: 'success' });
        this.addDialogVisible = false;
        this.refreshTable()
        return true
      } else {
        this.$message({ message: '操作失败', type: 'error' });
        return false
      }
    },
    // æäº¤å®¡æ ¸
    handleCheckSub(type) {
      this.type = type
      if (type == '通过') {
        this.checkLoading = true
      } else {
        this.noCheckLoading = true
      }
      this.addLoading = true
      this.$refs['UpPdfStamp'].generatePDF()
    },
    handleDelete(row) {
      this.$confirm("是否删除该条数据?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          delManageDocumentAlter({ id: row.id }).then((res) => {
            if (res.code == 201) return;
            this.$message.success("删除成功");
            this.refresh();
          });
        })
        .catch(() => { });
    },
  }
}
</script>
<style scoped>
.title {
  height: 60px;
  line-height: 60px;
}
.search {
  background-color: #fff;
  height: 40px;
  display: flex;
  align-items: center;
  position: relative;
}
.search_thing {
  width: 350px;
  display: flex;
  align-items: center;
}
.search_label {
  width: 110px;
  font-size: 14px;
  text-align: right;
}
.search_input {
  width: calc(100% - 110px);
}
.table {
  background-color: #fff;
  height: calc(100% - 60px - 80px);
  padding: 20px;
}
.btns {
  position: absolute;
  right: 20px;
  top: 5px;
}
h4.title {
  position: relative;
  height: 30px;
  line-height: 30px;
  box-sizing: border-box;
  padding-left: 16px;
  margin-left: 10px;
  margin-bottom: 10px;
}
h4.title::after {
  content: '';
  width: 4px;
  height: 20px;
  background: #3A7BFA;
  position: absolute;
  top: 5px;
  left: 0;
}
</style>
src/views/CNAS/systemManagement/documentControl/components/FileList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,436 @@
<template>
  <!-- æ–‡ä»¶æ¸…单 -->
  <div class="file-list" style="height: 100%;">
    <div class="search">
      <div class="search_thing">
        <div class="search_label">文件名称:</div>
        <div class="search_input"><el-input size="small" placeholder="请输入" clearable v-model="queryParams.name"
            @keyup.enter.native="refreshTable()"></el-input></div>
      </div>
      <div class="search_thing">
        <div class="search_label">文件状态:</div>
        <div class="search_input">
          <el-select v-model="queryParams.state" size="small" @change="refreshTable()">
            <el-option :label="item.label" :value="item.value" v-for="(item, index) in fileState"
              :key="index"></el-option>
          </el-select>
        </div>
      </div>
      <div class="search_thing" style="padding-left: 30px;">
        <el-button size="small" @click="refresh()">重 ç½®</el-button>
        <el-button size="small" type="primary" @click="refreshTable()">查 è¯¢</el-button>
      </div>
      <div class="btns" style="padding-left: 30px;">
        <el-upload :action="action" :multiple="false" accept='.xls,.xlsx' :headers="headers" :on-change="beforeUpload"
          :on-error="onError" ref='upload' :on-success="handleSuccessUp" :show-file-list="false">
          <el-button size="small" type="primary" :loading="upLoading">导入</el-button></el-upload>
      </div>
    </div>
    <div class="table">
      <lims-table :tableData="tableData" :column="column" :page="page" :tableLoading="tableLoading"
        :height="'calc(100vh - 290px)'" @pagination="pagination"></lims-table>
    </div>
    <el-dialog title="上传" :visible.sync="addDialogVisible" width="1000px" top="3vh">
      <UpPdfStamp ref="UpPdfStamp" v-if="addDialogVisible" @uploadPDF="uploadPDF" :isUpFile="true"
        @uploadPDFErr="uploadPDFErr"></UpPdfStamp>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addDialogVisible = false">取 æ¶ˆ</el-button>
        <el-button type="primary" @click="handleAdd" v-loading="addLoading">ç¡® å®š</el-button>
      </span>
    </el-dialog>
    <el-dialog title="查看附件" :visible.sync="lookDialogVisible" width="800px" top="5vh" fullscreen>
      <filePreview v-if="lookDialogVisible" :fileUrl="javaApi + '/word/' + currentInfo.url" :currentFile="{}"
        style="max-height: 90vh;overflow-y: auto;" />
    </el-dialog>
    <!-- æ–°å¢ž/编辑 -->
    <el-dialog :title="title" :visible.sync="addDia" width="500px">
      <el-form :model="currentInfo" ref="currentInfoForm" :rules="rules" label-position="right" label-width="120px">
        <el-form-item label="文件编号" prop="documentCode">
          <el-input size="small" placeholder="请输入" clearable v-model="currentInfo.documentCode"></el-input>
        </el-form-item>
        <el-form-item label="类别" prop="type">
          <el-select v-model="currentInfo.type" size="small" clearable placeholder="请选择" style="width: 100%">
            <el-option v-for="item in fileType" :key="item.value" :label="item.label" :value="item.value">
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="名称" prop="name">
          <el-input size="small" placeholder="请输入" clearable v-model="currentInfo.name">
          </el-input>
        </el-form-item>
        <el-form-item label="文件版本" prop="version">
          <el-input size="small" placeholder="请输入" clearable v-model="currentInfo.version">
          </el-input>
        </el-form-item>
        <el-form-item label="作者" prop="writer">
          <el-input size="small" placeholder="请输入" clearable v-model="currentInfo.writer">
          </el-input>
        </el-form-item>
        <el-form-item label="生效日期" prop="effectiveDate">
          <el-date-picker v-model="currentInfo.effectiveDate" format="yyyy-MM-dd" value-format="yyyy-MM-dd" type="date"
            size="small" placeholder="选择日期">
          </el-date-picker>
        </el-form-item>
        <el-form-item label="文件状态" prop="state">
          <el-select v-model="currentInfo.state" size="small" clearable placeholder="请选择" style="width: 100%">
            <el-option v-for="item in fileState" :key="item.value" :label="item.label" :value="item.value">
            </el-option>
          </el-select>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addDia = false">取 æ¶ˆ</el-button>
        <el-button :loading="uploading" type="primary" @click="submitProduct('currentInfoForm')">ç¡® è®¤</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
import UpPdfStamp from '@/components/UpPdfStamp/index.vue'
import filePreview from '@/components/Preview/filePreview.vue'
import limsTable from "@/components/Table/lims-table.vue";
import {
  uploadFileManageDocumentList,
  pageManageDocumentList,
  delManageDocumentList,
  doManageDocumentList,
} from '@/api/cnas/systemManagement/documentControl.js'
import { getToken } from "@/utils/auth";
export default {
  components: {
    UpPdfStamp,
    filePreview,
    limsTable,
  },
  data() {
    return {
      addDialogVisible: false,
      lookDialogVisible: false,
      addPower: false,
      upLoading: false,
      addLoading: false,
      currentInfo: {},
      fileType: [],
      fileState: [],
      title: '新增',
      queryParams: {},
      tableData: [],
      column: [
        { label: "文件编号", prop: "documentCode" },
        {
          label: "类别", prop: "type", width: "120px", dataType: "tag",
          formatData: (params) => {
            return this.fileType.find((m) => m.value == params).label;
          },
          formatType: (params) => {
            return this.fileType.find((m) => m.value == params).type;
          },
        },
        {
          label: "名称",
          prop: "name",
        },
        { label: "文件版本", prop: "version" },
        { label: "作者", prop: "writer" },
        { label: "生效日期", prop: "effectiveDate" },
        {
          label: "文件状态", prop: "state", dataType: "tag",
          formatData: (params) => {
            return this.fileState.find((m) => m.value == params).label;
          },
          formatType: (params) => {
            return this.fileState.find((m) => m.value == params).type;
          },
        },
        {
          dataType: "action",
          fixed: "right",
          label: "操作",
          operation: [
            {
              name: "编辑",
              type: "text",
              clickFun: (row) => {
                this.openAdd("编辑", row);
              },
            },
            {
              name: "上传",
              type: "text",
              clickFun: (row) => {
                this.handleUp(row);
              },
            },
            {
              name: "下载",
              type: "text",
              clickFun: (row) => {
                this.handleDown(row);
              },
            },
            {
              name: "查看附件",
              type: "text",
              clickFun: (row) => {
                this.handleLook(row);
              },
            },
            {
              name: "删除",
              type: "text",
              clickFun: (row) => {
                this.handleDelete(row);
              },
            },
          ],
        },
      ],
      page: {
        total: 0,
        size: 10,
        current: 0,
      },
      tableLoading: false,
      addDia: false,
      rules: {
        documentCode: [{ required: true, message: "请输入文件编号", trigger: "blur" }],
      },
      uploading: false,
    }
  },
  // ç”¨äºŽä¸Šä¼ æ–‡ä»¶çš„信息
  computed: {
    headers() {
      return {
        'Authorization': "Bearer " + getToken()
      }
    },
    action() {
      return this.javaApi + '/manageDocumentList/exportManageDocumentList'
    }
  },
  mounted() {
    this.getList()
    this.selectEnumByCategory()
  },
  methods: {
    getList() {
      this.tableLoading = true;
      let param = { ...this.queryParams, ...this.page };
      delete param.total;
      pageManageDocumentList({ ...param })
        .then((res) => {
          this.tableLoading = false;
          if (res.code === 200) {
            this.tableData = res.data.records;
            this.page.total = res.data.total;
          }
        })
        .catch((err) => {
          this.tableLoading = false;
        });
    },
    pagination({ page, limit }) {
      this.page.current = page;
      this.page.size = limit;
      this.getList();
    },
    refreshTable() {
      this.page.current = 1;
      this.getList();
    },
    refresh() {
      this.queryParams = {};
      this.page.current = 1;
      this.getList();
    },
    openAdd(title, row) {
      this.title = title;
      if (row) {
        this.currentInfo = row;
      } else {
        this.currentInfo = {};
      }
      this.addDia = true;
    },
    // ä¸‹è½½æ–‡ä»¶
    handleDown(row) {
      if (!row.url) return this.$message.warning('文件未上传')
      let url = this.javaApi + '/word/' + row.url
      this.$download.saveAs(url, row.url);
    },
    // æŸ¥çœ‹æ–‡ä»¶
    handleLook(row) {
      if (!row.url) return this.$message.warning('文件未上传')
      this.currentInfo = row
      this.lookDialogVisible = true
    },
    getPower() {
      let power = JSON.parse(sessionStorage.getItem('power'))
      let up = false
      let upFile = false
      let add = false
      for (var i = 0; i < power.length; i++) {
        if (power[i].menuMethod == 'doManageDocumentList') {
          up = true
        }
        if (power[i].menuMethod == 'exportManageDocumentList') {
          add = true
        }
        if (power[i].menuMethod == 'uploadFileManageDocumentList') {
          upFile = true
        }
      }
      if (!upFile) {
        this.componentData.do.splice(1, 1)
      }
      if (!up) {
        this.componentData.do.splice(0, 1)
      }
      this.addPower = add
    },
    // ä¸Šä¼ æ–‡ä»¶
    handleUp(row) {
      this.currentInfo = row
      this.addDialogVisible = true;
    },
    // æäº¤ä¸Šä¼ 
    handleAdd() {
      this.addLoading = true
      this.$refs['UpPdfStamp'].generatePDF()
    },
    uploadPDFErr() {
      this.addLoading = false
    },
    beforeUpload(file) {
      if (file.size > 1024 * 1024 * 10) {
        this.$message.error('上传文件不超过10M');
        this.$refs.upload.clearFiles()
        return false;
      } else {
        // this.upLoading = true;
        return true;
      }
    },
    onError(err, file, fileList) {
      this.$message.error('上传失败')
      this.$refs.upload.clearFiles()
    },
    handleSuccessUp(response) {
      this.upLoading = false;
      if (response.code == 200) {
        this.$message.success('上传成功');
        this.refreshTable()
      }
    },
    selectEnumByCategory() {
      // æ–‡ä»¶ç±»åˆ«
      this.getDicts("document_type").then((response) => {
        this.fileType = this.dictToValue(response.data);
      });
      // æ–‡ä»¶çŠ¶æ€
      this.getDicts("document_state").then((response) => {
        this.fileState = this.dictToValue(response.data);
      });
    },
    async uploadPDF(pdfBlob, fileName) {
      const formData = new FormData();
      formData.append('file', pdfBlob, fileName); // æ–‡ä»¶å­—段
      formData.append('id', this.currentInfo.id); // æ–‡ä»¶åå­—段
      let res = await uploadFileManageDocumentList(formData)
      this.addLoading = false
      if (res.code == 200) {
        this.$message({ message: '上传成功', type: 'success' });
        this.addDialogVisible = false;
        this.refreshTable()
        return true
      } else {
        this.$message({ message: '上传失败', type: 'error' });
        return false
      }
    },
    handleDelete(row) {
      this.$confirm("是否删除该条数据?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          delManageDocumentList({ id: row.id }).then((res) => {
            if (res.code == 201) return;
            this.$message.success("删除成功");
            this.refresh();
          });
        })
        .catch(() => { });
    },
    submitProduct(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          this.uploading = true;
          doManageDocumentList(this.currentInfo)
            .then((res) => {
              this.uploading = false;
              if (res.code != 200) {
                return;
              }
              this.$message.success("提交成功");
              this.refresh();
              this.addDia = false;
            })
            .catch((err) => {
              this.uploading = false;
            });
        } else {
          return false;
        }
      });
    },
  }
}
</script>
<style scoped>
.title {
  height: 60px;
  line-height: 60px;
}
.search {
  background-color: #fff;
  height: 40px;
  display: flex;
  align-items: center;
  position: relative;
}
.search_thing {
  width: 350px;
  display: flex;
  align-items: center;
}
.search_label {
  width: 110px;
  font-size: 14px;
  text-align: right;
}
.search_input {
  width: calc(100% - 110px);
}
.table {
  background-color: #fff;
  height: calc(100% - 60px - 80px);
  padding: 20px;
}
.btns {
  position: absolute;
  right: 20px;
  top: 5px;
}
</style>
src/views/CNAS/systemManagement/documentControl/components/FileObsoletionRequest.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,496 @@
<template>
  <!-- æ–‡ä»¶ä½œåºŸç”³è¯· -->
  <div class="file-obsoletion-request" style="height: 100%;">
    <div class="search">
      <div class="search_thing">
        <div class="search_label">文件编号:</div>
        <div class="search_input"><el-input v-model="queryParams.documentCode" clearable placeholder="请输入" size="small"
            @keyup.enter.native="refreshTable()"></el-input></div>
      </div>
      <div class="search_thing" style="padding-left: 30px;">
        <el-button size="small" @click="refresh()">重 ç½®</el-button>
        <el-button size="small" type="primary" @click="refreshTable()">查 è¯¢</el-button>
      </div>
      <div class="btns" style="padding-left: 30px;">
        <el-button size="small" type="primary" @click="addDialogVisible = true, addInfo = {}">文件作废申请</el-button>
        <el-button :loading="outLoading" size="small" type="primary" @click="handleOut">导出</el-button>
      </div>
    </div>
    <div class="table">
      <!-- <ValueTable :key="upIndex" ref="ValueTable" :componentData="componentData"
        :delUrl="$api.manageDocumentList.delManageDocumentCancel"
        :url="$api.manageDocumentList.pageManageDocumentCancel" /> -->
      <lims-table :tableData="tableData" :column="column" :page="page" :tableLoading="tableLoading"
        :height="'calc(100vh - 290px)'" @pagination="pagination"></lims-table>
    </div>
    <el-dialog :visible.sync="addDialogVisible" title="文件作废申请" top="10vh" width="800px">
      <el-row>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label"><span style="color:red;margin-right: 4px;">*</span>申请编号:</div>
            <div class="search_input">
              <el-select v-model="addInfo.documentCode" allow-create clearable filterable size="small"
                style="width: 100%;" @change="changeFileList">
                <el-option v-for="item in fileList" :key="item.documentCode" :label="item.documentCode"
                  :value="item.documentCode">
                </el-option>
              </el-select>
            </div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">审批人:</div>
            <div class="search_input">
              <el-select v-model="addInfo.checkUser" filterable size="small" style="width: 100%;">
                <el-option v-for="item in personList" :key="item.value" :label="item.label" :value="item.value">
                </el-option>
              </el-select>
            </div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">文件名称:</div>
            <div class="search_input"><el-input v-model="addInfo.name" clearable placeholder="请输入"
                size="small"></el-input></div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">文件版本:</div>
            <div class="search_input"><el-input v-model="addInfo.version" clearable placeholder="请输入"
                size="small"></el-input></div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">文件状态:</div>
            <div class="search_input">
              <el-select v-model="addInfo.state" size="small" style="width: 100%;">
                <el-option v-for="(item, index) in fileState" :key="index" :label="item.label"
                  :value="item.value"></el-option>
              </el-select>
            </div>
          </div>
        </el-col>
        <!-- <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">作废方式:</div>
            <div class="search_input">
              <el-select v-model="addInfo.method" size="small" style="width: 100%;">
                <el-option label="作废" value="作废"></el-option>
                <el-option label="无效" value="无效"></el-option>
              </el-select>
            </div>
          </div>
        </el-col> -->
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">期望作废时间:</div>
            <div class="search_input">
              <el-date-picker v-model="addInfo.expectCancelDate" format="yyyy-MM-dd" placeholder="选择日期" size="small"
                style="width: 100%;" type="date" value-format="yyyy-MM-dd">
              </el-date-picker>
            </div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">实际作废时间:</div>
            <div class="search_input">
              <el-date-picker v-model="addInfo.actuallyCancelDate" format="yyyy-MM-dd" placeholder="选择日期" size="small"
                style="width: 100%;" type="date" value-format="yyyy-MM-dd">
              </el-date-picker>
            </div>
          </div>
        </el-col>
        <el-col :span="12" style="margin-bottom: 16px;">
          <div class="search_thing">
            <div class="search_label">作废说明:</div>
            <div class="search_input"><el-input v-model="addInfo.cancelNote" :rows="2" clearable placeholder="请输入"
                size="small" type="textarea"></el-input></div>
          </div>
        </el-col>
      </el-row>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addDialogVisible = false">取 æ¶ˆ</el-button>
        <el-button :loading="addLoading" type="primary" @click="handleAdd">ç¡® å®š</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
import limsTable from "@/components/Table/lims-table.vue";
import {
  selectUserCondition,
} from "@/api/business/inspectionTask.js";
import {
  addManageDocumentCancel,
  pageManageDocumentList,
  doManageDocumentCancel,
  checkManageDocumentCancel,
  exportManageDocumentCancel,
  delManageDocumentCancel,
  pageManageDocumentCancel,
} from '@/api/cnas/systemManagement/documentControl.js'
import { mapGetters } from "vuex";
export default {
  components: {
    limsTable
  },
  computed: {
    ...mapGetters(["userId"]),
  },
  data() {
    return {
      addPower: false,
      outPower: false,
      addInfo: {},
      addLoading: false,
      addDialogVisible: false,
      personList: [],
      fileList: [],
      outLoading: false,
      fileState: [],
      queryParams: {},
      tableData: [],
      column: [
        { label: "文件编号", prop: "documentCode" },
        { label: "申请人", prop: "createUserName", width: "120px" },
        {
          label: "作废说明",
          prop: "cancelNote",
        },
        { label: "期望作废时间", prop: "expectCancelDate" },
        { label: "实际作废日期", prop: "actuallyCancelDate" },
        {
          label: "作废状态", prop: "state", dataType: "tag",
          formatData: (params) => {
            return params;
          },
          formatType: (params) => {
            if (params == '通过') {
              return 'success'
            } else {
              return 'danger'
            }
          }
        },
        {
          dataType: "action",
          fixed: "right",
          label: "操作",
          operation: [
            {
              name: "编辑",
              type: "text",
              clickFun: (row) => {
                this.handleUpdate(row);
              },
              disabled: (row, index) => {
                return row.state == '通过'
              }
            },
            {
              name: "删除",
              type: "text",
              clickFun: (row) => {
                this.handleDelete(row);
              },
              disabled: (row, index) => {
                return row.state == '通过'
              }
            },
            {
              name: "审核",
              type: "text",
              clickFun: (row) => {
                this.handleCheck(row);
              },
              disabled: (row, index) => {
                return row.checkUser != this.userId || row.state == '通过'
              }
            },
          ],
        },
      ],
      page: {
        total: 0,
        size: 10,
        current: 0,
      },
      tableLoading: false,
    }
  },
  mounted() {
    this.getList()
    this.getAuthorizedPerson()
    this.getFileList()
    this.selectEnumByCategory()
  },
  methods: {
    getPower() {
      let power = JSON.parse(sessionStorage.getItem('power'))
      let out = false
      let del = false
      let add = false
      // let check = false
      for (var i = 0; i < power.length; i++) {
        if (power[i].menuMethod == 'exportManageDocumentCancel') {
          out = true
        }
        if (power[i].menuMethod == 'addManageDocumentCancel') {
          add = true
        }
        if (power[i].menuMethod == 'delManageDocumentCancel') {
          del = true
        }
        // if (power[i].menuMethod == 'checkManageDocumentControlled') {
        //   check = true
        // }
      }
      // if (!check) {
      //   this.componentData.do.splice(2, 1)
      // }
      if (!del) {
        this.componentData.do.splice(1, 1)
      }
      if (!add) {
        this.componentData.do.splice(0, 1)
      }
      this.addPower = add
      this.outPower = out
    },
    getList() {
      this.tableLoading = true;
      let param = { ...this.queryParams, ...this.page };
      delete param.total;
      pageManageDocumentCancel({ ...param })
        .then((res) => {
          this.tableLoading = false;
          if (res.code === 200) {
            this.tableData = res.data.records;
            this.page.total = res.data.total;
          }
        })
        .catch((err) => {
          this.tableLoading = false;
        });
    },
    pagination({ page, limit }) {
      this.page.current = page;
      this.page.size = limit;
      this.getList();
    },
    refresh() {
      this.queryParams = {};
      this.page.current = 1;
      this.getList();
    },
    refreshTable() {
      this.page.current = 1;
      this.getList();
    },
    selectEnumByCategory() {
      // æ–‡ä»¶çŠ¶æ€
      this.getDicts("document_state").then((response) => {
        this.fileState = this.dictToValue(response.data);
      });
    },
    // èŽ·å–äººå‘˜åˆ—è¡¨
    getAuthorizedPerson() {
      selectUserCondition().then(res => {
        let data = []
        res.data.forEach(a => {
          data.push({
            label: a.name,
            value: a.id
          })
        })
        this.personList = data
      })
    },
    // èŽ·å–æ–‡ä»¶åˆ—è¡¨
    getFileList() {
      pageManageDocumentList({
        current: -1,
        size: -1
      }).then(res => {
        this.fileList = res.data.records
      }).catch(err => { })
    },
    // æäº¤
    handleAdd() {
      if (!this.addInfo.documentCode) {
        this.$message.error('请输入申请编号')
        return false
      }
      this.addInfo.method = '作废'
      this.addLoading = true
      if (!this.addInfo.id) {
        // æ–°å¢ž
        addManageDocumentCancel(this.addInfo).then(res => {
          this.addLoading = false
          this.refreshTable()
          this.$message({
            type: 'success',
            message: '提交成功'
          })
          this.addDialogVisible = false
        }).catch(err => { })
      } else {
        // ç¼–辑
        doManageDocumentCancel({
          id: this.addInfo.id,
          method: '作废',
          documentCode: this.addInfo.documentCode,
          checkUser: this.addInfo.checkUser,
          name: this.addInfo.name,
          version: this.addInfo.version,
          documentState: this.addInfo.documentState,
          expectCancelDate: this.addInfo.expectCancelDate,
          actuallyCancelDate: this.addInfo.actuallyCancelDate,
          cancelNote: this.addInfo.cancelNote,
        }).then(res => {
          this.addLoading = false
          this.refreshTable()
          this.$message({
            type: 'success',
            message: '提交成功'
          })
          this.addDialogVisible = false
        }).catch(err => { })
      }
    },
    // é€‰ä¸­æ–‡ä»¶
    changeFileList(e) {
      if (e) {
        let obj = this.fileList.find(a => a.documentCode == e)
        if (obj) {
          this.addInfo.name = obj.name
          this.addInfo.version = obj.version
          this.addInfo.documentState = obj.state
        }
      }
    },
    handleUpdate(row) {
      this.addInfo = this.HaveJson(row)
      this.addDialogVisible = true
    },
    // å®¡æ ¸
    handleCheck(row) {
      this.$confirm('是否审核通过?', '提示', {
        confirmButtonText: '通过',
        cancelButtonText: '不通过',
        type: 'warning',
        closeOnClickModal: false, // ç¦æ­¢ç‚¹å‡»é®ç½©å±‚关闭
        distinguishCancelAndClose: true,
        beforeClose: (action, instance, done) => {
          if (action === 'confirm') {
            // ç‚¹å‡»â€œç¡®å®šâ€æŒ‰é’®ï¼Œå…è®¸å…³é—­
            checkManageDocumentCancel({ id: row.id, state: '通过' }).then(res => {
              this.refreshTable()
              done();
              this.$message({
                type: 'success',
                message: '提交成功'
              })
            })
              .catch(err => {
              })
          } else if (action === 'cancel') {
            // ç‚¹å‡»â€œå–消”按钮,不允许关闭
            checkManageDocumentCancel({ id: row.id, state: '不通过' }).then(res => {
              this.refreshTable()
              done();
              this.$message({
                type: 'success',
                message: '提交成功'
              })
            })
              .catch(err => {
              })
          } else if (action === 'close') {
            // ç‚¹å‡»â€œÃ—”按钮,不允许关闭
            done();
            console.log("×按钮点击事件,不关闭弹框");
          }
        }
      })
    },
    // å¯¼å‡º
    handleOut() {
      this.outLoading = true
      exportManageDocumentCancel(this.queryParams).then(res => {
        this.outLoading = false
        const blob = new Blob([res], { type: 'application/octet-stream' });
        this.$download.saveAs(blob, '文件作废表.xlsx')
        this.$message.success('导出成功')
      })
    },
    handleDelete(row) {
      this.$confirm("是否删除该条数据?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          delManageDocumentCancel({ id: row.id }).then((res) => {
            if (res.code == 201) return;
            this.$message.success("删除成功");
            this.refresh();
          });
        })
        .catch(() => { });
    },
  }
}
</script>
<style scoped>
.title {
  height: 60px;
  line-height: 60px;
}
.search {
  background-color: #fff;
  height: 40px;
  display: flex;
  align-items: center;
  position: relative;
}
.search_thing {
  width: 350px;
  display: flex;
  align-items: center;
}
.search_label {
  width: 110px;
  font-size: 14px;
  text-align: right;
}
.search_input {
  width: calc(100% - 110px);
}
.table {
  background-color: #fff;
  height: calc(100% - 60px - 80px);
  padding: 20px;
}
.btns {
  position: absolute;
  right: 20px;
  top: 5px;
}
</style>
src/views/CNAS/systemManagement/documentControl/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
<template>
  <div class="file-handling">
    <el-tabs type="border-card" v-model="activeName" style="height: 100%;">
      <el-tab-pane :label="item.name" :name="item.component" v-for="(item, index) in tabList" :key="index"
        style="height: 100%;">
        <component :is="item.component" :key="item.component"></component>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>
<script>
import FileList from './components/FileList.vue'
import ControlledFileApplication from './components/ControlledFileApplication.vue'
import DistributionCollectionRecord from './components/DistributionCollectionRecord.vue'
import FileChangeRequest from './components/FileChangeRequest.vue'
import FileObsoletionRequest from './components/FileObsoletionRequest.vue'
export default {
  components: {
    FileList,
    ControlledFileApplication,
    DistributionCollectionRecord,
    FileChangeRequest,
    FileObsoletionRequest
  },
  data() {
    return {
      tabList: [
        {
          name: '文件清单',
          component: 'FileList'
        },
        {
          name: '文件受控申请',
          component: 'ControlledFileApplication'
        },
        {
          name: '发放回收记录',
          component: 'DistributionCollectionRecord'
        },
        {
          name: '文件变更申请',
          component: 'FileChangeRequest'
        },
        {
          name: '文件作废申请',
          component: 'FileObsoletionRequest'
        },
      ],
      activeName: 'FileList'
    };
  },
}
</script>
<style scoped>
.file-handling {
  margin-top: 10px;
  height: calc(100% - 20px);
}
>>>.el-tabs__content {
  height: 100%;
  padding: 0;
  padding-top: 10px;
}
</style>
src/views/business/inspectionTask/inspection.vue
@@ -546,6 +546,7 @@
import DataWorker from '../../../DataWorker.worker';
import html2canvas from "html2canvas";
import { mapGetters } from "vuex";
import { getToken } from "@/utils/auth";
export default {
  name: 'inspection',
  components: {
@@ -771,7 +772,7 @@
    ...mapGetters(["userId"]),
    headers() {
      return {
        token: sessionStorage.getItem("token"),
        'Authorization': "Bearer " + getToken()
      };
    },
    action() {
src/views/business/materialOrderComponents/materialOrder/filesLookVisible.vue
@@ -3,23 +3,20 @@
    <el-dialog title="附件查看" :visible.sync="isShow" width="80%" @closed="$emit('closeFilesLook')">
      <div style="display: flex;justify-content: space-between;">
        <ul class="tab">
          <li v-for="(m,i) in dataVisibleTitle" :key="i" :class="{active:i===dataVisibleIndex}" @click="handleDataVisibleTab(m,i)">{{m.label}}</li>
          <li v-for="(m, i) in dataVisibleTitle" :key="i" :class="{ active: i === dataVisibleIndex }"
            @click="handleDataVisibleTab(m, i)">{{ m.label }}</li>
        </ul>
        <el-upload :action="action"
                   :auto-upload="true"
                   :data="{orderId: dataVisibleIndex === 0 ? filesLookInfo.enterOrderId : filesLookInfo.quarterOrderId}"
                   :on-success="handleSuccessUp" :show-file-list="false"
                   accept='.jpg,.jpeg,.png,.gif,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip,.rar' :headers="headers"
                   :before-upload="beforeUpload"
                   style="width: 80px !important;"
                   :on-error="onError" ref='upload'>
        <el-upload :action="action" :auto-upload="true"
          :data="{ orderId: dataVisibleIndex === 0 ? filesLookInfo.enterOrderId : filesLookInfo.quarterOrderId }"
          :on-success="handleSuccessUp" :show-file-list="false"
          accept='.jpg,.jpeg,.png,.gif,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip,.rar' :headers="headers"
          :before-upload="beforeUpload" style="width: 80px !important;" :on-error="onError" ref='upload'>
          <el-button size="small" type="primary" style="height: 38px">附件上传</el-button>
        </el-upload>
      </div>
      <div v-if="filesDialogVisible">
        <lims-table :tableData="tableDataFile" :column="columnFile"
                    @pagination="paginationFile" height="500px" key="tableDataFile"
                    :page="pageFile" :tableLoading="tableLoadingFile"></lims-table>
        <lims-table :tableData="tableDataFile" :column="columnFile" @pagination="paginationFile" height="500px"
          key="tableDataFile" :page="pageFile" :tableLoading="tableLoadingFile"></lims-table>
      </div>
    </el-dialog>
  </div>
@@ -29,12 +26,12 @@
import ValueTable from "@/components/Table/value-table.vue";
import file from "@/utils/file";
import limsTable from "@/components/Table/lims-table.vue";
import {delfile, downFile, getFileList} from "@/api/business/rawMaterialOrder";
import { delfile, downFile, getFileList } from "@/api/business/rawMaterialOrder";
import { getToken } from "@/utils/auth";
export default {
  name: "filesLookVisible",
  // import å¼•入的组件需要注入到对象中才能使用
  components: {limsTable, ValueTable},
  components: { limsTable, ValueTable },
  props: {
    filesDialogVisible: {
      type: Boolean,
@@ -42,7 +39,7 @@
    },
    filesLookInfo: {
      type: Object,
      default: () => {}
      default: () => { }
    },
  },
  data() {
@@ -74,9 +71,9 @@
            }
          }
        },
        {label: '附件名称', prop: 'fileName'},
        {label: '上传人', prop: 'name'},
        {label: '上传时间', prop: 'createTime'},
        { label: '附件名称', prop: 'fileName' },
        { label: '上传人', prop: 'name' },
        { label: '上传时间', prop: 'createTime' },
        {
          dataType: 'action',
          fixed: 'right',
@@ -101,9 +98,9 @@
        }
      ],
      pageFile: {
        total:0,
        size:10,
        current:1
        total: 0,
        size: 10,
        current: 1
      },
      isShow: this.filesDialogVisible,
      dataVisibleTitle: [
@@ -118,7 +115,7 @@
      ],
      dataVisibleIndex: 0, // tab栏选择值
      entity: {
        insOrderId:''
        insOrderId: ''
      },
    }
  },
@@ -128,7 +125,7 @@
  // æ–¹æ³•集合
  methods: {
    // åˆ‡æ¢æ•°æ®æŸ¥çœ‹tab栏
    handleDataVisibleTab (m, i) {
    handleDataVisibleTab(m, i) {
      this.dataVisibleIndex = i
      this.getFileList()
    },
@@ -140,7 +137,7 @@
        this.entity.insOrderId = this.filesLookInfo.quarterOrderId
      }
      this.tableLoadingFile = true
      const params = {...this.entity}
      const params = { ...this.entity }
      getFileList(params).then(res => {
        this.tableLoadingFile = false
        if (res.code === 200) {
@@ -151,20 +148,20 @@
        this.tableLoadingFile = false
      })
    },
    paginationFile (page) {
    paginationFile(page) {
      this.pageFile.size = page.limit
      this.getFileList()
    },
    // ä¸‹è½½
    handleDown(row){
      downFile({id: row.id,}).then(res => {
    handleDown(row) {
      downFile({ id: row.id, }).then(res => {
        if (res.code === 200) {
          let url = '';
          if(res.data.type==1){
            url = this.javaApi+'/img/'+res.data.fileUrl
            file.downloadIamge(url,row.fileName)
          }else{
            url = this.javaApi+'/word/'+res.data.fileUrl
          if (res.data.type == 1) {
            url = this.javaApi + '/img/' + res.data.fileUrl
            file.downloadIamge(url, row.fileName)
          } else {
            url = this.javaApi + '/word/' + res.data.fileUrl
            const link = document.createElement('a');
            link.href = url;
            link.download = row.fileName;
@@ -175,7 +172,7 @@
      })
    },
    handleSuccessUp(response, ) {
    handleSuccessUp(response,) {
      this.upLoading = false;
      if (response.code == 200) {
        this.$message.success('上传成功');
@@ -205,13 +202,13 @@
      this.$refs.upload.clearFiles()
    },
    // åˆ é™¤
    delete (row) {
    delete(row) {
      this.$confirm('是否删除当前数据?', "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(() => {
        delfile({id: row.id}).then(res => {
        delfile({ id: row.id }).then(res => {
          if (res.code === 500) {
            return
          }
@@ -220,13 +217,13 @@
        }).catch(e => {
          this.$message.error('删除失败')
        })
      }).catch(() => {})
      }).catch(() => { })
    }
  },
  computed: {
    headers() {
      return {
        'token': sessionStorage.getItem('token')
        'Authorization': "Bearer " + getToken()
      }
    },
    action() {
src/views/structural/premises/index.vue
@@ -5,11 +5,11 @@
        <el-form :model="queryParams" ref="queryForm" size="small" :inline="true">
          <el-form-item label="实验室名称" prop="laboratoryName">
            <el-input size="small" placeholder="请输入" clearable v-model="queryParams.laboratoryName"
                      @keyup.enter.native="refreshTable"></el-input>
              @keyup.enter.native="refreshTable"></el-input>
          </el-form-item>
          <el-form-item label="实验室编码" prop="laboratoryNumber">
            <el-input size="small" placeholder="请输入" clearable v-model="queryParams.laboratoryNumber"
                      @keyup.enter.native="refreshTable"></el-input>
              @keyup.enter.native="refreshTable"></el-input>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" icon="el-icon-search" size="mini" @click="refreshTable">查 è¯¢</el-button>
@@ -22,15 +22,13 @@
      </div>
    </div>
    <div class="table">
      <lims-table :tableData="tableData" :column="column"
                  :height="'calc(100vh - 250px)'"
                  @pagination="pagination"
                  :page="page" :tableLoading="tableLoading"></lims-table>
      <lims-table :tableData="tableData" :column="column" :height="'calc(100vh - 250px)'" @pagination="pagination"
        :page="page" :tableLoading="tableLoading"></lims-table>
    </div>
<!--    æ–°å¢žå®žéªŒå®¤-->
    <!--    æ–°å¢žå®žéªŒå®¤-->
    <el-dialog :title="formTitle" :visible.sync="addDia" width="450px">
      <el-form ref="laboratoryForm" :model="laboratoryForm" :rules="userRules"
               label-position="right" label-width="100px">
      <el-form ref="laboratoryForm" :model="laboratoryForm" :rules="userRules" label-position="right"
        label-width="100px">
        <el-form-item label="实验室名称" prop="laboratoryName">
          <el-input v-model="laboratoryForm.laboratoryName" size="small" clearable></el-input>
        </el-form-item>
@@ -51,52 +49,36 @@
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
                <el-button @click="reset">取 æ¶ˆ</el-button>
                <el-button type="primary" @click="customAdd" :loading="loading">ç¡® å®š</el-button>
            </span>
        <el-button @click="reset">取 æ¶ˆ</el-button>
        <el-button type="primary" @click="customAdd" :loading="loading">ç¡® å®š</el-button>
      </span>
    </el-dialog>
    <el-dialog title="印章管理" :visible.sync="fileVisible" width="60vw">
      <div class="btns">
        <el-button size="medium" type="primary" @click="openUpload">更新印章</el-button>
      </div>
      <lims-table :tableData="fileComponentData" :column="fileComponentDataColumn"
                  @pagination="fileComponentPagination" height="500px"
                  :page="fileComponentPage" :tableLoading="fileComponentTableLoading"></lims-table>
      <lims-table :tableData="fileComponentData" :column="fileComponentDataColumn" @pagination="fileComponentPagination"
        height="500px" :page="fileComponentPage" :tableLoading="fileComponentTableLoading"></lims-table>
    </el-dialog>
    <el-dialog title="更新印章" :visible.sync="upFileVisible" width="400px">
      <el-form ref="dataForm" :model="dataForm" :rules="dataFormRules"
               label-position="right" label-width="80px">
      <el-form ref="dataForm" :model="dataForm" :rules="dataFormRules" label-position="right" label-width="80px">
        <el-form-item label="印章类型" prop="type">
          <el-cascader
            v-model="dataForm.type"
            :options="options"
            :show-all-levels="false"
            :props="props"
            placeholder="请选择" size="small"
            style="width:100%"
            collapse-tags
            clearable></el-cascader>
          <el-cascader v-model="dataForm.type" :options="options" :show-all-levels="false" :props="props"
            placeholder="请选择" size="small" style="width:100%" collapse-tags clearable></el-cascader>
        </el-form-item>
        <el-form-item label="印章图片" prop="address">
          <el-upload
            class="avatar-uploader"
            :action="action"
            :headers="headers"
            accept='image/jpg,image/jpeg,image/png'
            :show-file-list="false"
            :on-success="handleSuccess"
            :on-change="beforeUpload"
            ref="upload"
          <el-upload class="avatar-uploader" :action="action" :headers="headers" accept='image/jpg,image/jpeg,image/png'
            :show-file-list="false" :on-success="handleSuccess" :on-change="beforeUpload" ref="upload"
            :on-error="onError">
            <img v-if="dataForm.address" :src="javaApi+'/img/'+dataForm.address" class="avatar" >
            <img v-if="dataForm.address" :src="javaApi + '/img/' + dataForm.address" class="avatar">
            <i v-else class="el-icon-plus avatar-uploader-icon"></i>
          </el-upload>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
                <el-button @click="upFileVisible = false">取 æ¶ˆ</el-button>
                <el-button type="primary" @click="confirmConnect" :loading="loading">ç¡® å®š</el-button>
            </span>
        <el-button @click="upFileVisible = false">取 æ¶ˆ</el-button>
        <el-button type="primary" @click="confirmConnect" :loading="loading">ç¡® å®š</el-button>
      </span>
    </el-dialog>
  </div>
</template>
@@ -111,8 +93,8 @@
  selectSeal,
  upParameter
} from "@/api/structural/laboratoryScope";
import {getCertificationDetail} from "@/api/structural/laboratory";
import { getCertificationDetail } from "@/api/structural/laboratory";
import { getToken } from "@/utils/auth";
export default {
  components: {
    limsTable
@@ -121,7 +103,7 @@
  computed: {
    headers() {
      return {
        'token': sessionStorage.getItem('token')
        'Authorization': "Bearer " + getToken()
      }
    },
    action() {
@@ -137,14 +119,14 @@
      tableData: [],
      tableLoading: false,
      column: [
        {label: '实验室名称', prop: 'laboratoryName'},
        {label: '场所编码', prop: 'laboratoryNumber'},
        {label: '实验室代号', prop: 'laboratoryCode'},
        {label: '负责人', prop: 'head'},
        {label: '负责人电话', prop: 'phoneNumber'},
        {label: '地址', prop: 'address'},
        {label: '创建人', prop: 'createUserName'},
        {label: '创建时间', prop: 'createTime'},
        { label: '实验室名称', prop: 'laboratoryName' },
        { label: '场所编码', prop: 'laboratoryNumber' },
        { label: '实验室代号', prop: 'laboratoryCode' },
        { label: '负责人', prop: 'head' },
        { label: '负责人电话', prop: 'phoneNumber' },
        { label: '地址', prop: 'address' },
        { label: '创建人', prop: 'createUserName' },
        { label: '创建时间', prop: 'createTime' },
        {
          dataType: 'action',
          fixed: 'right',
@@ -176,9 +158,9 @@
        }
      ],
      page: {
        total:0,
        size:10,
        current:1
        total: 0,
        size: 10,
        current: 1
      },
      addDia: false,
      formTitle: '',
@@ -196,43 +178,43 @@
      fileComponentTableLoading: false,
      fileComponentData: [],
      fileComponentDataColumn: [
        {label: '实验室名称', prop: 'laboratoryName'},
        {label: '印章图片', prop: 'address', dataType: 'image'},
        {label: '印章类型', prop: 'type'},
        { label: '实验室名称', prop: 'laboratoryName' },
        { label: '印章图片', prop: 'address', dataType: 'image' },
        { label: '印章类型', prop: 'type' },
      ],
      fileComponentPage: {
        total:0,
        size:10,
        current:1,
        total: 0,
        size: 10,
        current: 1,
        layout: 'total, prev, pager, next'
      },
      fileVisible:false,
      upFileVisible:false,
      loading:false,
      dataForm:{
        type:'',
        address:'',
      fileVisible: false,
      upFileVisible: false,
      loading: false,
      dataForm: {
        type: '',
        address: '',
      },
      dataFormRules: {
        type: [{ required: true, message: '请选择印章类型', trigger: 'change' }],
        address: [{ required: false, message: '请上传图片', trigger: 'change' }],
      },
      props: { multiple: false,emitPath:false,},
      options:[
      props: { multiple: false, emitPath: false, },
      options: [
        {
          value:'实验室资质',
          label:'实验室资质',
          children:[]
          value: '实验室资质',
          label: '实验室资质',
          children: []
        },
        {
          value:'委托报告',
          label:'委托报告',
          children:null
          value: '委托报告',
          label: '委托报告',
          children: null
        },
        {
          value:'进厂报告',
          label:'进厂报告',
          children:null
          value: '进厂报告',
          label: '进厂报告',
          children: null
        },
      ],
    }
@@ -243,7 +225,7 @@
  methods: {
    refreshTable() {
      this.tableLoading = true
      selectItemParameter({...this.page, ...this.queryParams}).then(res => {
      selectItemParameter({ ...this.page, ...this.queryParams }).then(res => {
        this.tableLoading = false
        if (res.code === 200) {
          this.tableData = res.data.records
@@ -259,7 +241,7 @@
      this.refreshTable()
    },
    // åˆ†é¡µåˆ‡æ¢
    pagination (page) {
    pagination(page) {
      this.page.size = page.limit
      this.refreshTable()
    },
@@ -300,34 +282,34 @@
        }
      })
    },
    reset () {
    reset() {
      this.resetForm('laboratoryForm')
      this.addDia = false
    },
    // åˆ é™¤å®žéªŒå®¤
    delete (row) {
    delete(row) {
      this.$confirm('是否删除当前数据?', "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(() => {
        delParameter({id: row.id}).then(res => {
        delParameter({ id: row.id }).then(res => {
          this.$message.success('删除成功')
          this.refreshTable()
        }).catch(e => {
          this.$message.error('删除失败')
        })
      }).catch(() => {})
      }).catch(() => { })
    },
    // æ‰“开印章管理弹框
    fileManagement(row){
    fileManagement(row) {
      this.fileVisible = true;
      this.fileComponentTableLoading = true
      this.currentRow = row
      this.getFileComponentList()
    },
    getFileComponentList () {
      selectSeal({id: this.currentRow.id, ...this.fileComponentPage}).then(res => {
    getFileComponentList() {
      selectSeal({ id: this.currentRow.id, ...this.fileComponentPage }).then(res => {
        this.fileComponentTableLoading = false
        if (res.code === 200) {
          this.fileComponentData = res.data.records
@@ -337,19 +319,19 @@
        this.fileComponentTableLoading = false
      })
    },
    fileComponentPagination (page) {
    fileComponentPagination(page) {
      this.fileComponentPage.size = page.limit
      this.getFileComponentList()
    },
    // æ‰“开更新印章弹框
    openUpload(){
    openUpload() {
      this.dataForm.type = '';
      this.dataForm.address = '';
      this.upFileVisible = true;
      this.getCertificationOperation()
    },
    // æŸ¥è¯¢å°ç« ç±»åž‹
    getCertificationOperation(){
    getCertificationOperation() {
      const params = {
        current: -1,
        size: -1,
@@ -363,11 +345,11 @@
      })
    },
    // æäº¤æ›´æ–°å°ç« 
    confirmConnect(){
    confirmConnect() {
      this.$refs['dataForm'].validate((valid) => {
        if (valid) {
          this.loading = true;
          addSeal({labId:this.currentRow.id, ...this.dataForm}).then(res => {
          addSeal({ labId: this.currentRow.id, ...this.dataForm }).then(res => {
            this.loading = false;
            this.getFileComponentList()
            this.upFileVisible = false;
@@ -375,12 +357,12 @@
        }
      })
    },
    handleSuccess(response,){
    handleSuccess(response,) {
      if (response.code === 200) {
        this.dataForm.address = response.data.url
      }
    },
    beforeUpload(file,type) {
    beforeUpload(file, type) {
      if (file.size > 1024 * 1024 * 10) {
        this.$message.error('上传文件不超过10M');
        this.$refs.upload.clearFiles()
@@ -389,7 +371,7 @@
        return true;
      }
    },
    onError(err, file, fileList,type) {
    onError(err, file, fileList, type) {
      this.$message.error('上传失败')
      this.$refs.upload.clearFiles()
    },
@@ -402,13 +384,16 @@
  display: flex;
  justify-content: space-between;
}
.btns{
.btns {
  text-align: right;
  margin-bottom: 10px;
}
::v-deep .el-dialog__body {
  padding-top: 8px !important;
}
.avatar-uploader ::v-deep .el-upload {
  border: 1px dashed #666666;
  border-radius: 6px;
@@ -416,9 +401,11 @@
  position: relative;
  overflow: hidden;
}
.avatar-uploader ::v-deep .el-upload:hover {
  border-color: #409EFF;
}
.avatar-uploader-icon {
  font-size: 20px;
  color: #8c939d;
@@ -427,6 +414,7 @@
  line-height: 90px;
  text-align: center;
}
.avatar {
  width: 90px;
  height: 90px;
src/views/system/menu/index.vue
@@ -2,21 +2,12 @@
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch">
      <el-form-item label="菜单名称" prop="menuName">
        <el-input
          v-model="queryParams.menuName"
          placeholder="请输入菜单名称"
          clearable
          @keyup.enter.native="handleQuery"
        />
        <el-input v-model="queryParams.menuName" placeholder="请输入菜单名称" clearable @keyup.enter.native="handleQuery" />
      </el-form-item>
      <el-form-item label="状态" prop="status">
        <el-select v-model="queryParams.status" placeholder="菜单状态" clearable>
          <el-option
            v-for="dict in dict.type.sys_normal_disable"
            :key="dict.value"
            :label="dict.label"
            :value="dict.value"
          />
          <el-option v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.label"
            :value="dict.value" />
        </el-select>
      </el-form-item>
      <el-form-item>
@@ -27,35 +18,17 @@
    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button
          type="primary"
          plain
          icon="el-icon-plus"
          size="mini"
          @click="handleAdd"
          v-hasPermi="['system:menu:add']"
        >新增</el-button>
        <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
          v-hasPermi="['system:menu:add']">新增</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="info"
          plain
          icon="el-icon-sort"
          size="mini"
          @click="toggleExpandAll"
        >展开/折叠</el-button>
        <el-button type="info" plain icon="el-icon-sort" size="mini" @click="toggleExpandAll">展开/折叠</el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>
    <el-table
      v-if="refreshTable"
      v-loading="loading"
      :data="menuList"
      row-key="menuId"
      :default-expand-all="isExpandAll"
      :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
    >
    <el-table v-if="refreshTable" v-loading="loading" :data="menuList" row-key="menuId"
      :default-expand-all="isExpandAll" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }">
      <el-table-column prop="menuName" label="菜单名称" :show-overflow-tooltip="true" width="160"></el-table-column>
      <el-table-column prop="icon" label="图标" align="center" width="100">
        <template slot-scope="scope">
@@ -67,7 +40,7 @@
      <el-table-column prop="component" label="组件路径" :show-overflow-tooltip="true"></el-table-column>
      <el-table-column prop="status" label="状态" width="80">
        <template slot-scope="scope">
          <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
          <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status" />
        </template>
      </el-table-column>
      <el-table-column label="创建时间" align="center" prop="createTime">
@@ -77,27 +50,12 @@
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['system:menu:edit']"
          >修改</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-plus"
            @click="handleAdd(scope.row)"
            v-hasPermi="['system:menu:add']"
          >新增</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['system:menu:remove']"
          >删除</el-button>
          <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
            v-hasPermi="['system:menu:edit']">修改</el-button>
          <el-button size="mini" type="text" icon="el-icon-plus" @click="handleAdd(scope.row)"
            v-hasPermi="['system:menu:add']">新增</el-button>
          <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
            v-hasPermi="['system:menu:remove']">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
@@ -108,13 +66,8 @@
        <el-row>
          <el-col :span="24">
            <el-form-item label="上级菜单" prop="parentId">
              <treeselect
                v-model="form.parentId"
                :options="menuOptions"
                :normalizer="normalizer"
                :show-count="true"
                placeholder="选择上级菜单"
              />
              <treeselect v-model="form.parentId" :options="menuOptions" :normalizer="normalizer" :show-count="true"
                placeholder="选择上级菜单" />
            </el-form-item>
          </el-col>
        </el-row>
@@ -132,20 +85,10 @@
        <el-row>
          <el-col :span="24" v-if="form.menuType != 'F'">
            <el-form-item label="菜单图标" prop="icon">
              <el-popover
                placement="bottom-start"
                width="460"
                trigger="click"
                @show="$refs['iconSelect'].reset()"
              >
              <el-popover placement="bottom-start" width="460" trigger="click" @show="$refs['iconSelect'].reset()">
                <IconSelect ref="iconSelect" @selected="selected" :active-icon="form.icon" />
                <el-input slot="reference" v-model="form.icon" placeholder="点击选择图标" readonly>
                  <svg-icon
                    v-if="form.icon"
                    slot="prefix"
                    :icon-class="form.icon"
                    style="width: 25px;"
                  />
                  <svg-icon v-if="form.icon" slot="prefix" :icon-class="form.icon" style="width: 25px;" />
                  <i v-else slot="prefix" class="el-icon-search el-input__icon" />
                </el-input>
              </el-popover>
@@ -169,7 +112,7 @@
            <el-form-item prop="isFrame">
              <span slot="label">
                <el-tooltip content="选择是外链则路由地址需要以`http(s)://`开头" placement="top">
                <i class="el-icon-question"></i>
                  <i class="el-icon-question"></i>
                </el-tooltip>
                æ˜¯å¦å¤–链
              </span>
@@ -183,7 +126,7 @@
            <el-form-item prop="path">
              <span slot="label">
                <el-tooltip content="访问的路由地址,如:`user`,如外网地址需内链访问则以`http(s)://`开头" placement="top">
                <i class="el-icon-question"></i>
                  <i class="el-icon-question"></i>
                </el-tooltip>
                è·¯ç”±åœ°å€
              </span>
@@ -196,7 +139,7 @@
            <el-form-item prop="component">
              <span slot="label">
                <el-tooltip content="访问的组件路径,如:`system/user/index`,默认在`views`目录下" placement="top">
                <i class="el-icon-question"></i>
                  <i class="el-icon-question"></i>
                </el-tooltip>
                ç»„件路径
              </span>
@@ -208,7 +151,7 @@
              <el-input v-model="form.perms" placeholder="请输入权限标识" maxlength="100" />
              <span slot="label">
                <el-tooltip content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasPermi('system:user:list')`)" placement="top">
                <i class="el-icon-question"></i>
                  <i class="el-icon-question"></i>
                </el-tooltip>
                æƒé™å­—符
              </span>
@@ -221,7 +164,7 @@
              <el-input v-model="form.query" placeholder="请输入路由参数" maxlength="255" />
              <span slot="label">
                <el-tooltip content='访问路由的默认传递参数,如:`{"id": 1, "name": "ry"}`' placement="top">
                <i class="el-icon-question"></i>
                  <i class="el-icon-question"></i>
                </el-tooltip>
                è·¯ç”±å‚æ•°
              </span>
@@ -231,7 +174,7 @@
            <el-form-item prop="isCache">
              <span slot="label">
                <el-tooltip content="选择是则会被`keep-alive`缓存,需要匹配组件的`name`和地址保持一致" placement="top">
                <i class="el-icon-question"></i>
                  <i class="el-icon-question"></i>
                </el-tooltip>
                æ˜¯å¦ç¼“å­˜
              </span>
@@ -247,16 +190,13 @@
            <el-form-item prop="visible">
              <span slot="label">
                <el-tooltip content="选择隐藏则路由将不会出现在侧边栏,但仍然可以访问" placement="top">
                <i class="el-icon-question"></i>
                  <i class="el-icon-question"></i>
                </el-tooltip>
                æ˜¾ç¤ºçŠ¶æ€
              </span>
              <el-radio-group v-model="form.visible">
                <el-radio
                  v-for="dict in dict.type.sys_show_hide"
                  :key="dict.value"
                  :label="dict.value"
                >{{dict.label}}</el-radio>
                <el-radio v-for="dict in dict.type.sys_show_hide" :key="dict.value" :label="dict.value">{{ dict.label
                  }}</el-radio>
              </el-radio-group>
            </el-form-item>
          </el-col>
@@ -264,17 +204,28 @@
            <el-form-item prop="status">
              <span slot="label">
                <el-tooltip content="选择停用则路由将不会出现在侧边栏,也不能被访问" placement="top">
                <i class="el-icon-question"></i>
                  <i class="el-icon-question"></i>
                </el-tooltip>
                èœå•状态
              </span>
              <el-radio-group v-model="form.status">
                <el-radio
                  v-for="dict in dict.type.sys_normal_disable"
                  :key="dict.value"
                  :label="dict.value"
                >{{dict.label}}</el-radio>
                <el-radio v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.value">{{
                  dict.label
                }}</el-radio>
              </el-radio-group>
            </el-form-item>
          </el-col>
          <el-col :span="12" v-if="form.menuType != 'M'">
            <el-form-item prop="status">
              <span slot="label">
                <el-tooltip content="选择只看我按钮,在角色管理处可配置只看我的数据权限" placement="top">
                  <i class="el-icon-question"></i>
                </el-tooltip>
                åªçœ‹æˆ‘按钮
              </span>
              <el-switch v-model="form.isRersonalButton" inactive-text="不显示" active-text="显示" inactive-value="0"
                active-value="1">
              </el-switch>
            </el-form-item>
          </el-col>
        </el-row>
@@ -433,7 +384,7 @@
      });
    },
    /** æäº¤æŒ‰é’® */
    submitForm: function() {
    submitForm: function () {
      this.$refs["form"].validate(valid => {
        if (valid) {
          if (this.form.menuId != undefined) {
@@ -454,12 +405,12 @@
    },
    /** åˆ é™¤æŒ‰é’®æ“ä½œ */
    handleDelete(row) {
      this.$modal.confirm('是否确认删除名称为"' + row.menuName + '"的数据项?').then(function() {
      this.$modal.confirm('是否确认删除名称为"' + row.menuName + '"的数据项?').then(function () {
        return delMenu(row.menuId);
      }).then(() => {
        this.getList();
        this.$modal.msgSuccess("删除成功");
      }).catch(() => {});
      }).catch(() => { });
    }
  }
};
vue.config.js
@@ -36,7 +36,7 @@
    proxy: {
      // detail: https://cli.vuejs.org/config/#devserver-proxy
      [process.env.VUE_APP_BASE_API]: {
        target: `http://192.168.0.170:8002`,
        target: `http://192.168.1.36:8002`,
        changeOrigin: true,
        pathRewrite: {
          ["^" + process.env.VUE_APP_BASE_API]: "",