skyouc
2025-02-26 4e4df454d73c63137196c0b20238aab0a711d839
no message
29个文件已添加
24636 ■■■■■ 已修改文件
construction-data/.browserslistrc 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/.editorconfig 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/.env.development 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/.env.production 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/.eslintrc.js 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/README.md 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/babel.config.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/package-lock.json 14095 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/package.json 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/postcss.config.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/public/favicon.ico 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/public/index.html 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/src/App.vue 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/src/assets/common.less 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/src/components/datav/cards.vue 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/src/components/datav/digitalFlop.vue 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/src/components/datav/img/bg.png 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/src/components/datav/index.vue 759 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/src/components/datav/rankingBoard.vue 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/src/components/datav/roseChart.vue 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/src/components/datav/scrollBoard.vue 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/src/components/datav/topHeader.vue 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/src/components/datav/waterLevelChart.vue 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/src/main.js 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/src/utils/ajax.js 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/src/utils/utilPrint.js 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/src/utils/utilVue.js 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/vue.config.js 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/yarn.lock 8428 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
construction-data/.browserslistrc
New file
@@ -0,0 +1,2 @@
> 1%
last 2 versions
construction-data/.editorconfig
New file
@@ -0,0 +1,5 @@
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
construction-data/.env.development
New file
@@ -0,0 +1,14 @@
# just a flag
ENV = 'development'
# base api
VUE_APP_BASE_API = 'http://127.0.0.1:8081/'
# vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable,
# to control whether the babel-plugin-dynamic-import-node plugin is enabled.
# It only does one thing by converting all import() to require().
# This configuration can significantly increase the speed of hot updates,
# when you have a large number of pages.
# Detail:  https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js
VUE_CLI_BABEL_TRANSPILE_MODULES = true
construction-data/.env.production
New file
@@ -0,0 +1,8 @@
# just a flag
ENV = 'production'
# base api
VUE_APP_BASE_API = 'http://47.76.147.249:8081/'
#VUE_APP_BASE_API = 'http://127.0.0.1:8081/'
construction-data/.eslintrc.js
New file
@@ -0,0 +1,17 @@
module.exports = {
  root: true,
  env: {
    node: true
  },
  'extends': [
    'plugin:vue/essential',
    '@vue/standard'
  ],
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
  },
  parserOptions: {
    parser: 'babel-eslint'
  }
}
construction-data/README.md
New file
@@ -0,0 +1,16 @@
<h1 align="center">Construction-Data</h1>
## Project setup
```
yarn install
```
### Compiles and hot-reloads for development
```
yarn run dev
```
### Compiles and minifies for production
```
yarn run build
```
construction-data/babel.config.js
New file
@@ -0,0 +1,5 @@
module.exports = {
  presets: [
    '@vue/app'
  ]
}
construction-data/package-lock.json
New file
Diff too large
construction-data/package.json
New file
@@ -0,0 +1,31 @@
{
  "name": "adev",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "@jiaminghi/data-view": "^2.10.0",
    "axios": "^1.7.9",
    "element-ui": "^2.15.14",
    "html2canvas": "^1.4.1",
    "jspdf": "^2.5.2",
    "less": "^3.9.0",
    "less-loader": "^5.0.0",
    "print-js": "^1.6.0",
    "vue": "^2.6.10"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.8.0",
    "@vue/cli-plugin-eslint": "^3.8.0",
    "@vue/cli-service": "^3.8.0",
    "@vue/eslint-config-standard": "^4.0.0",
    "babel-eslint": "^10.0.1",
    "eslint": "^5.16.0",
    "eslint-plugin-vue": "^5.0.0",
    "vue-template-compiler": "^2.6.10"
  }
}
construction-data/postcss.config.js
New file
@@ -0,0 +1,5 @@
module.exports = {
  plugins: {
    autoprefixer: {}
  }
}
construction-data/public/favicon.ico
construction-data/public/index.html
New file
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title>DATAV-DEMO</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but adev doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>
construction-data/src/App.vue
New file
@@ -0,0 +1,31 @@
<template>
  <div id="app">
    <datav />
  </div>
</template>
<script>
import datav from './components/datav/index.vue'
export default {
  name: 'app',
  components: {
    datav
  },
  data () {
    return {}
  }
}
</script>
<style lang="less">
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  width: 100%;
  height: 100%;
}
</style>
construction-data/src/assets/common.less
New file
@@ -0,0 +1,6 @@
html, body {
  width: 100%;
  height: 100%;
  padding: 0px;
  margin: 0px;
}
construction-data/src/components/datav/cards.vue
New file
@@ -0,0 +1,189 @@
<template>
  <div id="cards">
    <div
      class="card-item"
      v-for="(card, i) in cards"
      :key="card.title"
    >
      <div class="card-header">
        <div class="card-header-left">{{ card.title }}</div>
        <div class="card-header-right">{{ '0' + (i + 1) }}</div>
      </div>
      <dv-charts class="ring-charts" :option="card.ring" />
      <div class="card-footer">
        <div class="card-footer-item">
          <div class="footer-title">累计金额</div>
          <div class="footer-detail">
            <dv-digital-flop :config="card.total" style="width:70%;height:35px;" />元
          </div>
        </div>
        <div class="card-footer-item">
          <div class="footer-title">巡查病害</div>
          <div class="footer-detail">
            <dv-digital-flop :config="card.num" style="width:70%;height:35px;" />处
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: 'Cards',
  data () {
    return {
      cards: []
    }
  },
  methods: {
    createData () {
      const { randomExtend } = this
      this.cards = new Array(5).fill(0).map((foo, i) => ({
        title: '测试路段' + (i + i),
        total: {
          number: [randomExtend(9000, 10000)],
          content: '{nt}',
          textAlign: 'right',
          style: {
            fill: '#ea6027',
            fontWeight: 'bold'
          }
        },
        num: {
          number: [randomExtend(30, 60)],
          content: '{nt}',
          textAlign: 'right',
          style: {
            fill: '#26fcd8',
            fontWeight: 'bold'
          }
        },
        ring: {
          series: [
            {
              type: 'gauge',
              startAngle: -Math.PI / 2,
              endAngle: Math.PI * 1.5,
              arcLineWidth: 13,
              radius: '80%',
              data: [
                { name: '资金占比', value: randomExtend(40, 60) }
              ],
              axisLabel: {
                show: false
              },
              axisTick: {
                show: false
              },
              pointer: {
                show: false
              },
              backgroundArc: {
                style: {
                  stroke: '#224590'
                }
              },
              details: {
                show: true,
                formatter: '资金占比{value}%',
                style: {
                  fill: '#1ed3e5',
                  fontSize: 20
                }
              }
            }
          ],
          color: ['#03d3ec']
        }
      }))
    },
    randomExtend (minNum, maxNum) {
      if (arguments.length === 1) {
        return parseInt(Math.random() * minNum + 1, 10)
      } else {
        return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10)
      }
    }
  },
  mounted () {
    const { createData } = this
    createData()
    setInterval(this.createData, 30000)
  }
}
</script>
<style lang="less">
#cards {
  display: flex;
  justify-content: space-between;
  height: 45%;
  .card-item {
    background-color: rgba(6, 30, 93, 0.5);
    border-top: 2px solid rgba(1, 153, 209, .5);
    width: 19%;
    display: flex;
    flex-direction: column;
  }
  .card-header {
    display: flex;
    height: 20%;
    align-items: center;
    justify-content: space-between;
    .card-header-left {
      font-size: 18px;
      font-weight: bold;
      padding-left: 20px;
    }
    .card-header-right {
      padding-right: 20px;
      font-size: 40px;
      color: #03d3ec;
    }
  }
  .ring-charts {
    height: 55%;
  }
  .card-footer {
    height: 25%;
    display: flex;
    align-items: center;
    justify-content: space-around;
  }
  .card-footer-item {
    padding: 5px 10px 0px 10px;
    box-sizing: border-box;
    width: 40%;
    background-color: rgba(6, 30, 93, 0.7);
    border-radius: 3px;
    .footer-title {
      font-size: 15px;
      margin-bottom: 5px;
    }
    .footer-detail {
      font-size: 20px;
      color: #1294fb;
      display: flex;
      font-size: 18px;
      align-items: center;
      .dv-digital-flop {
        margin-right: 5px;
      }
    }
  }
}
</style>
construction-data/src/components/datav/digitalFlop.vue
New file
@@ -0,0 +1,123 @@
<template>
  <div id="digital-flop">
    <div class="digital-flop-item" v-for="(item,index) in Object.keys(waveLabels)" :key="index">
      <div class="digital-flop-title">{{ waveLabels[item] }}</div>
      <div class="digital-flop">
        <div class="cell-item" v-if="item == 'workQty'">
          {{data.anfme - data[item]}}
        </div>
        <div class="cell-item" v-else>
          {{data[item]}}
        </div>
      </div>
    </div>
    <dv-decoration-10 />
  </div>
