guhongwei před 3 roky
rodič
revize
a1fb4a3838

+ 90 - 0
src/unit/htmlToPdf.js

@@ -0,0 +1,90 @@
+//不使用JQuery版的
+
+import html2canvas from 'html2canvas';
+import JsPDF from 'jspdf';
+
+/**
+ * @param  ele          要生成 pdf 的DOM元素(容器)
+ * @param  padfName     PDF文件生成后的文件名字
+ * */
+
+function downloadPDF(ele, pdfName) {
+  let eleW = ele.offsetWidth; // 获得该容器的宽
+  let eleH = ele.offsetHeight; // 获得该容器的高
+
+  let eleOffsetTop = ele.offsetTop; // 获得该容器到文档顶部的距离
+  let eleOffsetLeft = ele.offsetLeft; // 获得该容器到文档最左的距离
+
+  var canvas = document.createElement('canvas');
+  var abs = 0;
+
+  let win_in = document.documentElement.clientWidth || document.body.clientWidth; // 获得当前可视窗口的宽度(不包含滚动条)
+  let win_out = window.innerWidth; // 获得当前窗口的宽度(包含滚动条)
+
+  if (win_out > win_in) {
+    // abs = (win_o - win_i)/2;    // 获得滚动条长度的一半
+    abs = (win_out - win_in) / 2; // 获得滚动条宽度的一半
+    // console.log(a, '新abs');
+  }
+
+  canvas.width = eleW * 2; // 将画布宽&&高放大两倍
+  canvas.height = eleH * 2;
+
+  var context = canvas.getContext('2d');
+
+  context.scale(2, 2);
+
+  context.translate(-eleOffsetLeft - abs, -eleOffsetTop);
+  // 这里默认横向没有滚动条的情况,因为offset.left(),有无滚动条的时候存在差值,因此
+  // translate的时候,要把这个差值去掉
+
+  // html2canvas(element).then( (canvas)=>{ //报错
+  // html2canvas(element[0]).then( (canvas)=>{
+  html2canvas(ele, {
+    dpi: 300,
+    // allowTaint: true,  //允许 canvas 污染, allowTaint参数要去掉,否则是无法通过toDataURL导出canvas数据的
+    useCORS: true, //允许canvas画布内 可以跨域请求外部链接图片, 允许跨域请求。
+  }).then(canvas => {
+    var contentWidth = canvas.width;
+    var contentHeight = canvas.height;
+    //一页pdf显示html页面生成的canvas高度;
+    var pageHeight = (contentWidth / 592.28) * 841.89;
+    //未生成pdf的html页面高度
+    var leftHeight = contentHeight;
+    //页面偏移
+    var position = 0;
+    //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
+    var imgWidth = 595.28;
+    var imgHeight = (595.28 / contentWidth) * contentHeight;
+
+    var pageData = canvas.toDataURL('image/jpeg', 1.0);
+
+    var pdf = new JsPDF('', 'pt', 'a4');
+
+    //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
+    //当内容未超过pdf一页显示的范围,无需分页
+    if (leftHeight < pageHeight) {
+      //在pdf.addImage(pageData, 'JPEG', 左,上,宽度,高度)设置在pdf中显示;
+      pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
+      // pdf.addImage(pageData, 'JPEG', 20, 40, imgWidth, imgHeight);
+    } else {
+      // 分页
+      while (leftHeight > 0) {
+        pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight);
+        leftHeight -= pageHeight;
+        position -= 841.89;
+        //避免添加空白页
+        if (leftHeight > 0) {
+          pdf.addPage();
+        }
+      }
+    }
+
+    //可动态生成
+    pdf.save(pdfName);
+  });
+}
+
+export default {
+  downloadPDF,
+};

+ 109 - 0
src/unit/htmlToPdfJQ.js

