vue项目如何实现前端预览word与pdf格式文件

Vue
935
0
0
2023-09-09
标签   Vue实践

最近做vue项目遇到一个需求,就是前端实现上传word或pdf文件后,后端返回文件对应的文件流,前端需要在页面上展示出来。word预览简单一些,pdf预览我试过pdfjs,vue-pdf总是报各种奇奇怪怪的bug,但最终总算解决了问题,先看一下页面最终呈现效果吧:

页面上传pdf文件效果如下:

页面预览pdf文件效果如下:

页面上传word文件效果如下:

页面预览word文件效果如下:

这里先从上传组件页面说起,上传页面组件完整代码如下,按钮里面v-show=“$checkPermission([‘Register_tuotpUpload’])“都是给这个按钮设置了按钮权限的,我们只需要关注上传那一部分的代码即可,我们用了el-upload组件实现的手动上传,由于需求要求只能上传word和pdf,所以能看到属性设置的有 accept=”.pdf, .doc, .docx”,然后不展示上传成功的文件的列表设置的属性有:show-file-list=“false”,而handleExceed回调函数和limit都是为了限制只能上传一个文件,上传前的回调钩子函数beforeAvatarUpload里进行了文件类型判断与提醒,手动上传是通过UploadFile里进行完成的,需要注意的是由于docx-preview这个插件只能预览后缀为docx的word文件,如果是doc后缀格式的word文件一定要让后端强制将上传doc格式的文件改为docx格式,目前对于doc格式的word文件实现网页在线预览我只想到了docx-preview这个插件和这个解决办法