</template>
<script>
export default {
  name: 'DigitalFlop',
  data () {
    return {
      digitalFlopData: [],
      waveLabels: {
        orderNo: '订单编号',
        siteNo: '播种位',
        matnr: '物料编号',
        batch: '批号',
        waveNo: '波次',
        anfme: '总需求数量',
        workQty: '剩余需求数量',
      }
    }
  },
  props: {
    data: Object
  },
  methods: {
  },
  mounted () {
    // setInterval(createData, 30000)
  }
}
</script>
<style lang="less">
  @media screen and (max-width: 1920px){
    #digital-flop {
      position: relative;
      height: 100%;
      flex-shrink: 0;
      display: flex;
      justify-content: space-between;
      align-items: center;
      background-color: rgba(6, 30, 93, 0.5);
      .dv-decoration-10 {
        position: absolute;
        width: 95%;
        left: 2.5%;
        height: 5px;
        bottom: 0px;
      }
      .digital-flop-item {
        width: auto;
        padding: 10px;
        height: 80%;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
      }
      .digital-flop-title {
        padding: 10px;
        font-size: 20px;
      }
      .digital-flop {
        display: flex;
        .cell-item {
          height: 50px;
          text-align: center;
          font-style: italic;
          font-size: 18px;
        }
      }
      .unit {
        margin-left: 10px;
        display: flex;
        align-items: flex-end;
        box-sizing: border-box;
        padding-bottom: 13px;
      }
    }
  }
  @media screen and (max-width: 1280px) {
    #digital-flop{
      .digital-flop-title {
        padding: 10px;
        font-size: 14px;
      }
      .digital-flop {
        .cell-item {
          font-size: 12px;
          height: unset;
        }
      }
    }
  }
</style>
construction-data/src/components/datav/img/bg.png
construction-data/src/components/datav/index.vue
New file
@@ -0,0 +1,759 @@
<template>
  <div id="data-view" >
    <dv-full-screen-container>
      <top-header/>
      <template>
        <el-row>
          <el-col :span="15">
            <el-row class="header-left-task" type="flex" align="center" >
              <el-col v-for="(task, index) in tasks" :key="index" >
                <el-row type="flex" align="center" justify="center" @click.native="borderClick(task)">
                  <dv-border-box-2>
                    <el-card class="header-card" >
                      <el-row>
                        <el-col :span="4" v-for="(item, index) in Object.keys(waveLabels)" :key="index">
                          <div class="header-card-label" >{{ waveLabels[item] }}</div>
                          <div class="header-card-val" >{{ task[item] }}</div>
                        </el-col>
                      </el-row>
                    </el-card>
                  </dv-border-box-2>
                </el-row>
              </el-col>
            </el-row>
            <el-row class="detl-info-row">
              <dv-border-box-2>
                <el-card>
                  <el-table :data="mergeTaskDetl" class="talbe-matnr" @row-click="selectRow">
                    <el-table-column v-for="(item,index) in Object.keys(detlLabels)" :prop="item" :label="detlLabels[item]" :key="index"/>
                  </el-table>
                </el-card>
              </dv-border-box-2>
            </el-row>
            <el-row class="pick-order-detl">
              <dv-border-box11 title="拣货订单明细" class="detl-box-border">
                <div class="detl-box-padding"></div>
                <div class="order-detl-list">
                  <div class="main-content" v-for="(item,index) in taskDetls" @click="selectView(item)" :key="index">
                    <digital-flop :data = item />
                  </div>
                </div>
              </dv-border-box11>
            </el-row>
          </el-col>
          <el-col :span="9">
              <el-row class="order-box-row">
                <dv-border-box-11 title="订单完成情况" class="order-box">
                  <el-table :data="orders" v-if="orders.length > 0" class="order-table">
                    <el-table-column v-for="(item, dex) in Object.keys(orderStatus)" :prop="item" :label="orderStatus[item]" :key="dex">
                      <template slot-scope="scope">
                        <el-popover
                          placement="right"
                          width="300"
                          trigger="manual"
                          v-model="visible"
                          v-if="item === 'action'" >
                          <template>
                            <div>
                              <span>选择目标发货区</span>
                              <el-divider/>
                              <el-row :gutter="15">
                                <el-col v-for="(palt, index) in platforms" :key="index" class="platform" :span="7" @click.native="bindShipping(palt, scope)">
                                  <div>{{palt.platformNo}}</div>
                                </el-col>
                              </el-row>
                            </div>
                          </template>
                          <el-button slot="reference" type="text" @click.native.prevent="print(scope.$index, orders)">打印明细</el-button>
                        </el-popover>
                        <div v-else class="ship-order-list">
                          {{scope.row[item]}}
                        </div>
                      </template>
                    </el-table-column>
                  </el-table>
                  <el-empty description="暂无投放订单" class="empty-padding" v-else></el-empty>
                </dv-border-box-11>
              </el-row>
              <el-row type="flex" align="center" id="datav">
                <dv-border-box-11  title="播种墙" class="pick-wall" >
                  <el-row type="flex" align="center" justify="center" class="pick-wall-circle" >
                    <el-col :span="1">
                      <p class="pick-wall-tip"></p>
                    </el-col>
                    <el-col :span="22">
                      <p>绿色:任务已完成  黄色:等待中  红色:播种中</p>
                    </el-col>
                  </el-row>
                  <div class="pick-wall-box">
                    <el-col :span="6" v-for="(item, index) in seedBracket" :key="index" @click.native="lightClick(item)">
                      <div class="seed-bracket" :style="getLightStatus(item)" />
                    </el-col>
                  </div>
                </dv-border-box-11>
              </el-row>
          </el-col>
        </el-row>
      </template>
    </dv-full-screen-container>
    <template>
      <el-dialog :visible.sync="show" fullscreen>
        <el-row type="flex" align="center" justify="center">
          <dv-border-box-10 class="dialog-confirm" v-if="show">
            <div class="container">
              <el-row :gutter="25" >
                <el-col :span="12">
                  <el-row>
                    <el-col class="header-title">复核当前作业中数量</el-col>
                    <el-col style="text-align: center">
                      <el-col :span="10" class="content">总需求数量:</el-col>
                      <el-col :span="8" class="content-value">{{ selected.anfme }}</el-col>
                    </el-col>
                    <el-col>
                      <el-col :span="10" class="content">剩余需求数量:</el-col>
                      <el-col :span="8" class="content-value">{{  selected.anfme - selected.workQty }}</el-col>
                    </el-col>
                  </el-row>
                </el-col>
                <el-col :span="12" >
                  <el-row>
                    <el-col>
                      <div class="header-title">拣货数量:</div>
                      <dv-border-box-10  class="box">
                        <el-input type="text" v-model="workQty"></el-input>
                      </dv-border-box-10>
                    </el-col>
                  </el-row>
                </el-col>
              </el-row>
              <el-row :gutter="100" style="margin:25px;">
                <el-col :span="12">
                  <el-button type="success" class="btn" @click="review">确认</el-button>
                </el-col>
                <el-col :span="12">
                  <el-button type="danger" class="btn" @click="delSow">取消</el-button>
                </el-col>
              </el-row>
            </div>
          </dv-border-box-10>
        </el-row>
      </el-dialog>
    </template>
  </div>