@@ -0,0 +1,109 @@
+//使用JQuery方式写的。
+
+import html2canvas from 'html2canvas';
+import JsPDF from 'jspdf';
+import $ from 'jquery';
+
+// console.log($, '这是什么什么');
+
+
+
+function download(ele){
+    var element = $("#demo");    // 这个dom元素是要导出pdf的div容器
+    console.log(element,'1212122');
+
+
+    // var element = ele;    // 这个dom元素是要导出pdf的div容器
+
+    var w = element.width();    // 获得该容器的宽
+    var h = element.height();    // 获得该容器的高
+
+
+
+    var offsetTop = element.offset().top;    // 获得该容器到文档顶部的距离
+    var offsetLeft = element.offset().left;    // 获得该容器到文档最左的距离
+
+    console.log(offsetTop,'------',offsetLeft);
+
+
+
+
+    var canvas = document.createElement("canvas");
+    var abs = 0;
+    var win_i = $(window).width();    // 获得当前可视窗口的宽度(不包含滚动条)
+    var win_o = window.innerWidth;    // 获得当前窗口的宽度(包含滚动条)
+
+    console.log(canvas, abs, win_i, win_o);
+
+
+
+
+
+    if(win_o>win_i){
+        abs = (win_o - win_i)/2;    // 获得滚动条长度的一半
+    }
+
+
+
+
+
+
+    canvas.width = w * 2;    // 将画布宽&&高放大两倍
+    canvas.height = h * 2;
+    var context = canvas.getContext("2d");
+    context.scale(2, 2);
+    context.translate(-offsetLeft-abs,-offsetTop);
+    // 这里默认横向没有滚动条的情况,因为offset.left(),有无滚动条的时候存在差值,因此
+    // translate的时候,要把这个差值去掉
+
+    // html2canvas(element).then( (canvas)=>{ //报错
+    html2canvas(element[0]).then( (canvas)=>{
+
+        var contentWidth = canvas.width;
+        var contentHeight = canvas.height;
+        //一页pdf显示html页面生成的canvas高度;
+        var pageHeight = contentWidth / 592.28 * 841.89;
+        //未生成pdf的html页面高度
+        var leftHeight = contentHeight;
+        //页面偏移
+        var position = 0;
+        //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
+        var imgWidth = 595.28;
+        var imgHeight = 592.28/contentWidth * contentHeight;
+
+        var pageData = canvas.toDataURL('image/jpeg', 1.0);
+
+
+
+
+        var pdf = new JsPDF('', 'pt', 'a4');
+
+        //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
+        //当内容未超过pdf一页显示的范围,无需分页
+        if (leftHeight < pageHeight) {
+            pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
+        } else {    // 分页
+            while(leftHeight > 0) {
+                pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
+                leftHeight -= pageHeight;
+                position -= 841.89;
+                //避免添加空白页
+                if(leftHeight > 0) {
+                    pdf.addPage();
+                }
+            }
+        }
+
+
+
+        pdf.save('我的简历.pdf');
+    })
+
+
+
+}
+
+
+export default {
+    download
+}

+ 3 - 3
src/views/adminCenter/collect/index.vue

@@ -46,7 +46,7 @@ export default {
       ],
       opera: [
         { label: '查看信息', method: 'view' },
-        { label: '收货', method: 'sign', display: (i) => i.status == '0' },
+        { label: '收货', method: 'sign', display: (i) => i.status == '0' && i.type == '0' },
       ],
       // 弹框
       dialog: { title: '详细信息', show: false, type: '1', width: '50%' },
