| | |
| | | <template> |
| | | <view> |
| | | <page-nav title="生产分析"></page-nav> |
| | | |
| | | <view :style="{height:'1400rpx',overflow: 'scroll',background:'#0659FF'}"> |
| | | <view class="body"> |
| | | <view class="body_head"> |
| | | <view class="body_head_title">生产看板</view> |
| | | <view> |
| | | <u-button type="" icon='calendar' iconColor="#0659FF" size="mini" @click="calendarShowTrue" |
| | | :text="calendarRange"> |
| | | </u-button> |
| | | </view> |
| | | </view> |
| | | <view class="body_content" style="height: 330rpx;"> |
| | | <view class="body_content_title"> |
| | | <view>生产总览</view> |
| | | <uni-tooltip content="一段时间内的生产数据" style='margin-left: 10rpx;'> |
| | | <u-icon name="question-circle"></u-icon> |
| | | </uni-tooltip> |
| | | </view> |
| | | <view class="flex_between"> |
| | | <view class="cont_item"> |
| | | <view class="cont_item_title">计划数量</view> |
| | | <view class="cont_item_content">{{topData.plan_qty?topData.plan_qty:'0'}}</view> |
| | | </view> |
| | | <u-line direction="col" color="#ccc" style='margin-top: 35rpx;' length='60rpx'></u-line> |
| | | <view class="cont_item"> |
| | | <view class="cont_item_title">完工数量</view> |
| | | <!-- <view class="cont_item_content">{{topData.end_qty?topData.end_qty:'0'}}</view> --> |
| | | <view class="cont_item_content"> |
| | | {{topData.good_qty+topData.ng_qty+topData.laborbad_qty+topData.materielbad_qty ?topData.good_qty+topData.ng_qty+topData.laborbad_qty+topData.materielbad_qty:'0'}} |
| | | </view> |
| | | </view> |
| | | <u-line direction="col" color="#ccc" style='margin-top: 35rpx;' length='60rpx'></u-line> |
| | | <view class="cont_item"> |
| | | <view class="cont_item_title">合格产出</view> |
| | | <view class="cont_item_content">{{topData.good_qty?topData.good_qty:'0'}}</view> |
| | | </view> |
| | | </view> |
| | | <view class="flex_between"> |
| | | <view class="cont_item"> |
| | | <view class="cont_item_title">缺陷产出</view> |
| | | <view class="cont_item_content">{{topData.ng_qty?topData.ng_qty:'0'}}</view> |
| | | </view> |
| | | <u-line direction="col" color="#ccc" style='margin-top: 35rpx;' length='60rpx'></u-line> |
| | | <view class="cont_item"> |
| | | <view class="cont_item_title">工废产出</view> |
| | | <view class="cont_item_content"> |
| | | {{topData.good_qty? topData.good_qty :'0'}} |
| | | </view> |
| | | </view> |
| | | <u-line direction="col" color="#ccc" style='margin-top: 35rpx;' length='60rpx'></u-line> |
| | | <view class="cont_item"> |
| | | <view class="cont_item_title">料废产出</view> |
| | | <view class="cont_item_content"> |
| | | {{topData.materielbad_qty ? topData.materielbad_qty :'0'}} |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | </view> |
| | | <view class="body_content"> |
| | | <view class="body_content_title" style="margin-bottom: 0;">生产工单</view> |
| | | <view style="display: flex;justify-content: center;align-items: center;"> |
| | | <!-- <view v-if="centerData.length===0" style="color: #c5c5c5;margin:120rpx auto;">暂无数据</view> --> |
| | | <echarts ref="pieEcharts" :option="pieOption" canvasId="pieEcharts" |
| | | style='width: 100%;height:360rpx;' /> |
| | | </view> |
| | | |
| | | </view> |
| | | <view class="body_content" style="height: 440rpx;"> |
| | | <view class="body_content_title" |
| | | style="margin-bottom: 0;display: flex;justify-content: space-between;"> |
| | | <view>不良统计Top5</view> |
| | | <view |
| | | style="background-color: #c5c5c5;padding: 5rpx;font-size: 12px;display: flex;border-radius: 6rpx;" |
| | | @click="operSheetShow=true">{{seleteValue}} |
| | | <u-icon :name="!operSheetShow?'arrow-down-fill':'arrow-up-fill'"></u-icon> |
| | | </view> |
| | | </view> |
| | | <view style="display: flex;justify-content: center;align-items: center;"> |
| | | <!-- <view v-if="footerData.length===0" style="color: #c5c5c5;margin:120rpx auto;">暂无数据</view> --> |
| | | <echarts ref="barEcharts" :option="barOption" canvasId="barEcharts" |
| | | style='width: 100%;height:360rpx;' /> |
| | | </view> |
| | | </view> |
| | | |
| | | |
| | | |
| | | </view> |
| | | |
| | | </view> |
| | | <uni-calendar ref="calendar" :range='true' :insert="false" @confirm="calendarConfirm" /> |
| | | |
| | | |
| | | <u-picker :show="operSheetShow" :columns="columns" :itemHeight='55' :closeOnClickOverlay='true' |
| | | @close='operSheetShow=false' @confirm='operSheetShowConfirm' @cancel='operSheetShow=false'></u-picker> |
| | | |
| | | </view> |
| | | </template> |
| | | |
| | | |
| | | <script> |
| | | import echarts from '../../components/echarts-uniapp/echarts-uniapp.vue' |
| | | import { |
| | | ProductionKanban |
| | | } from '../../config/api.js'; |
| | | |
| | | export default { |
| | | components: { |
| | | echarts |
| | | }, |
| | | onLoad(option) {}, |
| | | |
| | | onPullDownRefresh() { |
| | | setTimeout(() => { |
| | | this.init(() => { |
| | | uni.stopPullDownRefresh(); |
| | | }) |
| | | }, 1000); |
| | | }, |
| | | |
| | | data() { |
| | | return { |
| | | topRightMessageCount: '', |
| | | |
| | | calendarRange: new Date().getFullYear() + '-' + (new Date().getMonth() + 1).toString().padStart( |
| | | 2, '0') + '-01' + '~' + new Date().toISOString().slice(0, 10), |
| | | |
| | | operSheetShow: false, //切换不良统计Top5 底部滑出组件是否显示 |
| | | |
| | | columns: [ //按产品统计:prt 按工序统计:stp |
| | | ['按产品统计', '按工序统计'] |
| | | ], |
| | | seleteValue: '按产品统计', |
| | | |
| | | topData: {}, |
| | | centerData: [], |
| | | footerData: [], |
| | | |
| | | pieOption: {}, |
| | | barOption: {}, |
| | | |
| | | |
| | | } |
| | | }, |
| | | created() { |
| | | |
| | | }, |
| | | mounted() { |
| | | this.init() |
| | | this.topRightMessageCount = uni.getStorageSync('topRightMessageCount') |
| | | |
| | | this.getData() |
| | | |
| | | |
| | | }, |
| | | methods: { |
| | | init() { |
| | | uni.stopPullDownRefresh() |
| | | }, |
| | | // 获取安灯消息列表长度 |
| | | getTopRightMessageCountIsChange(val) { |
| | | this.topRightMessageCount = val |
| | | }, |
| | | async getData() { |
| | | const data = { |
| | | Ratetime: this.calendarRange, |
| | | defecttype: this.seleteValue === '按产品统计' ? 'prt' : 'stp', |
| | | } |
| | | const res = await ProductionKanban(data) |
| | | if (res.code === '200') { |
| | | |
| | | this.topData = res.data.dt0[0] |
| | | this.centerData = res.data.dt1.map(({ |
| | | wo_coum, |
| | | wo_status |
| | | }) => ({ |
| | | name: wo_status + ' ' + wo_coum, |
| | | value: wo_coum |
| | | })) |
| | | this.footerData = res.data.dt2 |
| | | |
| | | this.getPieEcharts() |
| | | this.getTop5Echarts() |
| | | } |
| | | }, |
| | | |
| | | // 绘制生产工单图 |
| | | getPieEcharts() { |
| | | |
| | | let sum = 0 |
| | | |
| | | this.centerData.forEach(i => { |
| | | sum += i.value |
| | | }) |
| | | |
| | | |
| | | this.pieOption = { |
| | | title: [{ |
| | | text: '工单数', |
| | | subtext: sum + '个', |
| | | // textStyle: { |
| | | // fontSize: 20, |
| | | // color: "black" |
| | | // }, |
| | | // subtextStyle: { |
| | | // fontSize: 20, |
| | | // color: 'black' |
| | | // }, |
| | | textAlign: "center", |
| | | x: '33.5%', |
| | | y: '40%', |
| | | }], |
| | | legend: { |
| | | type: "scroll", |
| | | orient: 'vertical', |
| | | left: '70%', |
| | | align: 'left', |
| | | top: 'middle', |
| | | textStyle: { |
| | | color: '#8C8C8C' |
| | | }, |
| | | height: 250 |
| | | }, |
| | | series: [{ |
| | | name: '无数据', |
| | | type: 'pie', |
| | | center: ['35%', '50%'], |
| | | radius: ['40%', '65%'], |
| | | clockwise: false, //饼图的扇区是否是顺时针排布 |
| | | avoidLabelOverlap: false, |
| | | label: { |
| | | normal: { |
| | | show: true, |
| | | position: 'outter', |
| | | formatter: function(parms) { |
| | | return parms.data.legendname |
| | | } |
| | | } |
| | | }, |
| | | labelLine: { |
| | | normal: { |
| | | length: 5, |
| | | length2: 3, |
| | | smooth: true, |
| | | } |
| | | }, |
| | | data: this.centerData |
| | | }] |
| | | }; |
| | | }, |
| | | //绘制不良统计Top5 |
| | | getTop5Echarts() { |
| | | console.log(this.footerData); |
| | | const barData = this.footerData.map(i => i.defect_qty) //柱状图数据 |
| | | const lineData = this.footerData.map(i => parseFloat((i.defect_qty * 100 / i.plan_qty).toFixed( |
| | | 2))) //折线图图数据 |
| | | const xData = this.seleteValue === '按产品统计' ? this.footerData.map(i => i.partname) : this.footerData.map( |
| | | i => i.stepname) //x轴数据 |
| | | |
| | | this.barOption = { |
| | | grid: { |
| | | left: '5%', |
| | | right: '5%', |
| | | bottom: '10%', |
| | | top: '12%', |
| | | containLabel: true |
| | | }, |
| | | xAxis: [{ |
| | | type: 'category', |
| | | data: xData, |
| | | axisPointer: { |
| | | type: 'shadow' |
| | | }, |
| | | axisLabel: { |
| | | interval: 0, |
| | | // rotate: 40, |
| | | formatter: function(value) { |
| | | if (value.length > 4) { |
| | | return `${value.slice(0, 4)}...`; |
| | | } |
| | | return value; |
| | | }, |
| | | } |
| | | }], |
| | | yAxis: [{ |
| | | type: 'value', |
| | | // name: 'Precipitation', |
| | | min: 0, |
| | | max: Math.floor((Math.max(...barData) * 1.2)), |
| | | // interval: 4, |
| | | splitNumber: 4, |
| | | axisLabel: { |
| | | formatter: '{value}' |
| | | } |
| | | }, |
| | | { |
| | | show: false, |
| | | type: 'value', |
| | | // name: 'Temperature', |
| | | min: 0, |
| | | max: Math.floor((Math.max(...lineData) * 1.2)), |
| | | // interval: 4, |
| | | splitNumber: 4, |
| | | axisLabel: { |
| | | formatter: '{value} %' |
| | | } |
| | | } |
| | | ], |
| | | series: [{ |
| | | name: 'Precipitation', |
| | | type: 'bar', |
| | | label: { |
| | | show: true |
| | | }, |
| | | barWidth: 20, |
| | | itemStyle: { |
| | | color: '#356bff' |
| | | }, |
| | | data: barData |
| | | }, |
| | | { |
| | | name: 'Temperature', |
| | | type: 'line', |
| | | yAxisIndex: 1, |
| | | label: { |
| | | show: true, |
| | | |
| | | }, |
| | | itemStyle: { |
| | | color: '#0000ff', |
| | | "normal": { |
| | | "barBorderRadius": 0, |
| | | "label": { |
| | | "show": true, |
| | | "position": "top", |
| | | formatter: function(p) { |
| | | return p.value + '%'; |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | data: lineData |
| | | } |
| | | ] |
| | | }; |
| | | }, |
| | | operSheetShowConfirm(val) { |
| | | this.seleteValue = val.value[0] |
| | | this.operSheetShow = false |
| | | |
| | | this.getData() |
| | | }, |
| | | calendarConfirm(val) { |
| | | // this.calendarRange = val.range.before + '~' + val.range.after |
| | | if (val.range.before < val.range.after) { |
| | | this.calendarRange = val.range.before + '~' + val.range.after |
| | | } else { |
| | | this.calendarRange = val.range.after + '~' + val.range.before |
| | | } |
| | | |
| | | if (val.range.before === '') { |
| | | this.calendarRange = new Date().toISOString().slice(0, 10) + '~' + val.range.after |
| | | } |
| | | if (val.range.after === '') { |
| | | this.calendarRange = val.range.before + '~' + new Date().toISOString().slice(0, 10) |
| | | } |
| | | if (val.range.before === '' && val.range.after === '') { |
| | | this.calendarRange = new Date().toISOString().slice(0, 10) + '~' + new Date().toISOString() |
| | | .slice(0, |
| | | 10) |
| | | } |
| | | |
| | | this.getData() |
| | | }, |
| | | calendarShowTrue() { |
| | | this.$refs.calendar.open(); |
| | | } |
| | | |
| | | |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | <style lang="scss" scoped> |
| | | @import url('@/style/global.css'); |
| | | |
| | | |
| | | |
| | | |
| | | ::v-deep .uicon-arrow-left>span { |
| | | display: block; |
| | | } |
| | | |
| | | .body { |
| | | // min-height: 1382rpx; |
| | | background-color: #0659FF !important; |
| | | display: flex; |
| | | flex-direction: column; |
| | | padding: 20rpx 36rpx; |
| | | } |
| | | |
| | | .body_head { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | line-height: 40rpx; |
| | | height: 40rpx; |
| | | } |
| | | |
| | | .body_head_title { |
| | | font-size: 36rpx; |
| | | // font-weight: 490; |
| | | color: #fff; |
| | | } |
| | | |
| | | ::v-deep .uni-select__input-placeholder { |
| | | color: #ccc; |
| | | } |
| | | |
| | | ::v-deep .uni-select__input-text { |
| | | color: #fff; |
| | | } |
| | | |
| | | ::v-deep .uniui-bottom { |
| | | color: #fff !important; |
| | | } |
| | | |
| | | ::v-deep .uniui-top { |
| | | color: #fff !important; |
| | | } |
| | | |
| | | .body_content { |
| | | background-color: #fff; |
| | | // max-height: 390rpx; |
| | | margin: 12rpx 0; |
| | | border-radius: 10rpx; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .body_content_title { |
| | | margin: 20rpx 30rpx 0; |
| | | font-size: 32rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | font-weight: bolder; |
| | | } |
| | | |
| | | ::v-deep .uni-tooltip-popup { |
| | | width: 260rpx; |
| | | } |
| | | |
| | | .cont_item { |
| | | width: 33%; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | height: 120rpx; |
| | | flex-direction: column; |
| | | line-height: 50rpx; |
| | | |
| | | .cont_item_title { |
| | | color: #ccc; |
| | | font-size: 28rpx |
| | | } |
| | | |
| | | .cont_item_content { |
| | | font-weight: bolder; |
| | | } |
| | | } |
| | | |
| | | ::v-deep .uicon-calendar { |
| | | font-size: 32rpx !important; |
| | | } |
| | | </style> |