<template>
  <div class="app-container">
    <div class="cardWhite">
      <div class="itemBox">
        <div class="headerTitle">基本信息</div>
        <el-form
          :model="ruleForm"
          :rules="rules"
          ref="ruleForm"
          label-width="px"
          class="demo-ruleForm"
        >
          <el-row>
            <el-col :span="">
              <el-form-item label="链路名称" prop="name">
                <el-input
                  v-model="ruleForm.name"
                  placeholder="请输入链路名称"
                  clearable
                ></el-input>
              </el-form-item>
            </el-col>

            <el-col :span="">
              <el-form-item label="链路类型" prop="linkType">
                <el-select
                  v-model="ruleForm.linkType"
                  placeholder="请选择链路类型"
                  style="width:%"
                  clearable
                >
                  <el-option
                    v-for="item in linkTypeList"
                    :key="item.val"
                    :label="item.key"
                    :value="item.val"
                  ></el-option>
                </el-select>
              </el-form-item>
            </el-col>

            <el-col :span="">
              <el-form-item label="链路走向" prop="go">
                <el-row>
                  <el-col :span="">
                    <el-select
                      v-model="ruleForm.srcNetwork"
                      placeholder="请选择"
                      style="width:%"
                      clearable
                      @clear="clearSrc"
                      @change="srcChange"
                    >
                      <el-option
                        v-for="item in scrList"
                        :key="item.val"
                        :label="item.key"
                        :value="item.val"
                      ></el-option>
                    </el-select>
                  </el-col>

                  <el-col :span="">
                    <div style="text-align: center;width:%">
                      <img
                        src="@/assets/toRight.png"
                        style="width:.75rem;height:0.75rem;margin:0 auto"
                      />
                    </div>
                  </el-col>

                  <el-col :span="">
                    <el-select
                      v-model="ruleForm.dstNetwork"
                      placeholder="请选择"
                      style="width:%"
                      :clearable="false"
                      @clear="clearDst"
                      @change="dstChange"
                    >
                      <el-option
                        v-for="item in dstList"
                        :key="item.val"
                        :label="item.key"
                        :value="item.val"
                      ></el-option>
                    </el-select>
                  </el-col>
                </el-row>
              </el-form-item>
            </el-col>

            <el-col :span="">
              <el-form-item label="物理位置" prop="physicalPosition">
                <el-input
                  v-model="ruleForm.physicalPosition"
                  placeholder="请输入链路物理位置"
                  clearable
                ></el-input>
              </el-form-item>
            </el-col>

            <el-col :span="">
              <el-form-item label="所属机构" prop="orangeName">
                <el-input
                  v-model="ruleForm.orangeName"
                  placeholder="例:xx市公安局"
                  clearable
                ></el-input>
              </el-form-item>
            </el-col>

            <el-col :span="">
              <el-form-item label="行政区编码" prop="organCode">
                <el-input
                  v-model="ruleForm.organCode"
                  placeholder="请输入行政区编码,例:"
                  clearable
                ></el-input>
              </el-form-item>
            </el-col>

            <el-col :span="">
              <el-form-item label="责任人" prop="dutyPerson">
                <el-input
                  v-model="ruleForm.dutyPerson"
                  placeholder="请输入链路责任人"
                  clearable
                ></el-input>
              </el-form-item>
            </el-col>

            <el-col :span="">
              <el-form-item label="责任人电话" prop="dutyTel">
                <el-input
                  v-model="ruleForm.dutyTel"
                  placeholder="请输入链路责任人电话"
                  clearable
                ></el-input>
              </el-form-item>
            </el-col>
          </el-row>

          <el-row>
            <el-col :span="">
              <el-form-item label="公安网邮箱" prop="dutyEmail">
                <el-input
                  v-model="ruleForm.dutyEmail"
                  placeholder="请输入负责人公安网邮箱"
                  clearable
                ></el-input>
              </el-form-item>
            </el-col>
          </el-row>

          <el-row>
            <el-col :span="">
              <el-form-item label="链路IP地址" prop="ip">
                <el-input
                  v-model="ruleForm.ip"
                  placeholder="请输入链路IP地址"
                  clearable
                ></el-input>
              </el-form-item>
            </el-col>

            <el-col :span="">
              <el-form-item label="链路端口" prop="port">
                <el-input-number
                  placeholder="请输入链路端口"
                  type="text"
                  :min=""
                  :controls="false"
                  v-model.trim="ruleForm.port"
                  style="width:%"
                ></el-input-number>
              </el-form-item>
            </el-col>
          </el-row>

          <el-row>
            <el-col :span="">
              <el-form-item label="管理页面" prop="webUrl">
                <el-input
                  v-model="ruleForm.webUrl"
                  placeholder="请输入链路管理页面"
                  clearable
                ></el-input>
              </el-form-item>
            </el-col>
          </el-row>

          <el-row>
            <el-col :push="">
              <el-button
                class="filter-item"
                type="primary"
                icon="el-icon-plus"
                @click="saveForm"
                v-show="$checkPermission(['Register_boundarySave'])"
              >
                保存
              </el-button>
            </el-col>
          </el-row>
        </el-form>
      </div>

      <div class="itemBox">
        <div class="headerTitle">链路拓扑图</div>
        <el-form :model="tuopuForm" ref="tuopuForm" label-width="px">
          <el-row>
            <el-col :span="">
              <el-form-item label="拓扑图" prop="fileName">
                <el-input
                  v-model="tuopuForm.fileName"
                  placeholder="请选择电脑中拓扑图文件"
                  clearable
                  disabled
                >
                  <el-upload
                    accept=".pdf, .doc, .docx"
                    action="string"
                    :limit=""
                    :on-exceed="handleExceed"
                    :before-upload="beforeAvatarUpload"
                    :http-request="UploadFile"
                    slot="append"
                    :show-file-list="false"
                  >
                    <el-button
                      type="primary"
                      v-show="$checkPermission(['Register_tuotpUpload'])"
                      icon="el-icon-upload"
                      style="background:#ff;color:#fff;border-top-left-radius:0;border-bottom-left-radius:0"
                      >上传</el-button
                    >
                  </el-upload>
                </el-input>
              </el-form-item>
            </el-col>

            <el-col :span="" :push="1">
              <el-button
                class="filter-item"
                type="primary"
                icon="el-icon-view"
                @click="preview"
                v-show="$checkPermission(['Register_tuotpPreview'])"
              >
                预览
              </el-button>
            </el-col>
          </el-row>
        </el-form>
      </div>

      <div class="itemBox">
        <div class="headerTitle">设备信息列表</div>
        <el-row type="flex" justify="end" style="margin:.5rem 0;">
          <el-button
            class="filter-item"
            type="primary"
            icon="el-icon-plus"
            @click="addEquipment"
            v-show="$checkPermission(['Register_equipmentAdd'])"
          >
            添加
          </el-button>
        </el-row>

        <div>
          <commonTable
            :tableHead="tableHead"
            :tableData="tableData"
            :dataFiter="true"
            :selectionFlag="false"
            :dropdownList="[]"
            :resizable="true"
            :tableLoading="tableLoading"
            :showListD="showListD"
            :toolBoxFlag="false"
            @sortChange="() => {}"
            @selection-change="() => {}"
            @selectAction="() => {}"
            @addData="() => {}"
            :actionFlag="false"
            :actionList="[]"
            :freeElfFlag="false"
            :xuhaoFlag="true"
            :freeWidth=""
          >
            <template
              slot-scope="scope"
              slot="doSomething"
              fixed="right"
              align="left"
            >
              <el-button
                icon="el-icon-edit"
                type="primary"
                @click="handlerUpdate(scope.rows)"
                v-show="$checkPermission(['Register_equipmentEdit'])"
                >编辑</el-button
              >

              <el-button
                icon="el-icon-delete"
                type="danger"
                @click="handlerDelete(scope.rows)"
                v-show="$checkPermission(['Register_equipmentDelete'])"
                style="margin-left:-.015rem"
                >删除</el-button
              >
            </template>
          </commonTable>
        </div>
      </div>

      <div class="itemBox">
        <div class="headerTitle">链路注册</div>
        <el-row type="flex" justify="end" style="margin:.5rem 0;">
          <el-button
            class="filter-item"
            type="primary"
            icon="el-icon-plus"
            @click="addRegister"
            v-show="$checkPermission(['Register_registerAdd'])"
          >
            添加
          </el-button>
        </el-row>

        <div>
          <commonTable
            :tableHead="Register_tableHead"
            :tableData="Register_tableData"
            :dataFiter="true"
            :selectionFlag="false"
            :dropdownList="[]"
            :resizable="true"
            :tableLoading="Register_tableLoading"
            :showListD="Register_showListD"
            :toolBoxFlag="false"
            @sortChange="() => {}"
            @selection-change="() => {}"
            @selectAction="() => {}"
            @addData="() => {}"
            :actionFlag="false"
            :actionList="[]"
            :freeElfFlag="false"
            :xuhaoFlag="true"
            :freeWidth=""
          >
            <template slot-scope="scope" slot="status">
              <el-tag v-if="scope.rows.status ==">已注册</el-tag>
              <el-tag type="success" v-if="scope.rows.status =="
                >未注册</el-tag
              >
              <el-tag type="danger" v-if="scope.rows.status =="
                >注册失败</el-tag
              >
            </template>

            <template
              slot-scope="scope"
              slot="doSomething"
              fixed="right"
              align="left"
            >
              <el-button
                icon="el-icon-edit"
                type="primary"
                v-if="
                  scope.rows.status == &&
                    $checkPermission(['Register_registerOff'])
                "
                @click="handlerLogoff(scope.rows)"
                >注销</el-button
              >

              <el-button
                icon="el-icon-edit"
                type="primary"
                v-if="
                  ($checkPermission(['Register_registerGo']) &&
                    scope.rows.status ==) ||
                    scope.rows.status ==
                "
                @click="handlerLogoff(scope.rows)"
                >注册</el-button
              >

              <el-button
                icon="el-icon-delete"
                type="danger"
                v-if="$checkPermission(['Register_registerDelete'])"
                @click="Register_handlerDelete(scope.rows)"
                style="margin-left:-.015rem"
                >删除</el-button
              >
            </template>
          </commonTable>
        </div>
      </div>
    </div>

    <!-- 添加和编辑设备弹窗 -->
    <el-dialog
      :title="textMap[dialogStatus]"
      :visible.sync="dialogFormVisible"
      width="px"
      :before-close="handleClose"
      :close-on-click-modal="false"
    >
      <add-edit
        @refresh="fetchData"
        @closeDialog="dialogFormVisible = false"
        class="AddEdit"
        ref="AddEdit"
        :devTypeList="EquipmentList"
        v-if="dialogFormVisible"
      />

      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">
          取消
        </el-button>

        <el-button type="primary" @click="submitForm()">
          确定
        </el-button>
      </div>
    </el-dialog>

    <!-- 链路注册弹窗 -->
    <el-dialog
      title="链路注册"
      :visible.sync="Register_dialogFormVisible"
      width="px"
      :before-close="Register_handleClose"
      :close-on-click-modal="false"
    >
      <register-add
        @reg_refresh="Register_fetchData"
        @reg_closeDialog="Register_dialogFormVisible = false"
        ref="RegisterAdd"
        v-if="Register_dialogFormVisible"
      />

      <div slot="footer" class="dialog-footer">
        <el-button @click="Register_dialogFormVisible = false">
          取消
        </el-button>

        <el-button type="primary" @click="Register_submitForm()">
          确定
        </el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import commonTable from "@/components/common-table";