</template>
<script>
import topHeader from './topHeader'
import print from 'print-js'
import digitalFlop from './digitalFlop'
export default {
  name: 'DataView',
  components: {
    topHeader,
    digitalFlop
  },
  data () {
    return {
      visible: false,
      waveSeeds: [],
      tasks: [],
      taskDetls: [],
      mergeTaskDetl: [],
      currTask: {},
      detlLabels: {
        matnr: '物料码',
        batch: '批次',
        anfme: '拣货任务数量',
        stock: '库存数量'
      },
      orderStatus: {
        orderNo: '订单编码',
        orderStatus: '订单状态',
        action: '操作'
      },
      waveLabels: {
        barcode: '拖盘码',
        taskNo: '任务编号',
        waveNo: '波次',
        anfme: '数量',
        title: '作业类型'
      },
      waveNo: '',
      header: {},
      orders: [],
      seedBracket: [],
      platforms: [],
      selectOrder: {},
      selected: {},
      workQty: 0, //弹窗拣货数量
      show: false,
    }
  },
  created () {
    //获取当前执行任务
    this.getWaves()
    //获取当前播种墙库位信息
    this.getSeedLoc()
    //获取所有发货暂存区
    this.getAllPlatforms()
  },
  computed: {
    getLightStatus() {
      return (item)=> {
        if (item.orderNo != undefined && item.orderNo != null && item.orderNo != '') {
          return 'background: yellow;'
        } else {
          return  'background: gainsboro;'
        }
      }
    }
  },
  methods: {
    printPage() {
      // let element = document.querySelector('#datav');
      // let el = this.$refs.datav
      // console.log(this.$refs.datav)
      // console.log(element)
      // if (el) {
      //   let pdfLoader = new PdfLoader(el, 'pdf');
      //   pdfLoader.outPutPdfFn('datav')
      // }
      print( 'datav','html')
    },
    //点击打印明细按钮
    print(e, data) {
      this.visible = true
      this.selectOrder = data[e]
    },
    //拍灯容器流动
    lightClick(item) {
      this.slapLight(item)
    },
    //订单入库集货区,并打印单据
    bindShipping(plat, item) {
      this.visible = false
      let order = item.row
      if (order == null) {
        this.$message.error("当前订单信息不存在,请联系管理员!!")
        return
      }
      let that = this
      // eslint-disable-next-line no-undef
      $ajax.post('/api/bind/shipping/platform', {orderId: order.id, waveId: order.waveId, platformId: plat.id}).then(response => {
        if (response.code === 200) {
          //隐藏发货区,执行打印
        } else {
          this.$message.error(response.msg)
        }
      })
    },
    //获取所有集货区
    getAllPlatforms() {
      let that = this
      // eslint-disable-next-line no-undef
      $ajax.get('/out/stock/all/platforms').then(response => {
        if (response.code === 200) {
            that.platforms = response.data
            console.log(that.platforms)
        }
      })
    },
    slapLight(item) {
      let that = this
      if (that.tasks == null || that.tasks.length < 1) {
        this.$message.error('当前任务号为空,无法执行!!')
        return
      }
      let taskNo = that.tasks[0].taskNo
      // eslint-disable-next-line no-undef
      $ajax.post('/out/stock/slap/light', {taskNo: taskNo, siteNo: item.siteNo, orderNo: item.orderNo}).then(response => {
        if (response.code === 200) {
          //刷新播种墙
          this.getSeedLoc()
        }
      })
    },
    /**
     * 获取播种库位状态
     */
    getSeedLoc() {
      let that = this
      // eslint-disable-next-line no-undef
      $ajax.get('wave/seed/locs/').then(response => {
        if (response.code === 200) {
          that.seedBracket = response.data
        }
      })
    },
    /**
     * 获取波次订单完成状态
     * @param waveNo
     */
    getWaveOrders(waveNo) {
      let that = this
      // eslint-disable-next-line no-undef
      $ajax.get('wave/orders/' + this.waveNo).then(response => {
        if (response.code === 200) {
          that.orders = response.data
        }
      })
    },
    selectRow (row, index) {
      this.currTask = row
    },
    /**
     * 头部任务栏,支持多任务显示。可手动切换
     * @param data
     */
    borderClick (data) {
      // this.getMergeTaskDetl(data.taskNo)
    },
    /**
     * 波次播种
     */
    review () {
      for (let i = 0; i < this.mergeTaskDetl.length; i++) {
        if (this.mergeTaskDetl[i].matnr === this.selected.matnr && this.workQty > this.mergeTaskDetl[i].anfme) {
            this.$message({
              message: '拣货数量不能大于任务数量!!',
              type: 'error'
            })
            return
        }
      }
      //隐藏弹框
      this.show = false
      // eslint-disable-next-line no-undef
      $ajax.post('wave/sow/review', { waveSeedId: this.selected.id, reviewNum: this.workQty }).then(response => {
        if (response.code === 200) {
          this.getWaves()
        } else {
          this.$message({
            message: response.msg,
            type: 'error'
          })
        }
      })
    },
    delSow () {
      this.show = false
      // eslint-disable-next-line no-undef
      // $ajax.get('wave/sow/remove/' + this.selected.id).then(response => {
      //   if (response.code === 200) {
      //     this.getWaves()
      //   }
      // })
    },
    selectView (item) {
      if (item.siteNo == undefined && item.siteNo == null && item.siteNo == '') {
        this.$message({
          message: '请绑定播种站点后,再进行播种!!',
          type: 'error'
        })
        return
      }
      this.show = true
      this.workQty = item.workQty
      this.selected = item
    },
    getWaves () {
      let that = this
      // eslint-disable-next-line no-undef
      $ajax.get('wave/sow/tasks').then(response => {
        if (response.code === 200) {
          that.tasks = []
          // this.show = false
          that.tasks.push(...response.data)
          //获取合并波次任务明细
          that.getMergeTaskDetl(that.tasks[0].taskNo)
          //获取波次订单状态
          that.waveNo = that.tasks[0].waveNo
          that.getWaveOrders()
          //刷新播种墙信息
          this.getSeedLoc()
        }
      })
    },
    getMergeTaskDetl (taskNo) {
      let that = this
      // eslint-disable-next-line no-undef
      $ajax.get('wave/sow/task/detl/' + taskNo).then(response => {
        if (response.code === 200) {
          that.mergeTaskDetl = []
          that.mergeTaskDetl.push(...response.data)
          that.getTaskDetl(that.mergeTaskDetl[0])
        }
      })
    },
    getTaskDetl (data) {
      let that = this
      // eslint-disable-next-line no-undef
      $ajax.post('wave/task/detl/qutify/', { taskNo: data.taskNo, matnr: data.matnr }).then(response => {
        if (response.code === 200) {
          that.taskDetls = []
          // this.show = false
          that.taskDetls.push(...response.data)
        }
      })
    }
  }
}
</script>
<style lang="less">
  @media screen and (max-width: 1920px){
    #data-view {
      width: 100%;
      height: 100%;
      background-color: #030409;
      color: #fff;
      #dv-full-screen-container {
        background-image: url('./img/bg.png');
        background-size: 100% 100%;
        box-shadow: 0 0 3px blue;
        display: flex;
        flex-direction: column;
      }
      .main-content {
        margin: 5px 0;
      }
      .platform {
        display: flex;
        align-items: center;
        justify-content: center;
        height: 80px;
        background-color: #03d3ec;
        margin: 5px;
        text-align: center;
      }
      .pick-order-detl {
        padding: 0 15px;
        .detl-box-border {
          height: 59vh;
          .detl-box-padding {
            height: 65px
          }
        }
      }
      .dialog-confirm {
        text-align: center;
        color: white;
        margin: 20vh;
        .container {
          padding: 45px 60px;
          .btn {
            width: 30vh;height: 7vh;font-size: 23px;
          }
          .header-title {
            font-size: 24px;
            font-style: italic;
            padding: 10px;
          }
          .box {
            height:8vh;
            width: 20vh;
            margin: auto;
            text-align: center;
          }
          .content {
            font-size: 20px;
            text-align: end;
            padding: 10px;
          }
          .content-value {
            padding: 10px;
            text-align: start;font-size: 20px;
          }
        }
      }
      .pick-wall {
        padding: 60px 10px 10px 10px;
        height: 68vh;
        .pick-wall-box {
          .seed-bracket {
            background: gainsboro;
            margin: 15px;
            height: 110px;
          }
          .seed-bracket .el-col .el-col-24 :hover {
            background: white;
          }
        }
        .pick-wall-circle {
          padding: 10px;margin: 10px; background-color: #50bfff2e;
          .pick-wall-tip {
            height: 25px;
            width: 1px;
            background: #03d3ec;
            border-left: 5px solid #50bfff
          }
        }
      }
      .header-left-task {
        display: flex;
        padding: 15px 15px 5px 15px;
        margin-bottom: 0px !important;
        .header-card {
          margin: 10px;height: 120px;
          text-align: center;
          padding: 15px;
          .header-card-label {
            font-size: 25px;
            font-style: oblique;
            padding: 5px;
            color: white;
          }
          .header-card-val {
            font-size: 20px;
            padding: 10px;
            color: orange;
          }
        }
      }
      .detl-info-row {
        padding: 0 15px;
      }
      .order-box-row {
        width: 84vh;
        height: 38vh;
        .order-box {
          padding:0px 30px;
          .order-table {
            width: 74vh;
            padding-top: 65px;
            height: 275px;
            .ship-order-list {
              font-size: 18px; color: white;
            }
          }
        }
        .empty-padding {
          padding-top: 65px;
        }
      }
      .order-detl-list {
        overflow: hidden;
        overflow-y: scroll;
        height: 43vh;
        padding: 0 15px;
      }
      .el-card {
        background: rgba(8,32,92, 0.5) !important;
      }
      .bounce-enter-active {
        animation: bounce-in 0.5s;
      }
      .bounce-leave-active {
        animation: bounce-in 0.5s reverse;
      }
      @keyframes bounce-in {
        0% {
          transform: scale(0);
        }
        50% {
          transform: scale(1.25);
        }
        100% {
          transform: scale(1);
        }
      }
      .el-input__inner {
        background: rgba(8,32,92, 0.5) !important;
        border: 0 !important;
        height: unset !important;
        line-height: 80px !important;
        font-size: 30px !important;
        color: white !important;
        text-align: center !important;
      }
      .el-dialog {
        background: rgba(8,32,92, 0.9) !important;
      }
      .el-row {
        margin-bottom: 20px;
        &:last-child {
          margin-bottom: 0;
        }
      }
      .el-col {
        border-radius: 4px;
      }
      .el-dialog__wrapper {
        top: 0px !important;
      }
      .talbe-matnr {
        height: 26vh;
      }
      .el-table {
        background-color: unset !important;
        color: white !important;
        flex: 0.7 !important;
        font-style: italic !important;
      }
      .el-table::before {
        height: 0px !important;
      }
      .el-table .cell {
        font-size: 23px !important;
        color: white !important;
      }
      .el-table tr {
        background-color: unset !important;
      }
      .el-table th.el-table__cell {
        background-color: unset !important;
      }
      /* 用来设置当前页面element全局table 选中某行时的背景色*/
      .el-table__body tr.current-row > td {
        background-color: #92cbf1 !important;
        color: #fff;
      }
      /*鼠标移入某行时的背景色*/
      .el-table--enable-row-hover .el-table__body tr:hover > td {
        background-color: #071539 !important;
        /* color: #fff; */
      }
    }
  }