@@ -61,7 +61,7 @@ export default {
     ...mapBuy(['query', 'update']),
     ...market_enter(['create']),
     async search({ skip = 0, limit = 10, ...info } = {}) {
-      let res = await this.query({ skip, limit, type: '0', ...info });
+      let res = await this.query({ skip, limit, ...info });
       if (this.$checkRes(res)) {
         this.$set(this, `list`, res.data);
         this.$set(this, `total`, res.total);
@@ -72,7 +72,7 @@ export default {
     },
     // 查看信息
     toView({ data }) {
-      data.type = data.type == '0' ? '统一收' : '申请人';
+      data.type_copy = data.type == '0' ? '统一收' : '申请人';
       this.$set(this, `info`, data);
       this.dialog = { title: '详细信息', show: true, type: '1', width: '50%' };
     },

+ 2 - 2
src/views/adminCenter/finance/index.vue

@@ -58,7 +58,7 @@ export default {
   methods: {
     ...mapBuy(['query', 'update']),
     async search({ skip = 0, limit = 10, ...info } = {}) {
-      let res = await this.query({ skip, limit, type: '0', status: '2', ...info });
+      let res = await this.query({ skip, limit, status: '2', ...info });
       if (this.$checkRes(res)) {
         this.$set(this, `list`, res.data);
         this.$set(this, `total`, res.total);
@@ -69,7 +69,7 @@ export default {
     },
     // 查看信息
     toView({ data }) {
-      data.type = data.type == '0' ? '统一收' : '申请人';
+      data.type_copy = data.type == '0' ? '统一收' : '申请人';
       this.$set(this, `info`, data);
       this.dialog = { title: '详细信息', show: true, type: '1', width: '50%' };
     },

+ 107 - 21
src/views/adminCenter/purchase/index.vue

@@ -1,10 +1,41 @@
 <template>
   <div id="index">
-    <data-table :fields="fields" :opera="opera" @query="search" :data="list" :total="total" @view="toView" @sign="toSign">
-      <template slot="selfbtn">
-        <el-button type="primary" size="small" @click="toAdd()">添加</el-button>
-      </template>
-    </data-table>
+    <el-row v-if="show_type == '1'">
+      <data-table
+        :fields="fields"
+        :opera="opera"
+        @query="search"
+        :data="list"
+        :total="total"
+        @view="toView"
+        :select="true"
+        :selected="selected"
+        @handleSelect="handleSelect"
+      >
+        <template slot="selfbtn">
+          <el-button type="primary" size="small" @click="toExport()">导出采购单</el-button>
+          <el-button type="primary" size="small" @click="toAdd()">添加</el-button>
+        </template>
+        <template #options="{ item }">
+          <template v-if="item.prop === 'status'">
+            <el-option v-for="(item, index) in statusList" :key="index" :label="item.name" :value="item.num"></el-option>
+          </template>
+        </template>
+      </data-table>
+    </el-row>
+    <el-row v-else-if="show_type == '2'">
+      <el-col :span="24" class="two">
+        <el-col :span="24" class="btn">
+          <el-button type="primary" size="small" @click="back()">返回</el-button>
+          <el-button type="primary" size="mini" @click="download()">PDF下载</el-button>
+        </el-col>
+        <el-col :span="24" class="info">
+          <el-col :span="24" class="demo" id="demo">
+            <pdf-1 :selected="selected"></pdf-1>
+          </el-col>
+        </el-col>
+      </el-col>
+    </el-row>
     <el-dialog :title="dialog.title" :visible.sync="dialog.show" :width="dialog.width" :before-close="toClose" :close-on-click-modal="false">
       <info-1 v-if="dialog.type == '1'" :info="info"></info-1>
     </el-dialog>
@@ -12,7 +43,9 @@
 </template>
 
 <script>
+import htmlToPdf from '@/unit/htmlToPdf.js';
 import info1 from './parts/info-1.vue';
+import pdf1 from './parts/pdf-1.vue';
 import { mapState, createNamespacedHelpers } from 'vuex';
 const { mapActions: mapBuy } = createNamespacedHelpers('buy');
 export default {
@@ -20,9 +53,12 @@ export default {
   props: {},
   components: {
     info1,
+    pdf1,
   },
   data: function () {
     return {
+      // 页面展示
+      show_type: '1',
       list: [],
       total: 0,
       fields: [
@@ -41,19 +77,38 @@ export default {
         {
           label: '审核状态',
           prop: 'status',
+          filter: 'select',
           format: (i) => {
             return i === '0' ? '待收货' : i === '1' ? '已收货,待付款' : i === '2' ? '付款中,待财务确认' : '财务确认,已付款';
           },
         },
       ],
-      opera: [
-        { label: '查看信息', method: 'view' },
-        // { label: '收货', method: 'sign' },
-      ],
+      opera: [{ label: '查看信息', method: 'view' }],
       // 弹框
       dialog: { title: '详细信息', show: false, type: '1', width: '50%' },
       // 详细信息
       info: {},
+      // 导出
+      selected: [],
+      // 审核状态
+      statusList: [
+        {
+          name: '待收货',
+          num: '0',
+        },
+        {
+          name: '已收货,待付款',
+          num: '1',
+        },
+        {
+          name: '付款中,待财务确认',
+          num: '2',
+        },
+        {
+          name: '财务确认,已付款',
+          num: '3',
+        },
+      ],
     };
   },
   created() {
@@ -73,7 +128,7 @@ export default {
     },
     // 查看信息
     toView({ data }) {
-      data.type = data.type == '0' ? '统一收' : '申请人';
+      data.type_copy = data.type == '0' ? '统一收' : '申请人';
       this.$set(this, `info`, data);
       this.dialog = { title: '详细信息', show: true, type: '1', width: '50%' };
     },
@@ -81,22 +136,40 @@ export default {
     toClose() {
       this.dialog = { title: '详细信息', show: false, type: '1', width: '50%' };
     },
-    // 收货
-    async toSign({ data }) {
-      this.$confirm(`您确认提交签收吗?`, '提示', {
+    // 导出
+    handleSelect(data) {
+      for (const val of data) {
+        val.type_copy = val.type == '0' ? '统一收' : '申请人';
+      }
+      this.$set(this, `selected`, data);
+    },
+    // 导出采购单
+    toExport() {
+      if (this.selected.length > 0) {
+        this.show_type = '2';
+      } else {
+        this.$message({ type: `error`, message: `缺少导出必填项` });
+      }
+    },
+    // 返回
+    back() {
+      this.show_type = '1';
+      this.search();
+    },
+    // pdf下载
+    async download() {
+      this.$confirm(`您确认提交下载采购审批单吗?`, '提示', {
         confirmButtonText: '确定',
         cancelButtonText: '取消',
         type: 'warning',
       })
         .then(async () => {
-          data.status = '1';
-          let res = await this.update(data);
-          if (this.$checkRes(res)) {
-            this.$message({ type: `success`, message: `操作成功` });
-            this.search();
-          } else {
-            this.$message({ type: `error`, message: `${res.errmsg}` });
+          for (const val of this.selected) {
+            val.status = '2';
+            let res = await this.update(val);
+            if (this.$checkRes(res, '提交成功', '提交失败'));
           }
+          htmlToPdf.downloadPDF(document.querySelector('#demo'), '采购单');
         })
         .catch(() => {});
     },
@@ -117,4 +190,17 @@ export default {
 };
 </script>
 
-<style lang="less" scoped></style>
+<style lang="less" scoped>
+.two {
+  .btn {
+    margin: 0 0 10px 0;
+  }
+  .info {
+    padding: 0 200px;
+    .demo {
+      border: 1px solid #000;
+      border-bottom: none;
+    }
+  }
+}
+</style>

+ 2 - 2
src/views/adminCenter/purchase/parts/info-1.vue

@@ -25,8 +25,8 @@
               </el-form-item>
             </el-col>
             <el-col :span="12">
-              <el-form-item label="收货类型" prop="type">
-                <el-input v-model="info.type" readonly></el-input>
+              <el-form-item label="收货类型" prop="type_copy">
+                <el-input v-model="info.type_copy" readonly></el-input>
               </el-form-item>
             </el-col>
             <el-col :span="12">

+ 138 - 0
src/views/adminCenter/purchase/parts/pdf-1.vue

@@ -0,0 +1,138 @@
+<template>
+  <div id="pdf-1">
+    <el-row>
+      <el-col :span="24" class="main">
+        <el-col :span="24" class="one">
+          <el-col :span="24" class="title"> 商品采购审批表 </el-col>
+          <el-col :span="24" class="one_1">
+            <el-col :span="24" class="one_1one"> 签字: </el-col>
+            <el-col :span="24" class="one_1one"> 签字日期:<span>年</span><span>月</span><span>日</span> </el-col>
+          </el-col>
+        </el-col>
+        <el-col :span="24" class="two">
+          <el-col :span="24" class="title"> 采购商品信息 </el-col>
+          <el-col :span="24" class="two_2">
+            <el-col :span="24" class="list" v-for="(item, index) in selected" :key="index">
+              <el-form label-width="100px">
+                <el-col :span="12">
+                  <el-form-item label="采购人">
+                    <el-input v-model="item.buy_personal" readonly></el-input>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="采购电话">
+                    <el-input v-model="item.buy_phone" readonly></el-input>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="收货人">
+                    <el-input v-model="item.receive_personal" readonly></el-input>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="收货电话">
+                    <el-input v-model="item.receive_phone" readonly></el-input>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="收货类型">
+                    <el-input v-model="item.type_copy" readonly></el-input>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="收货地址">
+                    <el-input v-model="item.receive_address" readonly></el-input>
+                  </el-form-item>
+                </el-col>
+                <!-- <el-col :span="24"> 采购时间:{{ getDate(item.meta.createdAt) }} </el-col> -->
+                <el-col :span="24">
+                  <data-table :fields="fields" :data="item.order" :usePage="false"> </data-table>
+                </el-col>
+              </el-form>
+            </el-col>
+          </el-col>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+import moment from 'moment';
+export default {
+  name: 'pdf-1',
+  props: {
+    selected: { type: Array },
+  },
+  components: {},
+  data: function () {
+    return {
+      fields: [
+        { label: '商品名称', prop: 'name' },
+        { label: '商品单价(元)', prop: 'money' },
+        { label: '采购数量', prop: 'buy_num' },
+      ],
+    };
+  },
+  created() {},
+  methods: {
+    // // 过滤时间
+    // getDate(val) {
+    //   let newDate = moment(val).format('YYYY-MM-DD');
+    //   if (newDate) return newDate;
+    // },
+  },
+
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .one {
+    .title {
+      text-align: center;
+      font-size: 25px;
+      padding: 20px 0;
+    }
+    .one_1 {
+      padding: 0 200px;
+      .one_1one {
+        padding: 10px 0;
+        span {
+          padding: 0 30px;
+        }
+      }
+    }
+  }
+  .two {
+    .title {
+      text-align: center;
+      font-size: 25px;
+      padding: 20px 0;
+    }
+    .two_2 {
+      padding: 0 200px;
+      .list {
+        border: 1px solid #ccc;
+        border-radius: 8px;
+        margin: 0 0 10px 0;
+        padding: 10px;
+      }
+    }
+  }
+}
+</style>