Vue中使用froala富文本编辑器制作打印模板 + print.js 打印

参考上一篇 知识开发的一个功能,制作一个打印模板的管理模块,如下(就是保存froala编辑后的html文本,其中包括Vue的Template,这样我们可以利用Vue的模板的优势来动态绑定一些数据源进行HTML的打印,基本上跟过去水晶报表做一个模板再绑定数据源的方法异曲同工)

 在 main.js 里引用 froala 组件

// Import and use Vue Froala lib.
import VueFroala from 'vue-froala-wysiwyg'

// 引入 Froala Editor js file.
require('froala-editor/js/froala_editor.pkgd.min')
// 引入中文语言包
require('froala-editor/js/languages/zh_cn')
// 引入 Froala Editor css files.
require('froala-editor/css/froala_editor.pkgd.min.css')
require('font-awesome/css/font-awesome.css')
require('froala-editor/css/froala_style.min.css')

// 批量导入 froala-editor 插件文件
function importAll (r) {
  r.keys().forEach(r)
}
importAll(require.context('froala-editor/js/plugins/',false,/\.js$/))
importAll(require.context('froala-editor/css/plugins/',/\.css$/))

Vue.use(VueFroala)

以上基本是引用了所有的插件,不然工具栏会有很多的按钮不出来

然后把 froala 文本编辑器封装成一个Vue组件,只暴露我们想要的功能

<template>
  <div class="editor-wrap">
    <froala
      :tag="'textarea'"
      :config="config"
      v-model="body.innerHTML"
    />
  </div>