@media screen and (max-width: 1281px)  {
  #data-view {
    .header-left-task {
      .header-card {
        margin: 5px;height: 90px;
        padding: 5px;
        .header-card-label {
          font-size: 18px;
          padding: 5px;
        }
        .header-card-val {
          font-size: 14px;
        }
      }
    }
    .order-box-row {
      width: 58vh;
      height: 28vh;
      .order-box {
        padding:0px 30px;
        .order-table {
          width: 74vh;
          padding-top: 65px;
          height: 275px;
          .ship-order-list {
            font-size: 14px;
          }
        }
      }
      .empty-padding {
        padding-top: 65px;
      }
    }
    .talbe-matnr {
      height: 20vh;
    }
    .el-table .cell {
      font-size: 16px !important;
    }
    .pick-order-detl {
      padding: 0 10px;
      .detl-box-border {
        height: 39vh;
        .detl-box-padding {
          height: 65px
        }
        .order-detl-list {
          height: 23vh;
        }
      }
    }
    .pick-wall {
      padding: 60px 10px 10px 10px;
      height: 45vh;
      .pick-wall-circle {
        margin: 10px;
        padding: unset;
        background-color: #50bfff2e;
        .pick-wall-tip {
          height: 20px;
          width: 1px;
          background: #03d3ec;
          border-left: 5px solid #50bfff
        }
      }
      .pick-wall-box {
        .seed-bracket {
          background: gainsboro;
          margin: 8px;
          height: 65px;
        }
      }
    }
  }
}
</style>
construction-data/src/components/datav/rankingBoard.vue
New file
@@ -0,0 +1,82 @@
<template>
  <div id="ranking-board">
    <div class="ranking-board-title">巡查上报记录数量</div>
    <dv-scroll-ranking-board :config="config" />
  </div>