import Pagination from "@/components/Pagination";
import AddEdit from "./EquipmentAddEdit.vue";
import RegisterAdd from "./RegisterAdd.vue";

import * as api from "@/api/datax-register.js";

import axios from "axios";
import { getToken } from "@/utils/auth";

export default {
  components: {
    Pagination,
    commonTable,
    AddEdit,
    RegisterAdd
  },
  data() {
    const validateGo = (rule, value, callback) => {
      // if (!value) {
      //   callback("请输入平台IP地址");
      // } else {
      //   let index = value.indexOf("/");
      //   let findHengT = value.indexOf("-");
      //   let findHengP = value.indexOf("——");
      //   if (index > - || findHengT > -1 || findHengP > -1) {
      //     callback("请输入正确格式的平台IP地址");
      //   } else {
      //     validator.IpArea(rule, value, callback);
      //   }
      // }

      if (!this.ruleForm.srcNetwork || !this.ruleForm.dstNetwork) {
        callback("请选择链路走向");
      } else {
        callback();
      }
    };
    return {
      ruleForm: {
        name: "",
        linkType: "",
        srcNetwork: "",
        dstNetwork: "",
        physicalPosition: "",
        orangeName: "",
        organCode: "",
        dutyPerson: "",
        dutyTel: "",
        dutyEmail: "",
        ip: "",
        port: undefined,
        webUrl: ""
      },
      rules: {
        name: [{ required: true, message: "请输入链路名称", trigger: "blur" }],
        linkType: [
          { required: true, message: "请选择链路类型", trigger: "blur" }
        ],
        go: [
          {
            required: true,

            message: "请选择链路走向",
            trigger: "blur",
            validator: validateGo
          }
        ],
        physicalPosition: [
          { required: false, message: "请输入链路物理位置", trigger: "blur" }
        ],
        orangeName: [
          { required: true, message: "请输入所属机构", trigger: "blur" }
        ],

        organCode: [
          { required: true, message: "请输入行政区编码", trigger: "blur" }
        ],

        dutyPerson: [
          { required: true, message: "请输入链路责任人", trigger: "blur" }
        ],
        dutyTel: [
          { required: true, message: "请输入链路责任人电话", trigger: "blur" }
        ],
        dutyEmail: [
          {
            required: false,
            message: "请输入负责人公安网邮箱",
            trigger: "blur"
          }
        ],

        ip: [{ required: false, message: "请输入链路IP地址", trigger: "blur" }],
        port: [{ required: true, message: "请输入链路端口", trigger: "blur" }],
        webUrl: [
          { required: false, message: "请输入链路管理页面", trigger: "blur" }
        ]
      },
      linkTypeList: [],
      scrList: [],
      dstList: [],

      tuopuForm: {
        fileName: "",
        fileUrl: ""
      },
      tableHead: [
        {
          label: "设备名称",
          prop: "name",
          type: "normal",
          sortable: false
        },
        {
          label: "设备类型",
          prop: "devType",
          type: "normal",
          sortable: false
          // width:
        },

        {
          label: "厂商",
          prop: "manufacturer",
          type: "normal",
          sortable: false
          // width:
        },

        {
          label: "型号",
          prop: "model",
          type: "normal",
          sortable: false
          // width:
        },

        {
          label: "设备IP",
          prop: "devIp",
          type: "normal",
          sortable: false
        },

        {
          label: "子网掩码",
          prop: "ipMask",
          type: "normal",
          sortable: false
          // width:
        },
        {
          label: "网关",
          prop: "ipGaway",
          type: "normal",
          sortable: false
          // width:
        },

        {
          label: "安装时间",
          prop: "installTime",
          type: "normal",
          sortable: false,
          width:
        },
        {
          label: "操作",
          prop: "doSomething",
          type: "slot",
          sortable: false,
          slotName: "doSomething",
          width:
        }

        // {
        //   label: "任务详情",
        //   prop: "log_text",
        //   type: "slot",
        //   sortable: false,
        //   slotName: "log_text",
        //   width:
        // }
      ],
      showListD: [
        "name",
        "devType",

        "manufacturer",

        "model",
        "devIp",
        "ipMask",
        "ipGaway",

        "installTime",
        "doSomething"

        // "log_text"
      ],
      dialogStatus: "",
      dialogFormVisible: false,
      textMap: {
        update: "编辑设备",
        Edit: "编辑设备",
        edit: "编辑设备",
        create: "添加设备"
      },
      tableData: [],

      tableLoading: false,

      Register_tableHead: [
        {
          label: "平台名称",
          prop: "name",
          type: "normal",
          sortable: false
        },
        {
          label: "平台IP地址",
          prop: "ip",
          type: "normal",
          sortable: false
        },

        {
          label: "平台端口",
          prop: "port",
          type: "normal",
          sortable: false
        },

        {
          label: "平台唯一标识",
          prop: "uniquePlatformCode",
          type: "normal",
          sortable: false
        },

        {
          label: "注册时间",
          prop: "lastTime",
          type: "normal",

          sortable: false,
          width:
        },

        {
          label: "注册状态",
          prop: "status",
          type: "slot",
          slotName: "status",
          sortable: false
        },

        {
          label: "操作",
          prop: "doSomething",
          type: "slot",
          sortable: false,
          slotName: "doSomething",
          width:
        }

        // {
        //   label: "任务详情",
        //   prop: "log_text",
        //   type: "slot",
        //   sortable: false,
        //   slotName: "log_text",
        //   width:
        // }
      ],

      Register_tableData: [],

      Register_tableLoading: false,

      Register_showListD: [
        "name",
        "ip",
        "port",
        "uniquePlatformCode",
        "lastTime",
        "status",

        "doSomething"

        // "log_text"
      ],
      Register_dialogFormVisible: false,
      fileList: null, //拓扑图文件列表

      tuotpData: null,
      EquipmentList: [],
      originalList: []
    };
  },
  created() {
    this.getNews();
  },
  methods: {
    getNews() {
      //获取边界信息
      this.getBoundaryDetail();

      //获取拓扑图
      this.getTuotp();

      //获取设备列表
      this.fetchData();
      //获取链路注册列表
      this.Register_fetchData();

      //获取公共下拉
      this.getSelect();
    },
    saveForm() {
      this.$refs["ruleForm"].validate(valid => {
        if (valid) {
          let params = {
            ...this.ruleForm
          };

          console.log("修改入参", params);
          //修改
          api
            .boundaryEdit(params)
            .then(res => {
              console.log("修改", res);
              if (res.code ==) {
                this.$notify({
                  title: "成功",
                  message: "边界信息修改成功",
                  type: "success",
                  duration:
                });
              }
            })
            .catch(err => {});
        }
      });
    },
    preview() {
      console.log("预览", this.fileList, this.tuopuForm);
      if (!this.fileList) {
        this.$message.error(
          "拓扑图文件为空不能预览,请先上传拓扑图文件后再预览",
        );

        return false;
      }

      this.$router.push({
        path: "TuotpPreview",
        query: {
          fileName: this.tuopuForm.fileName,
          fileUrl: this.tuopuForm.fileUrl
        }
      });
    },
    addEquipment() {
      this.dialogStatus = "create";
      this.dialogFormVisible = true;
      setTimeout(() => {
        this.$refs["AddEdit"].dialogStatus = "create";
        // this.$refs.AddEdit.resetTransferDetail();
        // this.getCommonData();
      },);
    },

    //获取公共下拉
    getSelect() {
      api
        .getLinkEquimentSelect({
          clsName: "link_direction"
        })
        .then(res => {
          console.log("链路走向下拉", res);
          this.scrList = res.data;
          this.dstList = res.data;
          this.originalList = res.data;
        })
        .catch(err => {});

      api
        .getLinkEquimentSelect({
          clsName: "link_type"
        })
        .then(res => {
          console.log("链路类型下拉", res);
          this.linkTypeList = res.data;
        })
        .catch(err => {});

      api
        .getLinkEquimentSelect({
          clsName: "dev_type"
        })
        .then(res => {
          console.log("设备类型下拉", res);
          this.EquipmentList = res.data;
        })
        .catch(err => {});
    },

    //获取边界信息
    getBoundaryDetail() {
      api
        .boundaryDetail()
        .then(res => {
          // console.log("获取边界信息", res);
          this.ruleForm = res.data;
          this.ruleForm.port = res.data.port ? res.data.port : undefined;
        })
        .catch(err => {});
    },

    //获取拓扑图
    getTuotp() {
      api
        .boundaryTuopoDetail()
        .then(res => {
          // console.log("获取拓扑图成功", res);
          this.tuopuForm = res.data;
          this.tuopuPreview();
        })
        .catch(err => {
          // console.log("获取拓扑图失败", err);
        });
    },

    //获取设备列表
    fetchData() {
      this.tableLoading = true;

      api
        .equipmentList({
          page:,
          page_size:,
          ip: ""
        })
        .then(res => {
          // console.log("设备列表", res);

          if (res.code ==) {
            this.tableData = res.data.list;

            this.tableLoading = false;
          }
        })
        .catch(err => {});
    },

    //编辑
    handlerUpdate(row) {
      // console.log("点了修改", row);

      this.dialogStatus = "Edit";
      this.dialogFormVisible = true;
      setTimeout(() => {
        this.$refs["AddEdit"].setData(row);
        this.$refs["AddEdit"].dialogStatus = "Edit";
      },);
    },

    handleClose() {
      this.dialogFormVisible = false;
    },

    submitForm() {
      this.$refs["AddEdit"].submitForm();
    },

    //删除
    handlerDelete(row) {
      this.$confirm("确定删除吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(() => {
        api
          .equipmentDelete(row)
          .then(response => {
            if (response.code ==) {
              this.fetchData();
              this.$notify({
                title: "成功",
                message: "删除成功",
                type: "success",
                duration:
              });
            }
          })
          .catch(err => {});
      });

      // const index = this.list.indexOf(row)
    },

    //注销
    handlerLogoff(row) {
      api
        .registerStatusSwitch({
          id: row.id,
          status: row.status == ? 2 : 1
        })
        .then(res => {
          this.Register_fetchData();
        })
        .catch(err => {});
    },

    //链路删除
    Register_handlerDelete(row) {
      this.$confirm("确定删除吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(() => {
        let id = row.id;
        api.VideoTaskDelete(id).then(response => {
          if (response.code ==) {
            this.fetchData();
            this.$notify({
              title: "成功",
              message: "删除成功",
              type: "success",
              duration:
            });
          }
        });
      });
    },

    //链路添加
    addRegister() {
      this.$refs["ruleForm"].validate(valid => {
        if (valid) {
          this.Register_dialogFormVisible = true;
          setTimeout(() => {
            // this.$refs["AddEdit"].dialogStatus = "create";
            // this.$refs.AddEdit.resetTransferDetail();
            // this.getCommonData();
          },);
        } else {
          this.$message.error("基本信息为空不能添加,请先补充基本信息",);
        }
      });
    },

    Register_handleClose() {
      this.Register_dialogFormVisible = false;
    },

    Register_submitForm() {
      this.$refs["RegisterAdd"].submitForm();
    },
    //获取链路注册列表
    Register_fetchData() {
      this.Register_tableLoading = true;

      api
        .registerList({
          page:,
          page_size:,
          ip: ""
        })
        .then(res => {
          console.log("注册列表", res);

          if (res.code ==) {
            this.Register_tableData = res.data.list;

            this.Register_tableLoading = false;
          }
        })
        .catch(err => {});
    },

    handleExceed(files, fileList) {
      if (files.length >) {
        this.$message.warning(
          `当前限制最多选择个文件上传,本次选择了${files.length}个文件`
        );
      }
    },

    //文件上传前的钩子
    beforeAvatarUpload(file) {
      console.log("文件上传前的钩子", file);
      // 文件类型判断
      var testmsg = file.name.substring(file.name.lastIndexOf(".") +);
      const extension = testmsg === "doc";
      const extension = testmsg === "pdf";
      const extension = testmsg === "docx";

      if (!extension && !extension && !extension3) {
        this.$message({
          message: "上传的拓扑图文件只能是word、pdf格式,请重新上传!",
          type: "error",
          duration:
        });

        this.fileList = null;

        return false;
      } else {
        this.fileList = file;
      }
    },

    //自定义上传
    UploadFile() {
      // 参数拼接
      let fileData = new FormData();
      fileData.append("file", this.fileList);
      // 调用接口

      axios({
        url: `${window.g.API_URL}/api/cascade/topo/upload`,
        method: "post",
        data: fileData,
        headers: {
          "Content-Type": "multipart/form-data",
          Authorization: getToken()
        }
      })
        .then(res => {
          console.log("上传成功", res);
          if (res.data.code ==) {
            this.tuopuForm = res.data.data;

            //拓扑预览
            this.tuopuPreview();
          } else {
            console.log("上传失败");
            this.$message({
              message: "上传拓扑图文件失败,请稍后重试!",
              type: "error",
              duration:
            });
          }
        })
        .catch(err => {
          console.log("上传失败", err);

          this.$message({
            message: "上传拓扑图文件失败,请稍后重试!",
            type: "error",
            duration:
          });
        });
    },

    tuopuPreview() {
      axios({
        url: `${window.g.API_URL}/api/cascade/topo/preview`,
        method: "get",
        params: {
          fileUrl: this.tuopuForm.fileUrl
        },
        headers: {
          Authorization: getToken()
        },
        responseType: "arraybuffer"
      })
        .then(res => {
          console.log("拓扑预览获取成功", res);
          if (res.status ==) {
            this.fileList = res.data;
          }
        })
        .catch(err => {
          console.log("拓扑预览失败失败", err);
        });
    },

    srcChange(val) {
      console.log("srcChange", val);

      if (val == this.ruleForm.dstNetwork) {
        this.ruleForm.dstNetwork = "";
      }

      this.dstList = this.originalList.filter(k => {
        return k.val != val;
      });
    },
    clearSrc() {
      this.scrList = JSON.parse(JSON.stringify(this.originalList));
    },
    dstChange(val) {
      console.log("dstChange", val);

      if (val == this.ruleForm.srcNetwork) {
        this.ruleForm.srcNetwork = "";
      }

      this.scrList = this.originalList.filter(k => {
        return k.val != val;
      });
    },
    clearDst() {
      this.dstList = JSON.parse(JSON.stringify(this.originalList));
    }
  }
};
</script>

