zhangqing 1 год назад
Родитель
Сommit
5c6e7628d6
2 измененных файлов с 838 добавлено и 0 удалено
  1. 39 0
      src/api/mes/productionSchedule.js
  2. 799 0
      src/views/bpm/vis-page/productionSchedule.vue

+ 39 - 0
src/api/mes/productionSchedule.js

@@ -0,0 +1,39 @@
+import request from '@/utils/request'
+//获取头部统计数据
+export async function count(query) {
+  const res = await request({
+    url: '/mes/index/count',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//获取工单列表
+export async function getSalesFinishListAPI(query) {
+  const res = await request({
+    url: '/mes/index/getOrderList',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//获取工单完成统计
+export async function completionCount(query) {
+  const res = await request({
+    url: '/mes/index/completionCount',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 799 - 0
src/views/bpm/vis-page/productionSchedule.vue

@@ -0,0 +1,799 @@
+<template>
+  <vue-fullscreen
+    class="box-container"
+    v-model="isFullscreen"
+    fullscreenClass="box-container"
+    :exit-on-click-wrapper="false"
+    v-cloak
+  >
+    <div class="box-container">
+      <div class="box-header">
+        <div class="logo">
+        </div>
+        <div class="title"> 生产进度看板 </div>
+        <div class="time">
+          <span
+            style="color: #7fa7ce; margin: 5px 10px 15px 0; cursor: pointer"
+            @click.passive="onFullscreen"
+          >
+            <i
+              v-if="isFullscreen"
+              title="取消全屏"
+              class="el-icon-_screen-restore"
+            ></i>
+            <i v-else title="全屏" class="el-icon-_screen-full"></i>
+          </span>
+          <div style="margin-right: 10px">
+            <span style="color: #7fa7ce">{{ date }}</span>
+            <span style="color: rgb(210 215 221); padding: 0 5px">{{
+              time
+            }}</span>
+            <span style="color: #7fa7ce">{{ week }} </span>
+          </div>
+        </div>
+      </div>
+      <div class="box-middle">
+        <template v-for="item in borderData">
+          <div class="box-middle-content">
+            <dvBorderContent :objD="item"></dvBorderContent>
+          </div>
+        </template>
+      </div>
+      <div class="box-footer">
+        <div class="box-footer-left">
+          <div class="box-echarts">
+            <div class="box-echarts-top">
+              <span class="box-top-name">合格数统计</span>
+              <!-- <span class="box-top-unit">(万元)</span> -->
+            </div>
+            <div class="monthly_sales_volume">
+              <div
+                v-if="isFlag"
+                id="monthly_sales_volume"
+                ref="monthly_sales_volume"
+              ></div>
+            </div>
+          </div>
+          <div class="box-echarts">
+            <div class="box-echarts-top">
+              <span class="box-top-name">月度产量统计</span>
+              <span class="box-top-unit">(KG)</span>
+            </div>
+            <div class="monthly_output">
+              <div v-if="isFlag" id="monthly_output" ref="monthly_output"></div>
+            </div>
+          </div>
+        </div>
+
+        <div class="box-footer-right">
+          <div class="box-echarts-top">
+            <span class="box-top-name">生产工单进度看板</span>
+          </div>
+          <dv-scroll-board
+            v-if="isFlag"
+            :config="config"
+            style="width: 90%; height: 87%; transform: translate(5%, 1%)"
+          />
+        </div>
+      </div>
+    </div>
+  </vue-fullscreen>
+</template>
+
+<script>
+  import dvBorderContent from './dv-border-content.vue';
+  import * as echarts from 'echarts';
+  import { component } from 'vue-fullscreen';
+  import { isFullscreen } from 'ele-admin';
+  import { count, getSalesFinishListAPI, completionCount } from '@/api/mes/productionSchedule'
+
+  export default {
+    name: 'index',
+    components: {
+      dvBorderContent,
+      VueFullscreen: component
+    },
+    computed: {
+      contentWidth() {
+        return this.$store.state.theme.contentWidth;
+      }
+    },
+    watch: {
+      isFullscreen: {
+        handler() {
+          this.isFlag = false;
+          this.$nextTick(() => {
+            let { clientWidth: deviceWidth, clientHeight: deviceHeight } =
+              document.documentElement;
+            let eleAdminHeaderHeight =
+              (!this.isFullscreen &&
+                document.getElementsByClassName('ele-admin-header')[0]
+                  ?.offsetHeight) ||
+              0;
+            let eleAdminSidebarWidth =
+              (!this.isFullscreen &&
+                document.getElementsByClassName('ele-admin-sidebar')[0]
+                  ?.offsetWidth) ||
+              0;
+            let eleAdminTabsWidth =
+              (!this.isFullscreen &&
+                document.getElementsByClassName('ele-admin-tabs')[0]
+                  ?.offsetHeight) ||
+              0;
+
+            const setContainerSize = (item) => {
+              item.style.height =
+                deviceHeight - eleAdminHeaderHeight - eleAdminTabsWidth + 'px';
+              item.style.width = deviceWidth - eleAdminSidebarWidth + 'px';
+            };
+            let boxContainer = [
+              ...document.getElementsByClassName('box-container')
+            ];
+            boxContainer.forEach(setContainerSize);
+
+            document.documentElement.style.fontSize = this.isFullscreen
+              ? '24px'
+              : '16px';
+            this.isFlag = true;
+
+            this.$nextTick(() => {
+              let chartDom = this.$refs.monthly_sales_volume;
+              this.salesChart = echarts.init(chartDom);
+              let chartDom2 = this.$refs.monthly_output;
+              this.outputChart = echarts.init(chartDom2);
+              this.getMonthlySalesStatistic();
+              this.getMonthlyProduceStatistic();
+            });
+          });
+        },
+        immediate: true
+      },
+      contentWidth: {
+        handler() {
+          this.isFlag = false;
+          this.$nextTick(() => {
+            let { clientWidth: deviceWidth, clientHeight: deviceHeight } =
+              document.documentElement;
+            let eleAdminHeaderHeight =
+              (!this.isFullscreen &&
+                document.getElementsByClassName('ele-admin-header')[0]
+                  ?.offsetHeight) ||
+              0;
+            let eleAdminSidebarWidth =
+              (!this.isFullscreen &&
+                document.getElementsByClassName('ele-admin-sidebar')[0]
+                  ?.offsetWidth) ||
+              0;
+            let eleAdminTabsWidth =
+              (!this.isFullscreen &&
+                document.getElementsByClassName('ele-admin-tabs')[0]
+                  ?.offsetHeight) ||
+              0;
+            const setContainerSize = (item) => {
+              item.style.height =
+                deviceHeight - eleAdminHeaderHeight - eleAdminTabsWidth + 'px';
+              item.style.width = deviceWidth - eleAdminSidebarWidth + 'px';
+            };
+            let boxContainer = [
+              ...document.getElementsByClassName('box-container')
+            ];
+            boxContainer.forEach(setContainerSize);
+            document.documentElement.style.fontSize = this.isFullscreen
+              ? '24px'
+              : '16px';
+            this.isFlag = true;
+            this.$nextTick(() => {
+              let chartDom = this.$refs.monthly_sales_volume;
+              this.salesChart = echarts.init(chartDom);
+              let chartDom2 = this.$refs.monthly_output;
+              this.outputChart = echarts.init(chartDom2);
+              this.getMonthlySalesStatistic();
+              this.getMonthlyProduceStatistic();
+            });
+          });
+        },
+        immediate: true
+      }
+    },
+    deactivated() {
+      clearInterval(this.updateTimer);
+    },
+    data() {
+      return {
+        isFullscreen: false, // 是否是全屏
+        isFlag: false, // 是否展示图表
+        date: '',
+        time: '',
+        week: '',
+        signedTotal: 0,
+        deliveryTotal: 0,
+        salesTotal: 0,
+        WeightGainTotal: 0,
+        WeightStorageTotal: 0,
+        salesChart: null,
+        outputChart: null,
+        borderData: [
+          {
+            titleName: '工单数量',
+            titleUnit: '',
+            value: '0',
+          },
+          {
+            titleName: '待生产数量',
+            titleUnit: '',
+            value: '0',
+          },
+          {
+            titleName: '已完成数量',
+            titleUnit: '',
+            value: '0',
+          },
+          // {
+          //   titleName: '工时统计',
+          //   titleUnit: '',
+          //   value: '0',
+          //   fun: () => getIncreaseWeightAPI()
+          // },
+          {
+            titleName: '合格率',
+            titleUnit: '',
+            value: '0',
+          }
+        ],
+        monthlySalesVolumeOption: {
+          legend: {
+            left: 'center',
+            selectedMode: false,
+            itemGap: 30,
+            textStyle: {
+              fontSize: '0.7rem',
+              color: '#fff',
+              lineHeight: 0,
+              padding: [10, 0, 0, 0]
+            }
+          },
+          grid: {
+            bottom: '5%', // 组件离容器底部的距离
+            containLabel: true // 包含坐标轴的标签
+          },
+          xAxis: {
+            type: 'category',
+            data: [],
+            axisLabel: {
+              interval: 0,
+              formatter: function (value, index) {
+                let month = value.split('-');
+                return month[1] + '月';
+              },
+              textStyle: {
+                color: '#fff', // 修改字体颜色为红色
+                fontSize: '0.7rem',
+                fontFamily: 'AlibabaPuHuiTi' // 修改字体为Arial
+              }
+            }
+          },
+          yAxis: {
+            type: 'value',
+            axisLabel: {
+              textStyle: {
+                color: '#fff', // 修改字体颜色为红色
+                fontSize: '0.7rem',
+                fontFamily: 'AlibabaPuHuiTi' // 修改字体为Arial
+              }
+            }
+          },
+          series: []
+        },
+        monthlyOutputOption: {
+          legend: {
+            left: 'center',
+            selectedMode: false,
+            itemGap: 40,
+            textStyle: {
+              color: '#fff',
+              lineHeight: 0,
+              fontSize: '0.7rem',
+              padding: [10, 0, 0, 0]
+            }
+          },
+          grid: {
+            bottom: '5%', // 组件离容器底部的距离
+            containLabel: true // 包含坐标轴的标签
+          },
+          xAxis: {
+            type: 'category',
+            data: [],
+            axisLabel: {
+              interval: 0,
+              formatter: function (value, index) {
+                let month = value.split('-');
+                return month[1] + '月';
+              },
+              textStyle: {
+                color: '#fff', // 修改字体颜色为红色
+                fontSize: '0.7rem',
+                fontFamily: 'AlibabaPuHuiTi' // 修改字体为Arial
+              }
+            }
+          },
+          yAxis: {
+            type: 'value',
+            axisLabel: {
+              textStyle: {
+                color: '#fff', // 修改字体颜色为红色
+                fontSize: '0.7rem',
+                fontFamily: 'AlibabaPuHuiTi' // 修改字体为Arial
+              }
+            }
+          },
+          series: []
+        },
+        tableHeader: [
+          '客户代号',
+          '生产工单号',
+          '编码',
+          '名称',
+          '当前工序',
+          '交付状态'
+        ],
+        config: {
+          header: [],
+          data: [],
+          align: ['center', 'center', 'center', 'center', 'center', 'center'],
+          headerBGC: '#031d42',
+          columnWidth: [110, 200],
+          headerHeight: 20,
+          oddRowBGC: '#031d42',
+          evenRowBGC: '#031d42',
+          waitTime: 5000,
+          rowNum: 12
+        },
+        // 1未交付2提前3按时4延期
+        deliveryStatusList: [
+          {
+            dictKey: 1,
+            dictValue: '未交付'
+          },
+          {
+            dictKey: 2,
+            dictValue: '提前'
+          },
+          {
+            dictKey: 3,
+            dictValue: '按时'
+          },
+          {
+            dictKey: 4,
+            dictValue: '延期'
+          }
+        ],
+        //1未生产2已生产3已完成
+        produceStatusList: [
+          {
+            dictKey: 1,
+            dictValue: '未生产'
+          },
+          {
+            dictKey: 2,
+            dictValue: '已生产'
+          },
+          {
+            dictKey: 3,
+            dictValue: '已完成'
+          }
+        ],
+        //1未采购2已采购3已入库
+        purchaseStatusList: [
+          {
+            dictKey: 1,
+            dictValue: '未采购'
+          },
+          {
+            dictKey: 2,
+            dictValue: '已采购'
+          },
+          {
+            dictKey: 3,
+            dictValue: '已入库'
+          }
+        ],
+        //scheduleStatus	排产状态 1待排产2已排产	integer(int32)*/
+        scheduleStatusList: [
+          {
+            dictKey: 1,
+            dictValue: '待排产'
+          },
+          {
+            dictKey: 2,
+            dictValue: '已排产'
+          }
+        ]
+      };
+    },
+    created() {
+      this.updateTimer = setInterval(this.updateTime, 1000);
+    },
+    mounted() {
+      this.getAllAmount();
+      this.getSalesFinishList();
+      setInterval(() => {
+        this.getSalesFinishList();
+      }, 3600000);
+    },
+    methods: {
+      /* 全屏切换 */
+      onFullscreen() {
+        this.isFullscreen = !this.isFullscreen;
+      },
+      //获取头部统计
+      async getAllAmount() {
+        let rest = await count({factoriesId: 0});
+        this.borderData.forEach(async (item) => {
+          if (item.titleName === '工单数量') {
+            item.value = rest.inProductCount;
+          } else if (item.titleName === '待生产数量') {
+            item.value = rest.planCount;
+          } else if (item.titleName === '已完成数量') {
+            item.value = rest.formedWeight;
+          } else if (item.titleName === '合格率') {
+            item.value = rest.qualifiedRate;
+          }
+        });
+        console.log(rest, 'rest');
+      },
+      //获取成品发货统计
+      async getMonthlySalesStatistic() {
+        let params = {
+          startDate: new Date().getFullYear() + '-01-01',
+          endDate: new Date().getFullYear() + '-12-31',
+          factoriesId: 0
+        }
+        let data = await completionCount(params);
+        let series = [
+          {
+            field: 'qualified',
+            name: '合格数量',
+            data: [],
+            type: 'bar',
+            itemStyle: {
+              color: '#03c391'
+            }
+          },
+          {
+            field: 'noQualifiedSum',
+            name: '不合格数量',
+            data: [],
+            type: 'bar',
+            itemStyle: {
+              color: '#ea5082'
+            }
+          },
+        ];
+        this.monthlySalesVolumeOption.xAxis.data = data.map(
+          (item) => item.inProductDate
+        );
+        series.forEach((item) => {
+          item.data = data.map((i) => i[item.field]);
+        });
+        this.monthlySalesVolumeOption.series = series;
+        this.salesChart.setOption(this.monthlySalesVolumeOption);
+      },
+      //获取月度产量统计
+      async getMonthlyProduceStatistic() {
+        let params = {
+          startDate: new Date().getFullYear() + '-01-01',
+          endDate: new Date().getFullYear() + '-12-31',
+          factoriesId: 0
+        }
+        let data = await completionCount(params);
+        let series = [
+          {
+            field: 'inWarehouseCount',
+            name: '成品入库数量',
+            data: [],
+            type: 'bar',
+            itemStyle: {
+              color: '#e9ac80'
+            }
+          },
+          {
+            field: 'packingWeightSum',
+            name: '成品入库重量',
+            data: [],
+            type: 'bar',
+            itemStyle: {
+              color: '#03c391'
+            }
+          }
+        ];
+        this.monthlyOutputOption.xAxis.data = data.map((item) => item.inProductDate);
+        series.forEach((item) => {
+          item.data = data.map((i) => i[item.field]);
+        });
+        this.monthlyOutputOption.series = series;
+        this.outputChart.setOption(this.monthlyOutputOption);
+      },
+      //生产工单
+      async getSalesFinishList() {
+        /*serialNo	客户代号	string
+days	交付状态 N剩余N天 0已完成 -1延期	integer(int32)
+deviceName 名称	integer(int32)
+deviceCode	设备编码 1未采购2已采购3已入库	integer(int32)
+code	工单号	string
+taskName 当前工序	*/
+        let data = await getSalesFinishListAPI({factoriesId: 0});
+        this.config = {
+          header: this.tableHeader.map(
+            (item) =>
+              `<div style="color: #f4d29c;font-size: 0.9rem;font-weight: bold">${item}</div>`
+          ),
+          data:
+            data.map((item) => {
+              let list = [];
+              for (let i in item) {
+                let div = '';
+                if (i === 'serialNo') {
+                  div = `<div class="white" style="font-size: 0.8rem;">${item[i]}</div>`;
+                  list[0] = div;
+                }
+                if (i === 'code') {
+                  div = `<div class="white" style="font-size: 0.8rem;">${item[i]}</div>`;
+                  list[1] = div;
+                }
+                if (i === 'deviceCode') {
+                  div = `<div class="yellow" style="font-size: 0.8rem;">${item[i]}</div>`;
+                  list[2] = div;
+                }
+                if (i === 'deviceName') {
+                    div = `<div class="white" style="font-size: 0.8rem;">${item[i]}</div>`;
+                    list[3] = div;
+                }
+                if (i === 'taskName') {
+                  div = `<div class="yellow" style="font-size: 0.8rem;">${item[i]}</div>`;
+                  list[4] = div;
+                }
+                if (i === 'days') {
+                  //1未交付2提前3按时4延期
+                  if (item[i] == '-1') {
+                    div = `<div class="white" style="font-size: 0.8rem;color:red">已延期</div>`;
+                    list[5] = div;
+                  }
+                  if (item[i] == '0') {
+                    div = `<div class="green" style="font-size: 0.8rem;">已完成</div>`;
+                    list[5] = div;
+                  }
+                  if (item[i] >  0) {
+                    div = `<div class="yellow" style="font-size: 0.8rem;">剩余${item[i]}天</div>`;
+                    list[5] = div;
+                  }
+                }
+              }
+              return list;
+            }) ?? [],
+          align: ['center', 'center', 'center', 'center', 'center', 'center'],
+          headerBGC: '#031d42',
+          columnWidth: [110, 200],
+          headerHeight: 30,
+          oddRowBGC: '#031d42',
+          evenRowBGC: '#031d42',
+          waitTime: 5000,
+          rowNum: 10
+        };
+      },
+      //实时更新日期
+      updateTime() {
+        let now = new Date();
+        let hours = now.getHours();
+        let minutes = now.getMinutes();
+        let seconds = now.getSeconds();
+        this.time =
+          hours.toString().padStart(2, '0') +
+          ':' +
+          minutes.toString().padStart(2, '0') +
+          ':' +
+          seconds.toString().padStart(2, '0');
+
+        let year = now.getFullYear();
+        let month = now.getMonth() + 1;
+        let day = now.getDate();
+        this.date = year + '年' + month + '月' + day + '日';
+
+        let weekInfo = {
+          1: '一',
+          2: '二',
+          3: '三',
+          4: '四',
+          5: '五',
+          6: '六',
+          0: '日'
+        };
+        this.week = '星期' + weekInfo[now.getDay()];
+      }
+    }
+  };
+</script>
+<style lang="scss" scoped>
+  .box-container {
+    font-size: 16px;
+    font-family: 'AlibabaPuHuiTi';
+
+    background: #011635;
+    display: flex;
+    flex-direction: column;
+
+    .box-header {
+      background-image: url('@/assets/border2.png');
+      background-repeat: no-repeat;
+      background-size: 100% 100%;
+      display: flex;
+      width: 100% !important;
+      height: 10%;
+      justify-content: space-between;
+
+      .title {
+        font-family: '优设标题黑';
+        font-size: 2.75rem;
+        flex: 1;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        color: #fff;
+        transform: translateY(-10%);
+        letter-spacing: 0.4rem;
+      }
+
+      .logo {
+        display: inline-block;
+        width: 20%;
+      }
+
+      .time {
+        width: 20%;
+        display: flex;
+        align-items: center;
+        justify-content: space-around;
+        flex-direction: column;
+        align-items: flex-end;
+        justify-content: flex-start;
+        font-size: 0.8rem;
+      }
+    }
+
+    .box-middle {
+      width: 100% !important;
+      height: 10% !important;
+      display: flex;
+      justify-content: space-evenly;
+      margin: 5px 0 10px 0;
+
+      .box-middle-content {
+        width: 15%;
+        height: 100%;
+        background-image: url('@/assets/border1.png');
+        background-repeat: no-repeat;
+        background-size: 100% 100%;
+        margin: 0px 28px 10px 0px;
+      }
+
+      .border-box-content {
+      }
+    }
+
+    .box-footer {
+      flex: 1;
+      display: flex;
+      justify-content: space-around;
+
+      .box-footer-left {
+        width: 50%;
+        height: 100%;
+
+        .box-echarts {
+          width: 100%;
+          height: 50%;
+          display: flex;
+          flex-direction: column;
+          background-image: url('@/assets/border3.png');
+          background-repeat: no-repeat;
+          background-size: 100% 100%;
+
+          .box-echarts-top {
+            height: 20%;
+            width: 100%;
+            display: flex;
+            align-items: center;
+            /* justify-content: space-around; */
+            transform: translateX(6%);
+            color: #fff;
+
+            .box-top-name {
+              display: inline-block;
+              font-size: 1.3rem;
+              font-weight: bold;
+            }
+
+            .box-top-unit {
+              display: inline-block;
+              font-size: 0.75rem;
+              transform: translate(0px, 20%);
+              letter-spacing: 1px;
+              font-weight: bold;
+            }
+          }
+
+          .monthly_sales_volume {
+            transform: translateX(-7%);
+            flex: 1;
+            //width: 100%;
+            //height: 100%;
+            #monthly_sales_volume {
+              width: 113%;
+              height: 100%;
+            }
+          }
+
+          .monthly_output {
+            transform: translateX(-7%);
+            flex: 1;
+
+            #monthly_output {
+              width: 113%;
+              height: 100%;
+              //width: 100%;
+              //height: 100%;
+            }
+          }
+        }
+      }
+
+      .box-footer-right {
+        width: 50%;
+        height: 100%;
+        background-image: url('@/assets/border4.png');
+        background-repeat: no-repeat;
+        background-size: 100% 100%;
+
+        .box-echarts-top {
+          height: 10.5%;
+          width: 100%;
+          display: flex;
+          align-items: center;
+          /* justify-content: space-around; */
+          transform: translateX(8%);
+          color: #fff;
+          font-size: 1.1rem;
+          letter-spacing: 2px;
+
+          .box-top-name {
+            display: inline-block;
+            font-size: 1.3rem;
+            font-weight: bold;
+          }
+        }
+      }
+    }
+  }
+
+  [v-cloak] {
+    display: none;
+  }
+</style>
+<style>
+  .white {
+    color: #ffffff;
+  }
+
+  .yellow {
+    color: #ffd16c;
+  }
+
+  .green {
+    color: green;
+  }
+
+  .red {
+    color: red;
+  }
+
+
+</style>