</template>
<script>
export default {
  name: 'RankingBoard',
  data () {
    return {
      config: {
        data: [
          {
            name: '日常养护',
            value: 55
          },
          {
            name: '交通事故',
            value: 120
          },
          {
            name: '路面',
            value: 78
          },
          {
            name: '桥通',
            value: 66
          },
          {
            name: '计日工',
            value: 80
          },
          {
            name: '路基',
            value: 45
          },
          {
            name: '交安设施',
            value: 29
          },
          {
            name: '除雪',
            value: 29
          },
          {
            name: '绿化',
            value: 29
          }
        ],
        rowNum: 9
      }
    }
  }
}
</script>
<style lang="less">
#ranking-board {
  width: 20%;
  box-shadow: 0 0 3px blue;
  display: flex;
  flex-direction: column;
  background-color: rgba(6, 30, 93, 0.5);
  border-top: 2px solid rgba(1, 153, 209, .5);
  box-sizing: border-box;
  padding: 0px 30px;
  .ranking-board-title {
    font-weight: bold;
    height: 50px;
    display: flex;
    align-items: center;
    font-size: 20px;
  }
  .dv-scroll-ranking-board {
    flex: 1;
  }
}
</style>
construction-data/src/components/datav/roseChart.vue
New file
@@ -0,0 +1,95 @@
<template>
  <div id="rose-chart">
    <div class="rose-chart-title">累计计量资金分布</div>
    <dv-charts :option="option" />
  </div>
</template>
<script>
export default {
  name: 'RoseChart',
  data () {
    return {
      option: {}
    }
  },
  methods: {
    createData () {
      const { randomExtend } = this
      this.option = {
        series: [
          {
            type: 'pie',
            radius: '50%',
            roseSort: false,
            data: [
              { name: '路基', value: randomExtend(40, 70) },
              { name: '交安设施', value: randomExtend(20, 30) },
              { name: '日常养护', value: randomExtend(10, 50) },
              { name: '桥通', value: randomExtend(5, 20) },
              { name: '交通事故', value: randomExtend(40, 50) },
              { name: '路面', value: randomExtend(20, 30) },
              { name: '绿化', value: randomExtend(5, 10) },
              { name: '计日工', value: randomExtend(20, 35) },
              { name: '除雪', value: randomExtend(5, 10) }
            ],
            insideLabel: {
              show: false
            },
            outsideLabel: {
              formatter: '{name} {percent}%',
              labelLineEndLength: 20,
              style: {
                fill: '#fff'
              },
              labelLineStyle: {
                stroke: '#fff'
              }
            },
            roseType: true
          }
        ],
        color: ['#da2f00', '#fa3600', '#ff4411', '#ff724c', '#541200', '#801b00', '#a02200', '#5d1400', '#b72700']
      }
    },
    randomExtend (minNum, maxNum) {
      if (arguments.length === 1) {
        return parseInt(Math.random() * minNum + 1, 10)
      } else {
        return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10)
      }
    }
  },
  mounted () {
    const { createData } = this
    createData()
    setInterval(createData, 30000)
  }
}
</script>
<style lang="less">
#rose-chart {
  width: 30%;
  height: 100%;
  background-color: rgba(6, 30, 93, 0.5);
  border-top: 2px solid rgba(1, 153, 209, .5);
  box-sizing: border-box;
  .rose-chart-title {
    height: 50px;
    font-weight: bold;
    text-indent: 20px;
    font-size: 20px;
    display: flex;
    align-items: center;
  }
  .dv-charts-container {
    height: calc(~"100% - 50px");
  }
}
</style>
construction-data/src/components/datav/scrollBoard.vue
New file
@@ -0,0 +1,48 @@
<template>
  <div id="scroll-board">
    <dv-scroll-board :config="config" />
  </div>
</template>
<script>
export default {
  name: 'ScrollBoard',
  data () {
    return {
      config: {
        header: ['时间', '病害信息', '数量', '标段'],
        data: [
          ['2019-07-01 19:25:00', '路面危害-松散', '5', 'xxxxxxx'],
          ['2019-07-02 17:25:00', '路面危害-路面油污清理', '13', 'xxxxxxx'],
          ['2019-07-03 16:25:00', '交安设施-交通标志牌结构', '6', 'xxxxxxx'],
          ['2019-07-04 15:25:00', '路基危害-防尘网', '2', 'xxxxxxx'],
          ['2019-07-05 14:25:00', '交安设施-交通标志牌结构', '1', 'xxxxxxx'],
          ['2019-07-06 13:25:00', '路面危害-松散', '3', 'xxxxxxx'],
          ['2019-07-07 12:25:00', '路基危害-防尘网', '4', 'xxxxxxx'],
          ['2019-07-08 11:25:00', '路面危害-路面油污清理', '2', 'xxxxxxx'],
          ['2019-07-09 10:25:00', '交安设施-交通标志牌结构', '5', 'xxxxxxx'],
          ['2019-07-10 09:25:00', '路基危害-防尘网', '3', 'xxxxxxx']
        ],
        index: true,
        columnWidth: [50, 170, 300],
        align: ['center'],
        rowNum: 7,
        headerBGC: '#1981f6',
        headerHeight: 45,
        oddRowBGC: 'rgba(0, 44, 81, 0.8)',
        evenRowBGC: 'rgba(10, 29, 50, 0.8)'
      }
    }
  }
}
</script>
<style lang="less">
#scroll-board {
  width: 100%;
  box-sizing: border-box;
  margin: 10px;
  height: 100%;
  overflow: hidden;
}
</style>
construction-data/src/components/datav/topHeader.vue
New file
@@ -0,0 +1,95 @@
<template>
  <div id="top-header">
    <dv-decoration-8 class="header-left-decoration" />
    <dv-decoration-5 class="header-center-decoration" />
    <dv-decoration-8 class="header-right-decoration" :reverse="true" />
    <div class="center-title" @click="fullScreen">波次播种任务系统</div>