<style lang="scss" scoped>
.app-container {
  .cardWhite {
    .itemBox {
      margin-bottom:.5rem;
      .headerTitle {
        font-size:rem;
        font-weight: bold;
      }
    }
  }
}

::v-deep .el-input-number .el-input__inner {
  text-align: left;
}
</style>

上传功能实现之后,我们再看预览功能。我们需求是点击预览按钮之后跳到一个新页面进行预览,我先是对文件是否为空是否没上传进行了一个判断拦截,由于预览页面组件与当前页面组件既非父子关系,又非爷孙关系,八竿子打不着的关系,我们就不能通过绑定和sessionStorage来进行流文件的传递,只能在预览页面再发一次请求获取文件流,我们可以把参数通过路由带上,预览按钮对应的方法代码如下:

  preview() {
      console.log("预览", this.fileList, this.tuopuForm);
      if (!this.fileList) {
        this.$message.error(
          "拓扑图文件为空不能预览,请先上传拓扑图文件后再预览",
        );

        return false;
      }

      this.$router.push({
        path: "TuotpPreview",
        query: {
          fileName: this.tuopuForm.fileName,
          fileUrl: this.tuopuForm.fileUrl
        }
      });
    }

再来看预览页面,也就是TuotpPreview.vue组件。word预览比较简单,先通过命令cnpm i docx-preview --save安装插件docx-preview,需要注意的是这个插件只能预览后缀为docx的word文件,如果是doc后缀格式的word文件一定要让后端强制将上传doc格式的文件改为docx格式,然后当前页面组件就直接import { defaultOptions, renderAsync } from “docx-preview”;引入,最后在data里进行docxOptions配置,然后在页面上 要显示的div上绑定一个 id="bodyContainer"的,因为下面要通过document.getElementById来获取dom并操作dom,最后调用renderAsync方法即可。