</template>
<script>
import FroalaEditor from 'froala-editor'
export default {
  props: {
    // 显示的工具列表
    placeholder: {
      type: String
    },height: {
      type: Number
    },value: {
      type: String,default: null
    },index: {
      type: Number,default: 1
    },buttons: {
      type: Array,default: () => [
        'undo','redo','clearFormatting','bold','italic','underline','strikeThrough','fontFamily','fontSize','textColor','color','backgroundColor','inlineStyle','paragraphFormat','align','formatOL','formatUL','outdent','indent','quote',// 'insertLink','insertImage',// 'insertVideo',// 'embedly',// 'insertFile','insertTable',// 'emoticons','specialCharacters','insertHR','selectAll','print','spellChecker','html','help','fullscreen','clear','save'
      ]
    }
  },data () {
    const that = this
    return {
      editor: null,uploadImage: {
        loading: false,previewVisible: false,previewImage: '',imgFile: {},isSize: false,isType: false
      },fileList: [],body: {
        innerHTML: this.value,textLen: 0,leftoverLen: 99999999999,sumLen: 999999999,error_tip: '',error_show: false
      },config: {
        codeBeautifierOptions: {
          end_with_newline: true,indent_inner_html: true,extra_liners: "['p','h1','h2','h3','h4','h5','h6','blockquote','pre','ul','ol','table','dl']",brace_style: 'expand',indent_char: ' ',indent_size: 4,wrap_line_length: 0
        },htmlAllowedAttrs: ['.*'],pasteAllowedStyleProps: ['.*'],// fullPage: true,useClasses: false,htmlRemoveTags: [],iframe: true,toolbarButtons: this.buttons,// theme: "dark",//主题
        placeholderText: this.placeholder || '编辑文本',language: 'zh_cn',// 国际化
        imageUploadURL: '',// 上传url
        imageUploadParams: { token: '',i: '',ak: '',f: 9 },fileUploadURL: '',fileUploadParams: { token: '',videoUploadURL: '',videoUploadParams: { token: '',quickInsertButtons: ['image','hr'],// 快速插入项
        // toolbarVisibleWithoutSelection: true,//是否开启 不选中模式
        // disableRightClick: true,//是否屏蔽右击
        colorsHEXInput: true,// 关闭16进制色值
        toolbarSticky: false,// 操作栏是否自动吸顶,// Colors list.
        colorsBackground: [
          '#15E67F','#E3DE8C','#D8A076','#D83762','#76B6D8','REMOVE','#1C7A90','#249CB8','#4ABED9','#FBD75B','#FBE571','#FFFFFF'
        ],colorsStep: 6,colorsText: [
          '#15E67F',zIndex: 2501,height: this.height || '250',// autofocus: true,events: {
          initialized: function () {
            that.editor = this
            that.body.innerHTML = that.value
            that.editorChange()
          },blur: () => {
            that.$emit('blur')
          },contentChanged: () => {
            that.editorChange()
          },'image.beforeUpload': function (images) {
            // 自定义上传图片
            that.beforeUpload(images[0])
            return false
          },'file.beforeUpload': function () {
            // Image was uploaded to the server.
            return true
          },'video.beforeUpload': function () {
            // Image was uploaded to the server.
            return true
          }
        }
      }
    }
  },watch: {
    value: {
      handler: function (news,old) {
        this.body.innerHTML = news
      },deep: true
    },'body.innerHTML': function (newVal,old) {
      if (old !== newVal) {
        let val = this.getSimpleText(this.editor.html.get(true))
        this.editor.html.set(newVal)
      }
    },label: function (newVal,old) {
      if (old !== newVal) {
        this.editor.html.set(newVal)
      }
    }
  },mounted () {
    // 自定义按钮***********************************************************************
    // 清理
    FroalaEditor.DefineIcon('clear',{NAME: 'remove',SVG_KEY: 'remove'})
    FroalaEditor.RegisterCommand('clear',{
      title: '清空',focus: false,undo: true,refreshAfterCallback: true,callback: function () {
        this.html.set(null)
        this.events.focus()
      }
    })
    // 保存
    FroalaEditor.DefineIconTemplate('material_design','<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="floppy-disk" class="svg-inline--fa fa-floppy-disk" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M433.1 129.1l-83.9-83.9C342.3 38.32 327.1 32 316.1 32H64C28.65 32 0 60.65 0 96v320c0 35.35 28.65 64 64 64h320c35.35 0 64-28.65 64-64V163.9C448 152.9 441.7 137.7 433.1 129.1zM224 416c-35.34 0-64-28.66-64-64s28.66-64 64-64s64 28.66 64 64S259.3 416 224 416zM320 208C320 216.8 312.8 224 304 224h-224C71.16 224 64 216.8 64 208v-96C64 103.2 71.16 96 80 96h224C312.8 96 320 103.2 320 112V208z"></path></svg>')
    FroalaEditor.DefineIcon('saveIcon',{NAME: 'save',template: 'material_design'})
    FroalaEditor.RegisterCommand('save',{
      title: '保存',icon: 'saveIcon',callback: () => {
        this.$emit('save',this.body.innerHTML)
      }
    })
    // **********************************************************************************
    setTimeout(() => {
      this.setIndex(this.index)
    },200)
  },methods: {
    saveHtml () {
      this.$emit('save',this.body.innerHTML)
    },// 更改富文本层级
    setIndex (val) {
      this.$nextTick(() => {
        let dv = document.getElementsByClassName('fr-box')
        for (let i in dv) {
          if (!dv[i].style) {
            return
          }
          dv[i].style.cssText = 'z-index:' + val
        }
      })
    },// 富文本中提取纯文本
    getSimpleText: html => {
      var re1 = new RegExp('<p data-f-id="pbf".+?</p>','g') // 匹配html标签的正则表达式,"g"是搜索匹配多个符合的内容
      var msg = html.replace(re1,'') // 执行替换成空字符
      return msg
    },editorChange () {
      if (this.editor == null) return
      this.$emit('change',beforeUpload (file) {
      this.uploadImage.loading = true
      const formData = new FormData()
      formData.append('formFile',file)
      // this.$store
      //   .dispatch('UploadImg',formData)
      //   .then(res => {
      //     this.uploadImage.loading = false
      //     if (res.code === 200) {
      //       this.uploadImage.imgFile = JSON.parse(res.data)
      //       const url = this.uploadImage.imgFile.data
      //       // 插入图片
      //       this.editor.html.insert(
      //         '<img src=' + this.uploadImage.imgFile.data + '>',// HTML
      //         false // 在插入之前是否应清除HTML
      //       ) // 插入图片
      //     } else {
      //       this.fileList = []
      //       this.$message.error(res.msg)
      //     }
      //   })
      //   .catch(() => {
      //     this.uploadImage.loading = false
      //     this.$message.error('上传失败')
      //   })
    }
  }
}

</script>
<style lang="less" scoped>
.editor-wrap {
  width: 100%;
  height: calc(100% - 30px);
  // 去掉底部工具栏logo
  /deep/ .fr-second-toolbar {
    height: 42.19px;
    #fr-logo >span {
      display: none;
    }
    #Layer_1 {
      display: none;
    }
  }
  /deep/ .fr-box {
    height: 100% !important;
    .fr-wrapper {
      height: calc(100% - 93px) !important;
    }
  }
  /deep/ .fr-code {
    height: 100% !important;
  }
}

.fr-wrapper > div[style*="z-index:9999;"],.fr-wrapper > div[style*="z-index: 9999;"] {
  height: 0;
  overflow: hidden;
  position: absolute;
  top: -1000000px;
  opacity: 0;
}
.fr-view{
  position: absolute;
  top: 0;
}
.fr-placeholder{
  margin-top: 0;
}
.fr-box .second-toolbar {
  display: none;
}
.fr-box .second-toolbar #logo {
  width: 0 !important;
  height: 0 !important;
  overflow: hidden;
}
.fr-box .fr-toolbar {
  border-radius: 4px 4px 0 0;
  border-color: #dcdfe6;
}
.fr-box .second-toolbar {
  border-radius: 0 0 4px 4px;
  border-color: #dcdfe6;
}
.fr-box .fr-wrapper {
  border-color: #dcdfe6 !important;
}
</style>
<style>
.fa-floppy-disk {
  width: 18px;
  height: 18px;
}
</style>

具体的用法还要参详一下 froala 官方文档,这里不细表了

后端设计一张数据库表用来保存 html 文本

下面使用刚刚上面封装好的组件对html模板进行保存和更新

<template>
  <div class="page">
    <SplitPane direction="column" :min="10" :max="90" :triggerLength="10" :paneLengthPercent.sync="paneLengthPercent">
     <template v-slot:one>
      <div style="height:100%">
        <div style="height:calc(100% - 40px)">
          <el-table
            stripe
            element-loading-text="数据正在加载中"
            highlight-current-row
            size="small"
            ref="multipleTable"
            border
            :data="tableData"
            height="100%"
            :cell-style="{ 'text-align': 'center' }"
            :header-cell-style="{ 'text-align': 'center' }"
            v-on:select="handleSelectedRow" v-on:row-click="handleSelectedRow"
            @cell-click="cellClick">
            <el-table-column type="index" width="48"></el-table-column>
            <el-table-column label="模板名称" width="350px" prop='template_name'>
              <template slot-scope="scope">
                <el-input v-if="tableData.indexOf(scope.row)  === rowIndex && hearderTitle === '模板名称'"
                  v-model="scope.row.template_name" placeholder="请填写模板名称">
                </el-input>
                <span v-else>{{ scope.row.template_name }}</span>
              </template>
            </el-table-column>
            <template  v-for="item in headerGroup">
              <el-table-column
                :key="item.label"
                v-if="item.show"
                :label="item.label"
                :prop="item.prop"
                :width="item.width"
              ></el-table-column>
            </template>
            <el-table-column label="操作" width="120" fixed="right">
              <template slot="header">
                <el-popover
                  placement="top-start"
                  width="30"
                  trigger="hover"
                  content="点击添加一行">
                  <el-button size="mini" class='el-buttons' type="primary" icon="el-icon-plus" circle slot="reference" @click="renderAddRow"></el-button>
                </el-popover>
              </template>
              <template slot-scope="scope">
                <el-popover width="160" :ref="`popover-${scope.$index}`" placement="top">
                  <p>确定删除该项吗?</p>
                  <div style="text-align: right; margin: 0">
                    <el-button type="text" size="mini" @click="scope._self.$refs[`popover-${scope.$index}`].doClose()">
                      取消
                    </el-button>
                    <el-button type="danger" size="mini" @click="deleteRow(scope.$index,scope.row,scope._self.$refs[`popover-${scope.$index}`])">确定</el-button>
                  </div>
                  <el-button type="danger" class='el-buttons' style="background-color:#F56C6C" slot="reference" icon="el-icon-delete" size="mini" circle></el-button>
                </el-popover>
                <el-popover
                  placement="top-start"
                  width="80px"
                  trigger="hover"
                  content="点击保存这一行">
                  <el-button size="mini" class='el-buttons' style="background-color:#909399" type="info" icon="el-icon-edit" circle slot="reference" @click="addOrUpdateRow(scope.row)"></el-button>
                </el-popover>
              </template>
            </el-table-column>
          </el-table>
        </div>
        <div style="height:40px;padding:5px;">
          <el-pagination
            background
            layout="total,sizes,prev,pager,next"
            :total="GridPageRequest.PageTotal"
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange"
            :page-sizes="[20,30,50,100]"
            :page-size="GridPageRequest.PageSize"
            :current-page.sync="GridPageRequest.PageIndex">
          </el-pagination>
        </div>
      </div>
     </template>
     <template v-slot:two>
       <editor v-model="templateHtml" @save="saveTemplate" @change="changeTemplate"></editor>
     </template>
    </SplitPane>
  </div>
</template>
<script>
import SplitPane from '@/components/draglayhorizontallayouter/SplitPane.vue'
import Editor from '@/components/print/editor.vue'
export default {
  components: { SplitPane,Editor },data () {
    return {
      templateName: null,rowIndex: 0,hearderTitle: '',paneLengthPercent: 40,// 表格
      tableData: [],// 表头
      headerGroup: [
        { label: '主键ID',prop: 'id',show: false,width: '160px' },{ label: '创建人',prop: 'create_by_name',show: true,width: '160' },prop: 'create_by',{ label: '创建时间',prop: 'create_time',{ label: '修改人',prop: 'update_by_name',prop: 'update_by',{ label: '修改时间',prop: 'update_time',width: 'auto' }
      ],// 分页
      GridPageRequest: {
        PageSize: 20,PageIndex: 0,PageTotal: 0
      },selectedRows: [],templateHtml: ''
    }
  },mounted () {
    this.$nextTick(async () => {
      this.onSearch()
    })
  },methods: {
    // 选择行
    handleSelectedRow (selection,row) {
      this.templateHtml = ''
      this.selectedRows = []
      if (Array.isArray(selection)) {
        for (let i = 0; i < selection.length; i++) {
          this.selectedRows.push(selection[i])
        }
      } else { this.selectedRows.push(selection) }
      this.templateHtml = this.selectedRows[0].template_html
    },cellClick (row,column,cell,event) {
      this.rowIndex = this.tableData.indexOf(row)
      this.hearderTitle = column.label

      this.templateHtml = ''
      this.selectedRows = []
      if (Array.isArray(row)) {
        for (let i = 0; i < row.length; i++) {
          this.selectedRows.push(row[i])
        }
      } else { this.selectedRows.push(row) }
      this.templateHtml = this.selectedRows[0].template_html
    },renderAddRow () {
      this.tableData.push({ template_name: null })
    },addOrUpdateRow (row) {
      if (!row.id) {
        row.create_by = localStorage.getItem('user')
      } else {
        row.update_by = localStorage.getItem('user')
      }
      if (!row.template_name || row.template_name.length === 0) {
        this.$message.error('请先填写模板名称!')
        return
      }
      this.axios.post('BasePrintTemplate/AddOrUpdate',row)
        .then((response) => {
          this.$message.success('操作成功!')
          this.getTableData()
        })
        .catch((error) => {
          this.$message({
            message: error.response.data.Message,type: 'warning'
          })
        })
    },deleteRow (index,row,_self) {
      // 关闭气泡提示
      _self.doClose()
      if (!row.id) {
        this.tableData.splice(this.tableData.indexOf(row),1)
        this.$message.success('操作成功!')
        return
      }
      this.axios.post('BasePrintTemplate/Delete',row)
        .then((response) => {
          if (response.data.Data) {
            this.$message.success('操作成功!')
            this.tableData.splice(this.tableData.indexOf(row),1)
          } else {
            this.$message.error('操作失败!')
          }
        })
        .catch((error) => {
          this.$message({
            message: error.response.data.Message,getTableData () {
      this.GridPageRequest.Filter = []
      if (this.templateName != null) {
        this.GridPageRequest.Filter.push({FieldName: 'template_name',ConditionalType: '15',FieldValue: this.templateName})
      }
      this.axios
        .post('BasePrintTemplate/QueryTake',this.GridPageRequest)
        .then((response) => {
          this.tableData = response.data.Data
          this.GridPageRequest.PageTotal = response.data.TotalRows
          this.radio = ''
        })
        .catch((error) => {
          this.$message({
            message: error.response.data.Message,onSearch () {
      this.getTableData()
    },handleSizeChange (val) {
      this.GridPageRequest.PageSize = val
      this.onSearch()
    },handleCurrentChange (val) {
      this.GridPageRequest.PageIndex = val
      this.onSearch()
    },changeTemplate (template) {
      this.templateHtml = template
    },saveTemplate () {
      if (this.selectedRows.length === 0) {
        this.$message({
          message: '请选择一行数据再保存!',type: 'warning'
        })
        return
      }
      this.selectedRows[0].template_html = this.templateHtml
      this.axios.post('BasePrintTemplate/AddOrUpdate',this.selectedRows[0])
        .then((response) => {
          this.$message.success('操作成功!')
          this.getTableData()
        })
        .catch((error) => {
          this.$message({
            message: error.response.data.Message,type: 'warning'
          })
        })
    }
  }
}

</script>
<style lang="less" scoped>
.page {
  height: 100%;
  /deep/ .el-table .el-table__fixed-right .el-table__fixed-header-wrapper .el-table__cell {
    text-align: right !important;
  }
}
</style>
<style src='@/css/el-buttons.css' scoped>
</style>

 绑定Vue的数据源,

<template>
  <top-middle-bottom>
    <template v-slot:header>
      <el-row :gutter="20">
        <el-col :span="6">
          <el-button
            size="medium"
            icon="el-icon-circle-plus-outline"
            type="success"
            v-on:click="onOpenAdd()"
          >
            新 增
          </el-button>
          <el-button
            icon="el-icon-bottom"
            type="info"
            size="medium"
            v-on:click="onImport()"
          >
            批量导入
          </el-button>
          <el-button
            icon="el-icon-printer"
            size="medium"
            v-on:click="onShowTemplate()"
          >
            打印
          </el-button>
        </el-col>
        <el-col :span="18">
          <template>
            <el-radio v-model="search.status" :label="1000">全部</el-radio>
            <el-radio v-model="search.status" :label="1001">启用</el-radio>
            <el-radio v-model="search.status" :label="1002">禁用</el-radio>
          </template>
          <el-input
            size="medium"
            v-model="search.name"
            placeholder="请输入单位名称"
            style="width: 300px; margin-right: 5px"
            clearable
            v-on:clear="clearSearchValue"
          >
          </el-input>
          <el-button
            type="primary"
            v-on:click="onSearch"
            icon="el-icon-search"
            size="medium"
          >
            搜索
          </el-button>
        </el-col>
      </el-row>
    </template>
    <template v-slot:body>
      <el-table
        element-loading-text="数据正在加载中"
        highlight-current-row
        ref="multipleTable"
        border
        stripe
        :data="table.data"
        :cell-style="{ 'text-align': 'center' }"
        :header-cell-style="{ 'text-align': 'center' }"
        height="100%"
      >
        <el-table-column type="index" label="序号" width="50"></el-table-column>
        <el-table-column
          prop="supplier_name"
          show-overflow-tooltip
          label="单位名称"
          width="220px"
        >
        </el-table-column>
        <el-table-column
          prop="supplier_code"
          show-overflow-tooltip
          label="单位代码"
          width="220px"
        >
        </el-table-column>
        <el-table-column prop="supplier_type_text" label="类别" width="100px">
        </el-table-column>
        <el-table-column label="状态" width="65">
          <template slot-scope="scope">
            <el-switch
              v-model="scope.row.bill_status"
              active-color="#13ce66"
              inactive-color="#ff4949"
              :active-value="1001"
              :inactive-value="1002"
              @change="onUpdateStatus(scope.row)"
            >
            </el-switch>
          </template>
        </el-table-column>
        <el-table-column prop="create_time" label="创建时间" width="180px">
        </el-table-column>
        <el-table-column prop="context" label="联系人" width="100px">
        </el-table-column>
        <el-table-column prop="phone" label="电话" width="180px">
        </el-table-column>
        <el-table-column prop="email" label="邮箱" width="300px">
        </el-table-column>
        <el-table-column
          prop="address"
          show-overflow-tooltip
          label="地址"
          width="auto"
        >
        </el-table-column>
        <el-table-column fixed="right" width="180" label="操作" align="left">
          <template slot-scope="scope">
            <el-button
              type="text"
              size="small"
              icon="el-icon-edit"
              v-on:click="onOpenUpdate(scope.row)"
            >
              修改
            </el-button>
            <el-button
              type="text"
              size="small"
              icon="el-icon-delete"
              style="color: #f56c6c"
              v-on:click="onDelete(scope.row)"
            >
              删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </template>
    <template v-slot:footer>
      <el-pagination
        ref="pagination"
        v-on:size-change="onPageSizeChange"
        v-on:current-change="onPageCurrentChange"
        :current-page.sync="GridPageRequest.PageIndex"
        :page-sizes="[20,100,200]"
        :page-size="GridPageRequest.PageSize"
        layout="sizes,next,total"
        :total="table.count"
      >
      </el-pagination>
    </template>
    <template v-slot:dialog>
      <el-dialog title="打印" append-to-body :close-on-press-escape="false"
        :visible.sync = "showPrint"
        :close-on-click-modal="false"
        width="50%">
        <div id="myPrint" v-html="templateHtml"></div>
      </el-dialog>
      <!-- 弹窗 新增 / 修改 -->
      <el-dialog
        :title="form.id ? '修改' : '新增'"
        :visible.sync="form.show"
        :destroy-on-close="true"
        :close-on-press-escape="false"
        :close-on-click-modal="false"
        width="50%"
      >
        <el-form ref="form" :model="form" label-width="100px" :rules="rules">
          <el-row>
            <el-col :span="12">
              <el-form-item label="单位名称" prop="supplier_name">
                <el-input
                  v-model="form.supplier_name"
                  style="width: 300px"
                ></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="单位代码" prop="supplier_code">
                <el-input
                  v-model="form.supplier_code"
                  style="width: 300px"
                ></el-input>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-form-item label="地址">
              <el-input v-model="form.address"></el-input>
            </el-form-item>
          </el-row>
          <el-row>
            <el-col :span="12">
              <el-form-item label="联系人">
                <el-input v-model="form.context"></el-input> </el-form-item
            ></el-col>
            <el-col :span="12">
              <el-form-item label="电话">
                <el-input v-model="form.phone"></el-input> </el-form-item
            ></el-col>
          </el-row>
          <el-row>
            <el-col :span="12">
              <el-form-item label="邮箱">
                <el-input v-model="form.email"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="类别">
                <el-radio v-model="form.supplier_type" :label="1001">
                  供应商
                </el-radio>
                <el-radio v-model="form.supplier_type" :label="1002">
                  客户
                </el-radio>
                <el-radio v-model="form.supplier_type" :label="1003">
                  制造商
                </el-radio>
                <el-radio v-model="form.supplier_type" :label="1004">
                  维保外委
                </el-radio>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-form-item label="备注">
              <el-input
                type="textarea"
                :rows="3"
                placeholder="请输入备注内容"
                v-model="form.remarks"
              >
              </el-input>
            </el-form-item>
          </el-row>
        </el-form>
        <div slot="footer" class="dialog-footer">
          <el-button size="medium" v-on:click="dialogClose()">取 消</el-button>
          <el-button size="medium" type="primary" v-on:click="onFormSubmit()">
            确 定
          </el-button>
        </div>
      </el-dialog>
      <!-- 导出组件 -->
      <import-excel
        :visible="showExcelImport"
        :upload-path="uploadPath"
        :tempfile-url="tempfileUrl"
        tempfile-name="往来单位模板"
        v-on:close="closeExcelImport"
      ></import-excel>
    </template>
  </top-middle-bottom>
</template>
<script>
import axios from 'axios'
import ImportExcel from '@/components/importexcel.vue'
import TopMiddleBottom from '@/components/uilayout/topmiddlebottom.vue'
import Vue from 'vue'
import printJS from 'print-js'
export default {
  components: { ImportExcel,TopMiddleBottom },data () {
    return {
      condition: [],templateHtml: null,tempfileUrl: process.env.ROOT + '/TemplateFiles/单位模板.xls',uploadPath: process.env.ROOT + '/api/Supplier/Import',showPrint: false,showExcelImport: false,// 搜索
      search: {
        name: '',status: 1000
      },// 表格
      table: {
        data: [],count: 0
      },rules: {
        supplier_name: [
          { required: true,message: '请输入单位名称',trigger: 'blur' }
        ],supplier_code: [
          { required: true,message: '请输入单位代码',trigger: 'blur' }
        ]},form: {
        show: false,id: '',supplier_name: '',supplier_code: '',address: '',context: '',phone: '',supplier_type: 1001,bill_status: 1001,email: '',remarks: ''
      },styleNode: ''
    }
  },mounted () {
    this.$nextTick(() => {
      this.getTableData(this.search)
    })
  },methods: {
    // 搜索
    onSearch () {
      const search = this.search
      this.getTableData(search)
    },// 请求表格数据
    getTableData (data) {
      axios
        .get('Supplier/Query',{
          params: {
            name: data.name,status: data.status,pageIndex: this.GridPageRequest.PageIndex,pageSize: this.GridPageRequest.PageSize
          }
        })
        .then((response) => {
          if (response.data.Success === true) {
            this.table.data = response.data.Data
            this.table.count = response.data.TotalRows
          } else {
            // 请求失败
            this.$message.error(response.data.Msg)
          }
        })
        .catch(function (error) {
          this.$message.error(error)
        })
    },// 修改分页大小
    onPageSizeChange (val) {
      this.GridPageRequest.PageSize = val
      this.getTableData(this.search)
    },// 修改分页页数
    onPageCurrentChange (val) {
      this.GridPageRequest.PageIndex = val
      this.getTableData(this.search)
    },// 打开新增
    onOpenAdd () {
      this.dialogClose()
      this.form.show = true
    },// 打开修改
    onOpenUpdate (row) {
      // 赋值form数据
      this.form.id = row.id
      this.form.supplier_name = row.supplier_name
      this.form.supplier_code = row.supplier_code
      this.form.address = row.address
      this.form.context = row.context
      this.form.phone = row.phone
      this.form.supplier_type = row.supplier_type
      this.form.email = row.email
      this.form.remarks = row.remarks
      this.form.bill_status = row.bill_status
      this.form.show = true
    },// 删除
    onDelete (row) {
      const supplierId = row.id
      this.$confirm('此操作将删除该条记录,是否继续?','提示',{
        confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'
      })
        .then(() => {
          axios
            .post(`Supplier/IsDelete?supplierId=${supplierId}`)
            .then((response) => {
              if (response.data.Success === true) {
                this.$message({
                  type: 'success',message: '删除成功!'
                })
                this.getTableData(this.search)
              } else {
                this.$message({
                  type: 'warning',message: `${response.data.Msg}`
                })
              }
            })
            .catch((error) => {
              this.$message.error(error)
            })
        })
        .catch(() => {})
    },// 清除搜索条件
    clearSearchValue () {
      this.search = {
        name: '',status: 1000
      }
    },// 启用/禁用
    onUpdateStatus (row) {
      // 保存点击之后v-modeld的值
      const flag = row.bill_status
      // 保持switch点击前的状态
      row.bill_status = row.bill_status === 1001 ? 1002 : 1001
      this.$confirm('是否确认此操作?',type: 'warning'
      })
        .then(() => {
          const supplierId = row.id
          const status = flag
          axios
            .post(
              `Supplier/UpdateState?supplierId=${supplierId}&status=${status}`
            )
            .then((response) => {
              this.getTableData(this.search)
            })
            .catch((error) => {
              this.$message.error(error)
            })
        })
        .catch(() => {

        })
    },// 表单提交确认
    onFormSubmit () {
      const form = { ...this.form }
      const userId = localStorage.getItem('user')
      axios
        .post('Supplier/AddOrEdit',{
          UserId: userId,Data: {
            id: form.id,supplier_name: form.supplier_name,supplier_code: form.supplier_code,address: form.address,context: form.context,phone: form.phone,supplier_type: form.supplier_type,bill_status: form.bill_status,email: form.email,remarks: form.remarks
          }
        })
        .then((response) => {
          if (response.data.Success === true) {
            this.dialogClose()
            this.getTableData(this.search)
          } else {
            this.$message.error(response.data.Msg)
          }
        })
        .catch((error) => {
          console.log(error)
        })
    },// 导入
    onImport () {
      this.showExcelImport = true
    },// 对话框关闭
    dialogClose () {
      // 初始化form数据
      this.form = {
        show: false,remarks: ''
      }
    },closeExcelImport () {
      this.showExcelImport = false
    },async onShowTemplate () {
      let findTemplate = null
      this.condition = []
      // 查询模板
      this.condition.push({FieldName: 'template_name',ConditionalType: '0',FieldValue: '供应商与客户'})
      await this.axios.post('BasePrintTemplate/QueryCondition',this.condition)
        .then((response) => {
          if (Array.isArray(response.data.Data)) {
            findTemplate = response.data.Data[0].template_html
          }
        })
        .catch((error) => {
          this.$message({
            message: error.response.Message,type: 'warning'
          })
        })
      // 绑定模板
      if (findTemplate) {
        const printData = this.table.data
        const Component = Vue.extend({
          template: `<div>${findTemplate}</div>`,data () {
            return {
              printItems: printData
            }
          },methods: {
          }
        })
        const component = new Component().$mount()
        this.templateHtml = component.$el.innerHTML
        this.showPrint = true
        // 等待渲染完成后再调用打印方法
        this.$nextTick(() => {
          printJS({
            printable: 'myPrint',type: 'html',scanStyles: false,targetStyles: ['*'],// @media print 用来控制打印的样式,边距,横向/纵向,纸张大小等
            style: '@media print {@page {margin: 10px;size: portrait;} body {font-size: 10px;margin: 0px;}}'
          })
          this.showPrint = false
        })
      }
    }
  }
}
</script>

打印预览效果如下

原文地址:https://blog.csdn.net/lee576/article/details/122937418

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


https://segmentfault.com/a/1190000022018995 https://www.jianshu.com/p/8c3599dda094 vuex教程中,有这样一句话和这样一段代码: 实践中,我们会经常用到 ES2015 的参数解构来简化代码(特别是我们需要调用commi
ES6 (ECMAScript 6)中的模块是一个包含 JavaScript 代码的文件,在这个模块中所有的变量都对其他模块是不可见的,除非我们导出它。 ES6的模块系统大致分为导出(export)和导入(import)两个模块。 1、模块导出(export) 可以 导出 所有的最外层 函数 、 类
from https://mp.weixin.qq.com/s/-rc1lYYlsfx-wR4mQmIIQQ Vue知识点汇总(含Vue3) 一、Vue 基础 1. Vue的基本原理 当一个Vue实例创建时,Vue会遍历data中的属性,用 Object.defineProperty(vue3.0使
D:\Temp&gt;npm init vite@latest vue3study --template vuenpm ERR! code ETIMEDOUTnpm ERR! errno ETIMEDOUTnpm ERR! network request to https://registry.np
文章浏览阅读1.2k次。最近自己从零撸起的甘特图组件需要子组件的滚动条同步滚动这就涉及到子组件之间的互相通信,通过 消息总线可以达到我们的需求 ,首先建立一个标志位,拖动左边滚动条的时候,右边的滚动条事件不处理,反之拖动右边滚动条时,左边的滚动条事件不做处理,建立一个公共的变量用于两者的互斥store.jsimport Vue from 'vue'export let store = Vue.observable({ scrollFlag: true})export let mutations =.._vue 能不能同时有两个滚动事件
文章浏览阅读3.3k次,点赞3次,收藏16次。静默打印是什么?简单来说就是不需要用户点击"打印",自动去打印,但是使用浏览器web打印不可避免的要弹出以下画面面对这种问题也只能用"富客户端"技术来解决,在浏览器的沙盒安全模型中无法做到,那么只能使用插件的技术,这个我们就不自己花力气去做了,我找来了 lodop 这个免费的打印组件,功能还是挺强大的,下载下图的发行包解压后安装下图两个exe如果你的系统是64位的,可以安装install_lodop64.exe上图的LodopFuncs.js 是客户端要使用的核心库文件..._this.$getlodop().then((lodop) =>{
文章浏览阅读1.7k次。个人觉得大屏展示其实很简单,噱头多过技术含量,下面使用了 DataV (不是阿里的那个DataV哈,具体链接在这里)开发了一个大屏展示,使用了css flex弹性布局,使用了DataV的一些比较酷炫的边框(SVG写的),基本上功能没有全部完成,但是模子已经刻出来了,只是后端推送的内容没有全部写出来前端<template> <dv-full-screen-container class="screen-container"> <div class="ti_用signalr做一个简单的实时大屏显示
文章浏览阅读3.4k次,点赞3次,收藏10次。【说明】导入的Excel 字体颜色和背景色只能识别【标准色】,别的如"主题颜色",exceljs 解析出来不是颜色值。导入的样式包括字体,字号,列宽,合并单元格,【部分能识别】的背景色,文字颜色。导入到 x-data-spreadsheet 如下图。原Excel样式如下。_x-data-spreadsheet
文章浏览阅读1.7k次。之前参考某文章把 router-view 放在 el-tab-pane 外面都不起作用,问题根本不是出在 el-tab-pane,而是v-for 里面有多个route-view , keep-alive 时 tab 并未销毁掉,而是缓存隐藏了起来。需要把 router-view 的 name 与路由的 index.js 名称对应起来。之前参照很多文章修改试图修正这个问题,结果都徒劳,终于让我找到。我做了如下修改,主页面 main.vue。_el-tab-pane 后面接router-view
文章浏览阅读533次。今天在一台虚拟机上面运行老项目,报各种类型上图的错误提示,一开始还以为是less的问题,结果一个个装完还是报错,后面又说webpack, webpack cli有问题,头有点大了,google 一下,发现一个命令。讨论这个命令的文章,可以了解一下。运行以后终于出现了期待已久的。_npm install 忽略依赖
文章浏览阅读8k次,点赞3次,收藏12次。从这篇文章得到启发先定义一个组件从外部接收Template,然后在组件里调用<template > <div ref="markedContent"></div></template><script>import Vue from 'vue/dist/vue.esm.js'export default { name: 'wf-marked-content', props: ['content'], mounte.._vue components 动态传入模板
文章浏览阅读5.4k次。参考上一篇知识开发的一个功能,制作一个打印模板的管理模块,如下(就是保存froala编辑后的html文本,其中包括Vue的Template,这样我们可以利用Vue的模板的优势来动态绑定一些数据源进行HTML的打印,基本上跟过去水晶报表做一个模板再绑定数据源的方法异曲同工)在 main.js 里引用 froala 组件// Import and use Vue Froala lib.import VueFroala from 'vue-froala-wysiwyg'// 引入 Fr.._vue设计网页打印模板
文章浏览阅读992次。计划是这样,公司的项目一直在持续改动,安装包总是需要频繁生成新的,由此我想到了"持续集成"!有自动化工具不用,岂不可惜?这周的主要时间就用来学习CruiseControl.Net全面实现持续集成_怎么在vue的 script部分使用 eldigloa
文章浏览阅读1.2k次。其实Element UI 只用了文字提示的 el-tooltip 组件,不喜欢可以去掉,不记得是从哪拿到的原始代码,我给加了高亮渐变显示,图标,和拖拽时只能拖拽图标的位置,效果如上图,可以水平方向拖动,也可以垂直方向拖动。样式是less写的,css写嵌套样式太繁琐了。拿来主义,改造有理!下面贴代码<template> <div ref="splitPane" class="split-pane" :class="direction" :"{ fl..._element ui拉条样式
文章浏览阅读953次,点赞2次,收藏2次。接上一篇,这次加入的是从x-speadsheet导出Excel,并且带有x-speadsheet中的样式,重点关注 exportExcel 这个方法,我加入了 tinycolor 这个库用来翻译颜色值,值得注意的是, exceljs的颜色值是 argb 不是 rgba,一定不要弄混了a 是代表的透明度放在最前面_x-data-spreadsheet 导出
文章浏览阅读5.5k次,点赞2次,收藏21次。尝试了两个连线库 jsplumb 和 leadline ,其实两个库都很强大,但是基于个人使用的习惯,决定还是用 leadline ,在Vue 下我使用它的一个包装库 leader-line-vue 下面是上图的连接线示例代码,连接线很轻松的就实现了一个渐变效果..._vue 连线
文章浏览阅读4.2k次,点赞2次,收藏5次。首先官网推荐的安装方法没有生成dist文件,导致样式表等这些文件并没有生成npm install element-plus --save以上方法是有问题的,如果不幸执行了上面的命令,那么先执行卸载npm uninstall element-plus删除 main.js文件对element ui的引用,输入以下命令vue add element-plus..._elementui3.0
文章浏览阅读3.1k次。如上图,下面贴代码<template> <div> <el-date-picker size="large" style ="width:120px" v-model="selectYear" format="yyyy 年" value-format="yyyy" type="year" :clearable = "false" placeholder="选择年">.._vue多选周
文章浏览阅读1.8k次,点赞6次,收藏6次。经过 2021年的一个春节,从年前到现在,大致撸出一个 甘特图,进度条是用SVG画的,使用了几个工具库 (interactjs 用来处理拖拽和修改尺寸,snap.svg 用来处理 svg 的dom 操作,moment.js用来处理时间的操作),其他没有依赖任何的UI组件,目前初见雏形,还比较粗糙,后面会不断更新源码地址点击期间也摸索了怎么把vs code的项目上传到 GitHub 上面进行源代码的管理,基本上是参考的这篇文章做的..._vue gantt demo
文章浏览阅读2.1k次。接上两篇vue 下使用 exceljs + x-spreadsheet 带样式导入Excelvue 下使用 exceljs + x-spreadsheet 带样式导出Excel下面封装好一个组件调用组件的页面效果如图,目前“导出Json”还没有做_x-spreadsheet导入导出