<!--    <el-button class="center-title" @click="handleFullScreen">全屏</el-button>-->
  </div>
</template>
<script>
export default {
  name: 'TopHeader',
  data() {
    return {
    }
  },
  methods: {
    // fullScreen() {
    //   let element = document.documentElement
    //   element.requestFullscreen()
    // },
    handleFullScreen() {
      const element = document.documentElement
      if (this.fullscreen) {
        if (document.exitFullscreen) {
          document.exitFullscreen()
        } else if (document.webkitCancelFullScreen) {
          document.webkitCancelFullScreen()
        } else if (document.mozCancelFullScreen) {
          document.mozCancelFullScreen()
        } else if (document.msExitFullscreen) {
          document.msExitFullscreen()
        }
      } else {
        if (element.requestFullscreen) {
          element.requestFullscreen()
        } else if (element.webkitRequestFullScreen) {
          element.webkitRequestFullScreen()
        } else if (element.mozRequestFullScreen) {
          element.mozRequestFullScreen()
        } else if (element.msRequestFullscreen) {
          element.msRequestFullscreen()
        }
      }
    }
  }
}
</script>
<style lang="less">
@media screen and (max-width: 1920px){
  #top-header {
    position: relative;
    width: 100%;
    height: 100px;
    display: flex;
    justify-content: space-between;
    flex-shrink: 0;
    .header-center-decoration {
      width: 40%;
      height: 60px;
      margin-top: 30px;
    }
    .header-left-decoration, .header-right-decoration {
      width: 25%;
      height: 60px;
    }
    .center-title {
      position: absolute;
      font-size: 30px;
      font-weight: bold;
      left: 50%;
      top: 15px;
      transform: translateX(-50%);
    }
  }
}
@media only screen and (max-width: 1280px){
  #top-header {
    .center-title {
      font-size: 24px;
      font-weight: bold;
    }
  }
}
</style>
construction-data/src/components/datav/waterLevelChart.vue
New file
@@ -0,0 +1,89 @@
<template>
  <div id="water-level-chart">
    <div class="water-level-chart-title">计划资金累计完成情况</div>
    <div class="water-level-chart-details">
      累计完成<span>235,680</span>元
    </div>
    <div class="chart-container">
      <dv-water-level-pond :config="config" />
    </div>
  </div>