cnpm i docx-preview --save //安装word预览插件docx-preview
import { defaultOptions, renderAsync } from "docx-preview"; //引入docx-preview插件对应的方法
 docxOptions: {
        className: "kaimo-docx-", // string:默认和文档样式类的类名/前缀
        inWrapper: true, // boolean:启用围绕文档内容的包装器渲染
        ignoreWidth: false, // boolean:禁用页面的渲染宽度
        ignoreHeight: false, // boolean:禁止渲染页面高度
        ignoreFonts: false, // boolean:禁用字体渲染
        breakPages: true, // boolean:在分页符上启用分页
        ignoreLastRenderedPageBreak: true, // boolean:在 lastRenderedPageBreak 元素上禁用分页
        experimental: false, // boolean:启用实验功能(制表符停止计算)
        trimXmlDeclaration: true, // boolean:如果为true,解析前会从​​ xml 文档中移除 xml 声明
        useBaseURL: false, // boolean:如果为true,图片、字体等会转为base 64 URL,否则使用URL.createObjectURL
        useMathMLPolyfill: false, // boolean:包括用于 chrome、edge 等的 MathML polyfill。
        showChanges: false, // boolean:启用文档更改的实验性渲染(插入/删除)
        debug: false // boolean:启用额外的日志记录
  },