</template>
<script>
export default {
  name: 'WaterLevelChart',
  data () {
    return {
      config: {
        data: [45],
        shape: 'round',
        waveHeight: 25,
        waveNum: 2
      }
    }
  }
}
</script>
<style lang="less">
#water-level-chart {
  width: 20%;
  box-sizing: border-box;
  margin-left: 20px;
  background-color: rgba(6, 30, 93, 0.5);
  border-top: 2px solid rgba(1, 153, 209, .5);
  display: flex;
  flex-direction: column;
  .water-level-chart-title {
    font-weight: bold;
    height: 50px;
    display: flex;
    align-items: center;
    font-size: 20px;
    justify-content: center;
  }
  .water-level-chart-details {
    height: 15%;
    display: flex;
    justify-content: center;
    font-size: 17px;
    align-items: flex-end;
    span {
      font-size: 35px;
      font-weight: bold;
      color: #58a1ff;
      margin: 0 5px;
      margin-bottom: -5px;
    }
  }
  .chart-container {
    flex: 1;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .dv-water-pond-level {
    max-width: 90%;
    width: 200px;
    height: 200px;
    border: 10px solid #19c3eb;
    border-radius: 50%;
    ellipse {
      stroke: transparent !important;
    }
    text {
      font-size: 40px;
    }
  }
}
</style>
construction-data/src/main.js
New file
@@ -0,0 +1,20 @@
import Vue from 'vue'
import App from './App.vue'
import Ajax from './utils/ajax.js'
import './assets/common.less'
import dataV from '@jiaminghi/data-view'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.config.productionTip = false
Vue.prototype.$ajax = Ajax
Vue.use(ElementUI)
Vue.use(dataV)
global.$ajax = Ajax
new Vue({
  render: h => h(App)
}).$mount('#app')
construction-data/src/utils/ajax.js
New file
@@ -0,0 +1,202 @@
/* eslint-disable */
import axios from 'axios'
import {MessageBox, Message} from 'element-ui'
import utilVue from '../utils/utilVue.js'
const baseUrl = process.env.VUE_APP_BASE_API
// create an axios instance
const service = axios.create({
  baseURL: baseUrl + 'wms/', // url = base url + request url
  timeout: 120000 // request timeout
})
// request interceptor
service.interceptors.request.use(
  (config) => {
    // do something before request is sent
    // if (store.getters.currentUser) {
    //   // let each request carry token
    //   // ['X-Token'] is a custom headers key
    //   // please modify it according to the actual situation
    //   config.headers['Authorization'] = sessionStorage.getItem('Authorization')
    //   // config.headers['warehouseId'] = store.getters.currentWarehouse.id
    //   // config.headers['requestType'] = 'spdpc'
    // }
    if (config.method != 'get' && window.appendParam && config.data) {
      Object.keys(window.appendParam).forEach((key) => {
        if (Object.prototype.hasOwnProperty.call(window.appendParam, key)) {
          if (window.appendParam[key]) {
            if (Array.isArray(config.data)) {
              config.data.forEach(data => {
                if (typeof data == 'object') {
                  data[key] = window.appendParam[key]
                }
              })
            } else {
              config.data[key] = window.appendParam[key]
            }
          }
        }
      })
      window.appendParam = null
    }
    return config
  },
  (error) => {
    return Promise.reject(error)
  }
)
const download = (resp) => {
  //这里res.data是返回的blob对象
  var blob = new Blob([resp.data], {type: resp.headers['content-type']});
  // 自定义响应头
  let fileName = resp.headers['content-filename'] && decodeURIComponent(resp.headers['content-filename'])
  if (fileName === undefined || fileName === null || fileName === "") {
      fileName = new Date().getTime() + '.xlsx'
  }
  if (window.navigator.msSaveOrOpenBlob) {
      // 如果是IE浏览器
      navigator.msSaveBlob(blob, fileName);//filename文件名包括扩展名,下载路径为浏览器默认路径
      return
  }
  // chrome、Firefox
  var downloadElement = document.createElement('a');
  var href = window.URL.createObjectURL(blob); //创建下载的链接
  downloadElement.href = href;
  downloadElement.download = fileName
  document.body.appendChild(downloadElement);
  downloadElement.click(); //点击下载
  document.body.removeChild(downloadElement); //下载完成移除元素a
  window.URL.revokeObjectURL(href); //释放掉blob对象
}
// response interceptor
service.interceptors.response.use(
  /**
   * If you want to get http information such as headers or status
   * Please return  response => response
   */
  /**
   * Determine the request status by custom code
   * Here is just an example
   * You can also judge the status by HTTP Status Code
   */
  (response) => {
    let {headers, data} = response
    // 处理文件下载
    let contentType = headers['content-type']
    if (headers && contentType
        && (contentType.indexOf('application/x-msdownload') != -1
            || contentType.indexOf('application/octet-stream') != -1
            || contentType.indexOf('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') != -1)) {
        download(response)
        return response
    }
    if (contentType && contentType.indexOf('application/json') == -1) {
      return response
    }
    let isValidexception = headers && '1' == headers.validexception
    const res = data
    // if the custom code is not 0, it is judged as an error.
    if (res.success) {
      return res
    }
    utilVue.loadHide()
    if (res.success == undefined) { //如果未定义,直接返回
      return res
    }
    Message({
      message: '<div class="el-notification__content ">'+res.msg || 'Error'+'</div>',
      duration: 5000,
      dangerouslyUseHTMLString: true,
      iconClass: isValidexception ? 'el-notification__icon el-icon-warning' : 'el-notification__icon el-icon-error',
      customClass: isValidexception ? 'warn-ajax' : 'error-ajax',
      showClose: true,
      showIcon: true,
      offset: 1
    })
    //下面功能尚未实现,暂时注释掉
    // return Promise.reject(new Error(res.msg || 'Error'))
    // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
    if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
      // to re-login
      // MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
      //   confirmButtonText: 'Re-Login',
      //   cancelButtonText: 'Cancel',
      //   type: 'warning'
      // }).then(() => {
      //   store.dispatch('user/resetToken').then(() => {
      //     location.reload()
      //   })
      // })
    }
    return res
    // return Promise.reject(new Error(res.msg || 'Error'))
  },
  (error) => {
    utilVue.loadHide()
    let response = error?error.response:null
    let status = response?response.status:null
    let data = response?response.data:null
    let isValidexception = response.headers && '1' == response.headers.validexception
    if (data && data.data && data.data.needLogin) {
      try {
        MessageBox.close()
      }catch (e) {
      }
      MessageBox.alert('会话超时,请重新登录!', '系统提示', {
        confirmButtonText: '确定',
        type: 'warning',
        callback: action => {
          // router.push('/login', () => {
          //   // Message.error(error.response.data.msg)
          //   location.reload()
          // })
        }
      })
      return Promise.reject(error)
    }
    let msg='未知异常'
    if(!status){
      msg= '网络连接异常,请检查本地网络是否连接'
    }else if(status == 500){
      if(response.data.msg){
        msg= response.data.msg
      }else if(response.data.message){
        msg= response.data.message
        if(msg.indexOf('服务器异常') == -1) msg='服务器异常,原因:'+msg
      }else if(typeof(response.data) == 'string'  && response.data.indexOf('Proxy error') != -1){
        msg= '服务器暂时无法访问,请稍候重试'
      }
    }else if(status == 502){
      msg= '代理服务器暂时不可以使用,请稍候重试'
    }
    Message({
      message: '<div class="el-notification__content ">'+msg+'</div>',
      duration: 5000,
      dangerouslyUseHTMLString: true,
      iconClass: isValidexception ? 'el-notification__icon el-icon-warning' : 'el-notification__icon el-icon-error',
      customClass: isValidexception ? 'warn-ajax' : 'error-ajax',
      showClose: true,
      showIcon: true,
      offset: 1
    })
    return Promise.reject(error)
  }
)
export default service
construction-data/src/utils/utilPrint.js
New file
@@ -0,0 +1,180 @@
import jsPDF from 'jspdf'
import html2canvas from 'html2canvas'
/*
 * 使用说明
 * ele:需要导出pdf的容器元素(dom节点 不是id)
 * pdfFileName: 导出文件的名字 通过调用outPutPdfFn方法也可传参数改变
 * splitClassName: 避免分段截断的类名 当pdf有多页时需要传入此参数 , 避免pdf分页时截断元素  如表格<tr class="itemClass"></tr>
 * 调用方式 先 let pdf = new PdfLoader(ele, 'pdf' ,'itemClass');
 * 若想改变pdf名称 pdf.outPutPdfFn(fileName);  outPutPdfFn方法返回一个promise 可以使用then方法处理pdf生成后的逻辑
 * */