pdf文件的预览是通过获取到blob文件流之后,直接通过window.open打开新窗口,通过浏览器就能预览pdf,还有一种就是我现在的这种需求,要在页面上预览pdf,那就需要通过iframe,然后把blob流对应的src地址赋值回显即可。完整代码如下:

<template>
  <div>
    <!-- 拓扑图预览 -->
    <div class="app-container">
      <div class="cardWhite">
        <div class="topArea">
          <div class="backBox">
            <img src="@/assets/goBack.png" @click="goBack" alt="" />
          </div>

          <div class="titleBox">
            {{ myTitle }}
          </div>
        </div>

        <div class="previewBox">
          <div id="bodyContainer">
        
            <iframe :src="pdfUrl" width="%" height="750px" />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import axios from "axios";
import { getToken } from "@/utils/auth";
import { defaultOptions, renderAsync } from "docx-preview";
export default {
  data() {
    return {
      myTitle: "",
      previewData: null,
      docxOptions: {
        className: "kaimo-docx-", // string:默认和文档样式类的类名/前缀
        inWrapper: true, // boolean:启用围绕文档内容的包装器渲染
        ignoreWidth: false, // boolean:禁用页面的渲染宽度
        ignoreHeight: false, // boolean:禁止渲染页面高度
        ignoreFonts: false, // boolean:禁用字体渲染
        breakPages: true, // boolean:在分页符上启用分页
        ignoreLastRenderedPageBreak: true, // boolean:在 lastRenderedPageBreak 元素上禁用分页
        experimental: false, // boolean:启用实验功能(制表符停止计算)
        trimXmlDeclaration: true, // boolean:如果为true,解析前会从​​ xml 文档中移除 xml 声明
        useBaseURL: false, // boolean:如果为true,图片、字体等会转为base 64 URL,否则使用URL.createObjectURL
        useMathMLPolyfill: false, // boolean:包括用于 chrome、edge 等的 MathML polyfill。
        showChanges: false, // boolean:启用文档更改的实验性渲染(插入/删除)
        debug: false // boolean:启用额外的日志记录
      },
      num:,
      numPages:,
      pdfUrl: ""
    };
  },
  created() {
    console.log("接收", this.$route.query);
    this.resetTitle();
    this.getFile();
  },
  methods: {
    goBack() {
      this.$router.go(-);
    },

    //获取文件流
    getFile() {
      axios({
        url: `${window.g.API_URL}/api/cascade/topo/preview`,
        method: "get",
        params: {
          fileUrl: this.$route.query.fileUrl
        },
        headers: {
          Authorization: getToken()
        },
        responseType: "arraybuffer"
      })
        .then(res => {
          console.log("文件流获取成功", res);
          if (res.status ==) {
            this.previewData = res.data;
            if (
              this.$route.query.fileName &&
              this.$route.query.fileName.indexOf(".docx") &&
              this.$route.query.fileName.indexOf(".pdf") == -
            ) {
              //word--docx格式
              this.wordPreview(res.data);
            } else if (
              this.$route.query.fileName &&
              this.$route.query.fileName.indexOf(".doc") &&
              this.$route.query.fileName.indexOf(".pdf") == -
            ) {
              //word--doc格式
              this.wordPreview(res.data);
            } else if (
              this.$route.query.fileName &&
              this.$route.query.fileName.indexOf(".pdf")
            ) {
              //pdf
              this.pdfPreview(res.data);
            }
          }
        })
        .catch(err => {
          console.log("文件流获取失败", err);
          this.$message.error("获取文件信息失败,请稍后重试",);
        });
    },

    //重置标题
    resetTitle() {
      let fileName = this.$route.query.fileName;
      console.log("fileName", fileName);

      if (
        fileName &&
        fileName.indexOf(".docx") &&
        fileName.indexOf(".pdf") == -
      ) {
        //word--docx格式
        let wordDocxArr = fileName.split(".docx");
        console.log("wordDocxArr", wordDocxArr);
        this.myTitle = wordDocxArr[];
      } else if (
        fileName &&
        fileName.indexOf(".doc") &&
        fileName.indexOf(".pdf") == -
      ) {
        //word--doc格式

        let wordDocArr = fileName.split(".docx");
        console.log("wordDocArr", wordDocArr);
        this.myTitle = wordDocArr[];
      } else if (fileName && fileName.indexOf(".pdf")) {
        //pdf
        let pdfArr = fileName.split(".pdf");
        console.log("pdfArr", pdfArr);
        this.myTitle = pdfArr[];
      }
    },

    // word文档预览
    wordPreview(buffer) {
      console.log("文档buffer", buffer);
      let bodyContainer = document.getElementById("bodyContainer");
      renderAsync(
        buffer, // Blob | ArrayBuffer | UintArray, 可以是 JSZip.loadAsync 支持的任何类型
        bodyContainer, // HTMLElement 渲染文档内容的元素,
        null, // HTMLElement, 用于呈现文档样式、数字、字体的元素。如果为 null,则将使用 bodyContainer。
        this.docxOptions // 配置
      )
        .then(res => {
          console.log("res---->", res);
        })
        .catch(err => {
          console.log("err---->", err);
        });
    },

    //pdf预览
    pdfPreview(data) {
      // data是一个ArrayBuffer格式,也是一个buffer流的数据
      console.log("pdf流", data);

      const binaryData = [];

      binaryData.push(data);

      //获取blob链接

      let pdfUrl = window.URL.createObjectURL(
        new Blob(binaryData, { type: "application/pdf" })
      );
      console.log("pdfUrl", pdfUrl);
      // window.open(pdfUrl); 这种方式是直接打开新浏览器窗口预览
      this.pdfUrl = pdfUrl;
    }
  }
};
</script>

<style lang="scss" scoped>
.app-container {
  .cardWhite {
    display: flex;
    flex-direction: column;

    .topArea {
      display: flex;
      position: relative;
      .backBox {
        margin-bottom:rem;
        img {
          cursor: pointer;
        }
      }

      .titleBox {
        text-align: center;
        background: #fff;
        color: #;
        position: absolute;
        top:;
        left:%;
        width: auto;
      }
    }
  }
}
</style>