class PdfLoader {
  constructor (ele, pdfFileName, splitClassName) {
    this.ele = ele
    this.pdfFileName = pdfFileName
    this.splitClassName = splitClassName || ''
    this.A4_WIDTH = 841.89
    this.A4_HEIGHT = 595.28
  }
  async getPDF (resolve) {
    const ele = this.ele
    const pdfFileName = this.pdfFileName
    const eleW = ele.offsetWidth // 获得该容器的宽
    const eleH = ele.scrollHeight // 获得该容器的高
    const eleOffsetTop = ele.offsetTop // 获得该容器到文档顶部的距离
    const eleOffsetLeft = ele.offsetLeft // 获得该容器到文档最左的距离
    window.pageYoffset = 0
    document.documentElement.scrollTop = 0
    document.body.scrollTop = 0
    const canvas = document.createElement('canvas')
    let abs = 0
    // eslint-disable-next-line camelcase
    const win_in = document.documentElement.clientWidth || document.body.clientWidth // 获得当前可视窗口的宽度(不包含滚动条)
    // eslint-disable-next-line camelcase
    const win_out = window.innerWidth // 获得当前窗口的宽度(包含滚动条)
    // eslint-disable-next-line camelcase
    if (win_out > win_in) {
      // eslint-disable-next-line camelcase
      abs = (win_out - win_in) / 2 // 获得滚动条宽度的一半
    }
    canvas.width = eleW * 2 // 将画布宽&&高放大两倍
    canvas.height = eleH * 2
    const context = canvas.getContext('2d')
    context.scale(2, 2) // 增强图片清晰度
    context.translate(-eleOffsetLeft - abs, -eleOffsetTop)
    ele.style.height = ele.scrollHeight + 'px' // 获取元素的滚动高度,用于截取被滚动条隐藏的部分
    html2canvas(ele, {
      backgroundColor: null,
      allowTaint: false,
      dpi: window.devicePixelRatio * 4,
      width: ele.width,
      height: ele.width,
      windowWidth: ele.scrollWidth,
      scale: 4, // 按比例增加分辨率
      useCORS: true // 允许canvas画布内可以跨域请求外部链接图片, 允许跨域请求。
    }).then(async (canvas) => {
      const contentWidth = canvas.width
      const contentHeight = canvas.height
      ele.style.height = ele.clientHeight + 'px' // 获取元素的实际高度,不包括滚动条隐藏的部分
      // 一页pdf显示html页面生成的canvas高度;
      const pageHeight = (contentWidth / this.A4_WIDTH) * this.A4_HEIGHT // 这样写的目的在于保持宽高比例一致 pageHeight/canvas.width = a4纸高度/a4纸宽度// 宽度和canvas.width保持一致
      // 未生成pdf的html页面高度
      let leftHeight = contentHeight
      // 页面偏移
      let position = 0
      // a4纸的尺寸[595,842],单位像素,html页面生成的canvas在pdf中图片的宽高
      const imgWidth = this.A4_WIDTH - 10 // -10为了页面有右边距
      const imgHeight = (this.A4_WIDTH / contentWidth) * contentHeight
      const pageData = canvas.toDataURL('image/jpeg', 1.0)
      const pdf = jsPDF('l', 'pt', 'a4')
      // 有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
      // 当内容未超过pdf一页显示的范围,无需分页
      if (leftHeight < pageHeight) {
        // 在pdf.addImage(pageData, 'JPEG', 左,上,宽度,高度)设置在pdf中显示;
        pdf.addImage(pageData, 'JPEG', 5, 0, imgWidth, imgHeight)
        // pdf.addImage(pageData, 'JPEG', 20, 40, imgWidth, imgHeight);
      } else {
        // 分页
        while (leftHeight > 0) {
          pdf.addImage(
            pageData,
            'JPEG',
            5,
            position,
            imgWidth,
            imgHeight
          )
          leftHeight -= pageHeight
          position -= this.A4_HEIGHT
          // 避免添加空白页
          if (leftHeight > 0) {
            pdf.addPage()
          }
        }
      }
      // pdf.save(pdfFileName + '.pdf', { returnPromise: true }).then(() => {
      //   // 去除添加的空div 防止页面混乱
      //   const doms = document.querySelectorAll('.emptyDiv')
      //   for (let i = 0; i < doms.length; i++) {
      //     doms[i].remove()
      //   }
      // })
      let pdfData = pdf.output('datauristring')
      this.showPreview(pdfData)
      this.ele.style.height = ''
      resolve()
    })
  }
  // 此方法是防止(图表之类)内容因为A4纸张问题被截断
  async outPutPdfFn (pdfFileName) {
    return new Promise((resolve, reject) => {
      this.ele.style.height = 'initial'
      // eslint-disable-next-line no-unused-expressions
      pdfFileName ? (this.pdfFileName = pdfFileName) : null
      const target = this.ele
      const pageHeight = (target.scrollWidth / this.A4_WIDTH) * this.A4_HEIGHT
      if (this.splitClassName !== '' || this.splitClassName != null) {
        // 获取分割dom,此处为class类名为item的dom
        const domList = document.getElementsByClassName(this.splitClassName)
        // 进行分割操作,当dom内容已超出a4的高度,则将该dom前插入一个空dom,把他挤下去,分割
        let pageNum = 1 // pdf页数
        const eleBounding = this.ele.getBoundingClientRect()
        for (let i = 0; i < domList.length; i++) {
          const node = domList[i]
          const bound = node.getBoundingClientRect()
          const offset2Ele = bound.top - eleBounding.top
          const currentPage = Math.ceil(
            (bound.bottom - eleBounding.top) / pageHeight
          ) // 当前元素应该在哪一页
          if (pageNum < currentPage) {
            pageNum++
            const divParent = domList[i].parentNode // 获取该div的父节点
            const newNode = document.createElement('div')
            newNode.className = 'emptyDiv'
            newNode.style.background = 'white'
            newNode.style.height =
              pageHeight * (pageNum - 1) - offset2Ele + 30 + 'px' // +30为了在换下一页时有顶部的边距
            newNode.style.width = '100%'
            divParent.insertBefore(newNode, node) // 在每一个节点前面插入一个空的新节点,防止内容被分割截断
          }
        }
      }
      // 异步函数,导出成功后处理交互
      this.getPDF(resolve, reject)
    })
  }
  async showPreview (pdfData) {
    // const pdfPreview = this.$refs.pdfPreview
    const iframe = document.createElement('iframe')
    iframe.src = pdfData
    iframe.style.width = '100%'
    iframe.style.height = '600px'
    this.ele.innerHTML = ''
    this.ele.appendChild(iframe)
    // let loction = window.location.href
    // let url = loction + 'pdf/' + this.pdfFileName + '.pdf'
    // // 这一步是关键,使用window.URL.createObjectURL把blob数据转为本地URL
    // let url = window.URL.createObjectURL(new Blob([pdfData]))
    // let content = `/pdfjs/viewer.html?file=${url}`
    // window.open(content, '_blank')
    // let msg = open('cnrose', 'DisplayWindow', 'toolbar=no,,menubar=no,location=no,scrollbars=no')
    // msg.document.write('<HEAD><TITLE>sunset</TITLE></HEAD>')
    // msg.document.write('<CENTER>http://www.cjpfw.com/</CENTER>')
    // console.log(msg.document.body)
    // let iframe = msg.document.body.createElement('iframe')
    // iframe.src = pdfData
    // iframe.style.width = '100%'
    // iframe.style.height = '600px'
    // msg.document.write(iframe)
  }
}
export default PdfLoader
construction-data/src/utils/utilVue.js
New file
@@ -0,0 +1,41 @@
/* eslint-disable */
/**
* vue相关工具类
*/
let loadSystem
import {Loading, Message} from 'element-ui'
const utilVue = {
  /**显示系统遮罩层
   * @param isShow true:显示遮罩层
  * @Description:显示系统遮罩层
  */
  async loadShow(isShow,target, text){
    if(isShow != undefined && !isShow) return;
    if(!target) target= document.body
    if(loadSystem && await loadSystem.close()){
      loadSystem.show()
    }
    loadSystem=await Loading.service({ fullscreen: true, text: text ? text : '请稍候',
      spinner: 'spinner-user', background: 'rgba(0, 0, 0, 0)',customClass: 'spinner-custom', target: target
    });
  },
  //隐藏系统遮罩层
  async loadHide(){
    if(loadSystem) await loadSystem.close();
  },
  //字符串转换成json对象,单层
  str2json(jsonStr){
    return JSON.parse(jsonStr);
  },
  //json对象转换成json字符串
  json2str(jsonOb){
    return JSON.stringify(jsonOb);
  }
}
export default utilVue
construction-data/vue.config.js
New file
@@ -0,0 +1,33 @@
const port = 5555 // dev port
// eslint-disable-next-line import/order
// const VueLoaderPlugin = require('vue-loader/lib/plugin');
let proxyApi = process.env.VUE_APP_BASE_API
// let printApi = process.env.VUE_APP_PRINT_API
module.exports = {
  transpileDependencies: ['@jiaminghi/data-view'],
  publicPath: '/',
  outputDir: 'dist',
  assetsDir: 'static',
  lintOnSave: false,
  // lintOnSave: false,
  productionSourceMap: false,
  devServer: {
    port,
    open: false,
    overlay: {
      warnings: true,
      errors: true
    },
    proxy: {
      '/wms': {
        target: proxyApi, // 对应自己的接口
        changeOrigin: true,
        ws: true,
        pathRewrite: {
          '^/wms': ''
        }
      }
    }
  }
}
construction-data/yarn.lock
New file
Diff too large