Merge remote-tracking branch 'origin/master'
| | |
| | | "dayjs": "^1.11.13", |
| | | "echarts": "^5.5.1", |
| | | "element-plus": "^2.4.0", |
| | | "file-saver": "^2.0.5", |
| | | "moment": "^2.30.1", |
| | | "northglass-mes": "file:", |
| | | "pinia": "^2.1.6", |
| | |
| | | "dependencies": { |
| | | "reusify": "^1.0.4" |
| | | } |
| | | }, |
| | | "node_modules/file-saver": { |
| | | "version": "2.0.5", |
| | | "resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz", |
| | | "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" |
| | | }, |
| | | "node_modules/fill-range": { |
| | | "version": "7.0.1", |
| | |
| | | "reusify": "^1.0.4" |
| | | } |
| | | }, |
| | | "file-saver": { |
| | | "version": "2.0.5", |
| | | "resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz", |
| | | "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" |
| | | }, |
| | | "fill-range": { |
| | | "version": "7.0.1", |
| | | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", |
| | |
| | | "axios": "^1.6.8", |
| | | "babel-polyfill": "^6.26.0", |
| | | "chart.js": "^4.4.3", |
| | | "dayjs": "*", |
| | | "dayjs": "^1.11.13", |
| | | "echarts": "^5.5.1", |
| | | "element-plus": "^2.4.0", |
| | | "file-saver": "^2.0.5", |
| | | "http-proxy-middleware": "^3.0.0", |
| | | "moment": "^2.30.1", |
| | | "northglass-mes": "file:", |
| | |
| | | "reusify": "^1.0.4" |
| | | } |
| | | }, |
| | | "file-saver": { |
| | | "version": "2.0.5", |
| | | "resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz", |
| | | "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" |
| | | }, |
| | | "fill-range": { |
| | | "version": "7.0.1", |
| | | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", |
| | |
| | | "dayjs": "^1.11.13", |
| | | "echarts": "^5.5.1", |
| | | "element-plus": "^2.4.0", |
| | | "file-saver": "^2.0.5", |
| | | "moment": "^2.30.1", |
| | | "northglass-mes": "file:", |
| | | "pinia": "^2.1.6", |
| | |
| | | createTime: 'Return date', |
| | | }, |
| | | large: { |
| | | statistics: 'Statistics', |
| | | ExporttoExcel:'Export to Excel', |
| | | slice:'slice', |
| | | load: 'load', |
| | | edg: 'edg', |
| | |
| | | createTime :'返库日期', |
| | | }, |
| | | large:{ |
| | | statistics: '生产统计', |
| | | ExporttoExcel:'导出excel', |
| | | slice:'片', |
| | | load:'切割', |
| | | edg:'磨边', |
| | |
| | | }, |
| | | ] |
| | | }, |
| | | { |
| | | path: '/productionstatistics', |
| | | name: 'productionstatistics', |
| | | component: () => import('../views/largescreendisplay/productionstatistics.vue'), |
| | | children: [ |
| | | { |
| | | path: '/largescreendisplay/productionstatistics', |
| | | name: 'productionstatistics', |
| | | component: () => import('../views/largescreendisplay/productionstatistics.vue') |
| | | }, |
| | | ] |
| | | }, |
| | | ] |
| | | }) |
| | | // 导航守卫 |
| | |
| | | <script setup> |
| | | import { onBeforeUnmount, onMounted, onUnmounted, ref, computed, nextTick } from "vue"; |
| | | import { onBeforeUnmount, onMounted, onUnmounted, ref, computed, nextTick, watch } from "vue"; |
| | | import { useRouter } from "vue-router" |
| | | import { host, WebSocketHost } from '@/utils/constants' |
| | | import request from "@/utils/request" |
| | |
| | | const edgTwoTasks = ref([]) |
| | | const engineeringOne = ref([]) |
| | | const engineeringTwo = ref([]) |
| | | |
| | | let myChartLoad = null; |
| | | let myChartEdg = null; |
| | | let myChartTemp = null; |
| | | let myChartHollow = null; |
| | | const socketUrl1 = `ws://${WebSocketHost}:${host}/api/cacheGlass/api/talk/largenScreen`; |
| | | const handleMessage1 = (data) => { |
| | | edgOneTasks.value = data.edgOneTasks[0]; |
| | |
| | | engineeringTwo.value = data.engineeringTwo[0]; |
| | | if (numBoxes.value != edgOneTasks.value.length) { |
| | | numBoxes.value = edgOneTasks.value.length; |
| | | boxStart(); |
| | | initBoxes(boxes, numBoxes, 'firstup'); |
| | | |
| | | } |
| | | if (numBoxes2.value != edgTwoTasks.value.length) { |
| | | numBoxes2.value = edgTwoTasks.value.length; |
| | | boxStart2(); |
| | | initBoxes(boxes2, numBoxes2, 'firstup'); |
| | | } |
| | | const pieChartVOS = ref([]); |
| | | pieChartVOS.value = data.pieChartVOS[0]; |
| | | let oneFinish = ref(); |
| | | let twoFinish = ref(); |
| | | let oneunFinish = ref(); |
| | | let twounFinish = ref(); |
| | | if (pieChartVOS.value.length > 0) { |
| | | if (load.value) { |
| | | oneFinish = pieChartVOS.value[0].oneCompletedQuantity; |
| | | twoFinish = pieChartVOS.value[0].twoCompletedQuantity; |
| | | oneunFinish = pieChartVOS.value[0].oneUncompletedQuantity; |
| | | twounFinish = pieChartVOS.value[0].twoUncompletedQuantity; |
| | | } else { |
| | | oneFinish = pieChartVOS.value[0].oneCompletedArea; |
| | | twoFinish = pieChartVOS.value[0].twoCompletedArea; |
| | | oneunFinish = pieChartVOS.value[0].oneUncompletedArea; |
| | | twounFinish = pieChartVOS.value[0].twoUncompletedArea; |
| | | } |
| | | } else { |
| | | oneFinish = 0; |
| | | twoFinish = 0; |
| | | oneunFinish = 0; |
| | | twounFinish = 0; |
| | | } |
| | | |
| | | |
| | | const chartDomOne = chartOne.value; |
| | | if (!chartDomOne) { |
| | | console.error('图表容器未找到'); |
| | | return; |
| | | } |
| | | const myChartOne = echarts.init(chartDomOne); |
| | | const optionOne = { |
| | | title: { |
| | | text: t('large.load'), |
| | | subtext: '', |
| | | left: 'right' |
| | | }, |
| | | tooltip: { |
| | | trigger: 'item' |
| | | }, |
| | | legend: { |
| | | orient: 'vertical', |
| | | left: 'left' |
| | | }, |
| | | series: [ |
| | | { |
| | | name: '', |
| | | type: 'pie', |
| | | radius: '50%', |
| | | data: [ |
| | | { value: oneFinish, name: t('large.oneFinish') }, |
| | | { value: twoFinish, name: t('large.twoFinish') }, |
| | | { value: oneunFinish, name: t('large.oneunFinish') }, |
| | | { value: twounFinish, name: t('large.twounFinish') }, |
| | | ], |
| | | emphasis: { |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: 'rgba(0, 0, 0, 0.5)' |
| | | } |
| | | } |
| | | } |
| | | ], |
| | | graphic: [ |
| | | { |
| | | type: 'rect', |
| | | left: 'center', |
| | | top: 0, |
| | | shape: { |
| | | width: 80, |
| | | height: 30 |
| | | }, |
| | | style: { |
| | | text: load.value == true ? t('large.quantity') : t('large.are'), |
| | | fill: '#5470C6', |
| | | stroke: '#000', |
| | | lineWidth: 1 |
| | | }, |
| | | onclick: function () { |
| | | load.value = !load.value; |
| | | } |
| | | } |
| | | ] |
| | | }; |
| | | |
| | | myChartOne.setOption(optionOne); |
| | | timeAxisCreate("切割当天运行情况", chartLoad.value, myChartLoad, data.loadRunTimes[0]); |
| | | timeAxisCreate("磨边当天运行情况", chartEdg.value, myChartEdg, data.loadRunTimes[0]); |
| | | }; |
| | | let socket2 = null; |
| | | const temperingTaskType = ref([]) |
| | |
| | | temperingGlassInfoList.value = data.temperingGlassInfoList[0]; |
| | | if (numBoxes3.value != temperingTaskType.value) { |
| | | numBoxes3.value = temperingTaskType.value; |
| | | boxStart3(); |
| | | // initBoxes(boxes3, numBoxes3, 'secondleft'); |
| | | } |
| | | if (temperingGlassInfoList.value > 1) { |
| | | if (numBoxes4.value != 1) { |
| | | numBoxes4.value = 1; |
| | | // initBoxes(boxes4, numBoxes4, 'right'); |
| | | } |
| | | } else { |
| | | if (numBoxes4.value != 0) { |
| | | numBoxes4.value = 0; |
| | | // initBoxes(boxes4, numBoxes4, 'right'); |
| | | } |
| | | |
| | | } |
| | | |
| | | temperingGlassInfoInList.value = data.temperingGlassInfoInList[0]; |
| | | const pieChartVOS = ref([]); |
| | | pieChartVOS.value = data.pieChartVOS[0]; |
| | | let oneFinish = ref(); |
| | | let ondDamage = ref(); |
| | | let oneunFinish = ref(); |
| | | if (pieChartVOS.value.length > 0) { |
| | | if (temp.value) { |
| | | oneFinish = pieChartVOS.value[0].oneCompletedQuantity; |
| | | ondDamage = pieChartVOS.value[0].oneDamageQuantity; |
| | | oneunFinish = pieChartVOS.value[0].oneUncompletedQuantity; |
| | | } else { |
| | | oneFinish = pieChartVOS.value[0].oneCompletedArea; |
| | | ondDamage = pieChartVOS.value[0].oneDamageArea; |
| | | oneunFinish = pieChartVOS.value[0].oneUncompletedArea; |
| | | } |
| | | } else { |
| | | oneFinish = 0; |
| | | ondDamage = 0; |
| | | oneunFinish = 0; |
| | | } |
| | | |
| | | |
| | | const chartDomOne = chartThree.value; |
| | | if (!chartDomOne) { |
| | | console.error('图表容器未找到'); |
| | | return; |
| | | } |
| | | const myChartOne = echarts.init(chartDomOne); |
| | | const optionOne = { |
| | | title: { |
| | | text: t('large.temp'), |
| | | subtext: '', |
| | | left: 'right' |
| | | }, |
| | | tooltip: { |
| | | trigger: 'item' |
| | | }, |
| | | legend: { |
| | | orient: 'vertical', |
| | | left: 'left' |
| | | }, |
| | | series: [ |
| | | { |
| | | name: '', |
| | | type: 'pie', |
| | | radius: '50%', |
| | | data: [ |
| | | { value: oneFinish, name: t('large.oneFinish') }, |
| | | { value: ondDamage, name: t('large.ondDamage') }, |
| | | { value: oneunFinish, name: t('large.oneunFinish') }, |
| | | ], |
| | | emphasis: { |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: 'rgba(0, 0, 0, 0.5)' |
| | | } |
| | | } |
| | | } |
| | | ], |
| | | graphic: [ |
| | | { |
| | | type: 'rect', |
| | | left: 'center', |
| | | top: 0, |
| | | shape: { |
| | | width: 80, |
| | | height: 30 |
| | | }, |
| | | style: { |
| | | text: temp.value == true ? t('large.quantity') : t('large.are'), |
| | | fill: '#5470C6', |
| | | stroke: '#000', |
| | | lineWidth: 1 |
| | | }, |
| | | onclick: function () { |
| | | temp.value = !temp.value; |
| | | } |
| | | } |
| | | ] |
| | | }; |
| | | |
| | | myChartOne.setOption(optionOne); |
| | | }; |
| | | let socket3 = null; |
| | | const bigStorageCageUsage = ref([]) |
| | | const socketUrl3 = `ws://${WebSocketHost}:${host}/api/cacheVerticalGlass/api/talk/largenScreen`; |
| | | const handleMessage3 = (data) => { |
| | | bigStorageCageUsage.value = data.bigStorageCageUsage[0]; |
| | | const pieChartVOS = ref([]); |
| | | pieChartVOS.value = data.pieChartVOS[0]; |
| | | let oneFinish = ref(); |
| | | let twoFinish = ref(); |
| | | let oneunFinish = ref(); |
| | | let twounFinish = ref(); |
| | | if (pieChartVOS.value.length > 0) { |
| | | if (edg.value) { |
| | | oneFinish = pieChartVOS.value[0].oneCompletedQuantity; |
| | | twoFinish = pieChartVOS.value[0].twoCompletedQuantity; |
| | | oneunFinish = pieChartVOS.value[0].oneUncompletedQuantity; |
| | | twounFinish = pieChartVOS.value[0].twoUncompletedQuantity; |
| | | } else { |
| | | oneFinish = pieChartVOS.value[0].oneCompletedArea; |
| | | twoFinish = pieChartVOS.value[0].twoCompletedArea; |
| | | oneunFinish = pieChartVOS.value[0].oneUncompletedArea; |
| | | twounFinish = pieChartVOS.value[0].twoUncompletedArea; |
| | | } |
| | | } else { |
| | | oneFinish = 0; |
| | | twoFinish = 0; |
| | | oneunFinish = 0; |
| | | twounFinish = 0; |
| | | timeAxisCreate("钢化当天运行情况", chartTemp.value, myChartTemp, data.tempRunTimes[0]); |
| | | }; |
| | | // let mychart = null; // 建议设为外部变量避免重复初始化 |
| | | |
| | | // const formatToTimeString(datetime) => { |
| | | const formatToTimeString = (datetime) => { |
| | | const date = new Date(datetime); |
| | | const hours = date.getHours().toString().padStart(2, '0'); |
| | | const minutes = date.getMinutes().toString().padStart(2, '0'); |
| | | const seconds = date.getSeconds().toString().padStart(2, '0'); |
| | | return `${hours}:${minutes}:${seconds}`; |
| | | } |
| | | |
| | | |
| | | const chartDomOne = chartTwo.value; |
| | | if (!chartDomOne) { |
| | | const timeAxisCreate = (title, chartDom, mychart, RunTimes) => { |
| | | if (!chartDom) { |
| | | console.error('图表容器未找到'); |
| | | return; |
| | | } |
| | | const myChartOne = echarts.init(chartDomOne); |
| | | |
| | | if (!mychart) { |
| | | mychart = echarts.init(chartDom); |
| | | } |
| | | let firstTime = null; |
| | | let secondTime = null; |
| | | if (RunTimes.length == 0) { |
| | | firstTime = "00-00-00 00:00:00"; |
| | | secondTime = "00-00-00 00:00:00"; |
| | | } else { |
| | | firstTime = RunTimes[0].startTimestamp; |
| | | secondTime = RunTimes[0].endTimestamp; |
| | | } |
| | | |
| | | let datas = ref([]) |
| | | RunTimes.forEach((item, index) => { |
| | | // if (index % 2 === 0) { |
| | | datas.value.push([item.firstTimestamp, -1, formatToTimeString(item.firstTimestamp)]) |
| | | datas.value.push([item.secondTimestamp, 1, formatToTimeString(item.secondTimestamp)]) |
| | | // } else { |
| | | // datas.value.push([item.firstTimestamp, -1, item.diffMinutes]) |
| | | // datas.value.push([item.firstTimestamp, -1, item.diffMinutes]) |
| | | // } |
| | | }) |
| | | // console.log(D) |
| | | // 构建线段数组 |
| | | const segments = RunTimes.map(item => ({ |
| | | type: 'line', |
| | | symbol: 'none', |
| | | data: [ |
| | | [item.firstTimestamp, 0], |
| | | [item.secondTimestamp, 0] |
| | | ], |
| | | lineStyle: { |
| | | color: 'red', // 支持不同颜色 |
| | | width: 4 |
| | | }, |
| | | z: 1 |
| | | })); |
| | | |
| | | const optionOne = { |
| | | title: { |
| | | text: t('large.edg'), |
| | | subtext: '', |
| | | left: 'right' |
| | | text: title, |
| | | left: 'center', |
| | | top: '5%', |
| | | textStyle: { |
| | | fontSize: 16, |
| | | fontWeight: 'bold', |
| | | color: '#333' |
| | | } |
| | | }, |
| | | tooltip: { |
| | | trigger: 'item' |
| | | trigger: 'axis', |
| | | axisPointer: { type: 'line' }, |
| | | backgroundColor: 'rgba(50,50,50,0.7)', |
| | | textStyle: { color: '#fff' } |
| | | }, |
| | | legend: { |
| | | orient: 'vertical', |
| | | left: 'left' |
| | | grid: { |
| | | top: '20%', |
| | | left: '5%', |
| | | right: '5%', |
| | | bottom: '15%' |
| | | }, |
| | | xAxis: { |
| | | type: 'time', |
| | | axisLabel: { |
| | | formatter: function (value) { |
| | | const date = new Date(value); |
| | | return ( |
| | | date.getHours().toString().padStart(2, '0') + ':' + |
| | | date.getMinutes().toString().padStart(2, '0') + ':' + |
| | | date.getSeconds().toString().padStart(2, '0') |
| | | ); |
| | | }, |
| | | fontSize: 10, |
| | | color: '#666' |
| | | }, |
| | | splitLine: { show: false }, |
| | | axisLine: { lineStyle: { color: '#aaa' } } |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | min: -1, |
| | | max: 1, |
| | | show: false |
| | | }, |
| | | series: [ |
| | | // 主时间线(蓝色渐变,稍微加粗) |
| | | { |
| | | name: '', |
| | | type: 'pie', |
| | | radius: '50%', |
| | | type: 'line', |
| | | symbol: 'none', |
| | | data: [ |
| | | { value: oneFinish, name: t('large.oneFinish') }, |
| | | { value: twoFinish, name: t('large.twoFinish') }, |
| | | { value: oneunFinish, name: t('large.oneunFinish') }, |
| | | { value: twounFinish, name: t('large.twounFinish') }, |
| | | [firstTime, 0], |
| | | [secondTime, 0] |
| | | ], |
| | | emphasis: { |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: 'rgba(0, 0, 0, 0.5)' |
| | | lineStyle: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [ |
| | | { offset: 0, color: '#4facfe' }, |
| | | { offset: 1, color: '#00f2fe' } |
| | | ]), |
| | | width: 5, |
| | | shadowColor: 'rgba(0,0,0,0.2)', |
| | | shadowBlur: 8 |
| | | }, |
| | | z: 0 |
| | | }, |
| | | // 小段时间线(统一柔和的黄色) |
| | | ...segments.map(seg => ({ |
| | | ...seg, |
| | | lineStyle: { |
| | | color: '#ffcc00', |
| | | width: 3, |
| | | shadowColor: 'rgba(255,204,0,0.5)', |
| | | shadowBlur: 5 |
| | | } |
| | | } |
| | | } |
| | | ], |
| | | graphic: [ |
| | | })), |
| | | // 卡片事件(优化样式) |
| | | { |
| | | type: 'rect', |
| | | left: 'center', |
| | | top: 0, |
| | | type: 'custom', |
| | | renderItem: function (params, api) { |
| | | const x = api.coord([api.value(0), api.value(1)])[0]; |
| | | const yBase = api.coord([api.value(0), 0])[1]; |
| | | const yOffset = api.value(1) > 0 ? -70 : 30; |
| | | const cardWidth = 40; |
| | | const cardHeight = 22; |
| | | const cardX = x; |
| | | const cardY = yBase + yOffset; |
| | | const text = api.value(2); |
| | | |
| | | return { |
| | | type: 'group', |
| | | children: [ |
| | | { |
| | | type: 'line', |
| | | shape: { |
| | | width: 80, |
| | | height: 30 |
| | | x1: x, |
| | | y1: yBase, |
| | | x2: x, |
| | | y2: cardY + (api.value(1) > 0 ? cardHeight : 0) |
| | | }, |
| | | style: { |
| | | text: edg.value == true ? t('large.quantity') : t('large.are'), |
| | | fill: '#5470C6', |
| | | stroke: '#000', |
| | | lineWidth: 1 |
| | | stroke: '#888', |
| | | lineWidth: 1.5, |
| | | lineDash: [4, 2] |
| | | } |
| | | }, |
| | | onclick: function () { |
| | | edg.value = !edg.value; |
| | | { |
| | | type: 'rect', |
| | | shape: { |
| | | x: cardX - cardWidth / 2, |
| | | y: cardY, |
| | | width: cardWidth, |
| | | height: cardHeight, |
| | | r: 6 // 圆角 |
| | | }, |
| | | style: { |
| | | fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { offset: 0, color: '#ffffff' }, |
| | | { offset: 1, color: '#e0f7fa' } |
| | | ]), |
| | | stroke: '#00acc1', |
| | | lineWidth: 1, |
| | | shadowColor: '#ccc', |
| | | shadowBlur: 6 |
| | | } |
| | | }, |
| | | { |
| | | type: 'text', |
| | | style: { |
| | | text: text, |
| | | x: cardX, |
| | | y: cardY + cardHeight / 2, |
| | | textAlign: 'center', |
| | | textVerticalAlign: 'middle', |
| | | font: '12px sans-serif', |
| | | fill: '#006064' |
| | | } |
| | | } |
| | | ] |
| | | }; |
| | | |
| | | myChartOne.setOption(optionOne); |
| | | }, |
| | | encode: { |
| | | x: 0 |
| | | }, |
| | | data: datas.value, |
| | | z: 10 |
| | | } |
| | | ] |
| | | }; |
| | | |
| | | mychart.setOption(optionOne); |
| | | }; |
| | | |
| | | let socket4 = null; |
| | | const hollowBigStorageCageUsage = ref([]) |
| | | const socketUrl4 = `ws://${WebSocketHost}:${host}/api/hollowGlass/api/talk/largenScreen`; |
| | | const handleMessage4 = (data) => { |
| | | hollowBigStorageCageUsage.value = data.hollowBigStorageCageUsage[0]; |
| | | const pieChartVOS = ref([]); |
| | | pieChartVOS.value = data.pieChartVOS[0]; |
| | | let oneFinish = ref(); |
| | | let twoFinish = ref(); |
| | | let oneunFinish = ref(); |
| | | let twounFinish = ref(); |
| | | if (pieChartVOS.value.length > 0) { |
| | | if (hollow.value) { |
| | | oneFinish = pieChartVOS.value[0].oneCompletedQuantity; |
| | | twoFinish = pieChartVOS.value[0].twoCompletedQuantity; |
| | | oneunFinish = pieChartVOS.value[0].oneUncompletedQuantity; |
| | | twounFinish = pieChartVOS.value[0].twoUncompletedQuantity; |
| | | } else { |
| | | oneFinish = pieChartVOS.value[0].oneCompletedArea; |
| | | twoFinish = pieChartVOS.value[0].twoCompletedArea; |
| | | oneunFinish = pieChartVOS.value[0].oneUncompletedArea; |
| | | twounFinish = pieChartVOS.value[0].twoUncompletedArea; |
| | | } |
| | | } else { |
| | | oneFinish = 0; |
| | | twoFinish = 0; |
| | | oneunFinish = 0; |
| | | twounFinish = 0; |
| | | } |
| | | |
| | | |
| | | const chartDomOne = chartFour.value; |
| | | if (!chartDomOne) { |
| | | console.error('图表容器未找到'); |
| | | return; |
| | | } |
| | | const myChartOne = echarts.init(chartDomOne); |
| | | const optionOne = { |
| | | title: { |
| | | text: t('large.hollow'), |
| | | subtext: '', |
| | | left: 'right' |
| | | }, |
| | | tooltip: { |
| | | trigger: 'item' |
| | | }, |
| | | legend: { |
| | | orient: 'vertical', |
| | | left: 'left' |
| | | }, |
| | | series: [ |
| | | { |
| | | name: '', |
| | | type: 'pie', |
| | | radius: '50%', |
| | | data: [ |
| | | { value: oneFinish, name: t('large.oneFinish') }, |
| | | { value: twoFinish, name: t('large.twoFinish') }, |
| | | { value: oneunFinish, name: t('large.oneunFinish') }, |
| | | { value: twounFinish, name: t('large.twounFinish') }, |
| | | ], |
| | | emphasis: { |
| | | itemStyle: { |
| | | shadowBlur: 10, |
| | | shadowOffsetX: 0, |
| | | shadowColor: 'rgba(0, 0, 0, 0.5)' |
| | | } |
| | | } |
| | | } |
| | | ], |
| | | graphic: [ |
| | | { |
| | | type: 'rect', |
| | | left: 'center', |
| | | top: 0, |
| | | shape: { |
| | | width: 80, |
| | | height: 30 |
| | | }, |
| | | style: { |
| | | text: hollow.value == true ? t('large.quantity') : t('large.are'), |
| | | fill: '#5470C6', |
| | | stroke: '#000', |
| | | lineWidth: 1 |
| | | }, |
| | | onclick: function () { |
| | | hollow.value = !hollow.value; |
| | | } |
| | | } |
| | | ] |
| | | }; |
| | | |
| | | myChartOne.setOption(optionOne); |
| | | timeAxisCreate("中空当天运行情况", chartHollow.value, myChartHollow, data.hollowRunTimes[0]); |
| | | }; |
| | | let socket5 = null; |
| | | let myChart = null; |
| | | let myChart2 = null; |
| | | const productionVO = ref([]) |
| | | const socketUrl5 = `ws://${WebSocketHost}:${host}/api/cacheGlass/api/talk/largenScreenProduction`; |
| | | const handleMessage5 = (data) => { |
| | |
| | | console.error('图表容器未找到'); |
| | | return; |
| | | } |
| | | |
| | | const myChart = echarts.init(chartDom); |
| | | // myChart = echarts.init(chartDom); |
| | | if (!myChart) { |
| | | myChart = echarts.init(chartDom); |
| | | } |
| | | // else { |
| | | // myChart.clear(); |
| | | // } |
| | | |
| | | |
| | | const option = { |
| | |
| | | console.error('图表容器未找到'); |
| | | return; |
| | | } |
| | | const myChart2 = echarts.init(chartDom2); |
| | | // myChart2 = echarts.init(chartDom2); |
| | | if (!myChart2) { |
| | | myChart2 = echarts.init(chartDom2); |
| | | } |
| | | // else { |
| | | // myChart2.clear(); |
| | | // } |
| | | const option2 = { |
| | | title: { text: '' }, |
| | | tooltip: { trigger: 'axis' }, |
| | |
| | | }; |
| | | const chartRef = ref(null) |
| | | const chartRef2 = ref(null) |
| | | const chartOne = ref(null) |
| | | const chartTwo = ref(null) |
| | | const chartThree = ref(null) |
| | | const chartFour = ref(null) |
| | | const chartLoad = ref(null) |
| | | const chartEdg = ref(null) |
| | | const chartTemp = ref(null) |
| | | const chartHollow = ref(null) |
| | | |
| | | |
| | | // 在组件挂载时设置默认时间范围 |
| | | onMounted(async () => { |
| | | |
| | | boxStart(); |
| | | boxStart2(); |
| | | boxStart3(); |
| | | boxStart4(); |
| | | requestAnimationFrame(animate); |
| | | // boxStart(); |
| | | // boxStart2() |
| | | // boxStart3() |
| | | // boxStart4(); |
| | | // requestAnimationFrame(animate); |
| | | socket = initializeWebSocket(socketUrl, handleMessage); |
| | | socket1 = initializeWebSocket(socketUrl1, handleMessage1); |
| | | socket2 = initializeWebSocket(socketUrl2, handleMessage2); |
| | |
| | | closeWebSocket(socket5); |
| | | } |
| | | }); |
| | | onBeforeUnmount(() => { |
| | | closeWebSocket(); |
| | | }); |
| | | const numBoxes = ref(5); |
| | | const numBoxes2 = ref(5); |
| | | const numBoxes3 = ref(5); |
| | | const numBoxes = ref(1); |
| | | const numBoxes2 = ref(1); |
| | | const numBoxes3 = ref(1); |
| | | const numBoxes4 = ref(1); |
| | | const speed = 0.1; |
| | | const maxX = 200; // 终点X坐标 |
| | | const maxY = -75; // 终点Y坐标 |
| | | const maxX2 = 313; // 终点X坐标 |
| | | const maxY2 = -165; // 终点Y坐标 |
| | | const maxX3 = 95; // 终点X坐标 |
| | | const maxY3 = -85; // 终点Y坐标 |
| | | const maxX4 = 60; // 终点X坐标 |
| | | const maxY4 = 0; // 终点Y坐标 |
| | | const delayFrames = 600; // 每个 div 的延迟帧数 |
| | | const boxes = ref([]); |
| | | const boxes2 = ref([]); |
| | | const boxes3 = ref([]); |
| | | const boxes4 = ref([]); |
| | | // 初始化 div 数据 |
| | | const boxStart = () => { |
| | | boxes.value = []; |
| | | for (let i = 0; i < numBoxes.value; i++) { |
| | | boxes.value.push({ |
| | | const delayFrames = 600; |
| | | |
| | | const maxCoords = { |
| | | box1: { x: 250, y: -165 }, |
| | | box2: { x: 190, y: -75 }, |
| | | box3: { x: 95, y: -85 }, |
| | | box4: { x: 60, y: 0 }, |
| | | }; |
| | | |
| | | let boxes = ref([]); |
| | | let boxes2 = ref([]); |
| | | let boxes3 = ref([]); |
| | | let boxes4 = ref([]); |
| | | |
| | | const boxRefs = []; |
| | | const boxRefs2 = []; |
| | | const boxRefs3 = []; |
| | | const boxRefs4 = []; |
| | | let rafId = null; |
| | | |
| | | // ✅ 初始化 boxes,去除 style,保留 el |
| | | const initBoxes = (arr, countRef, direction) => { |
| | | const count = countRef.value; |
| | | const existing = arr.value; |
| | | |
| | | for (let i = 0; i < count; i++) { |
| | | if (!existing[i]) { |
| | | existing.push({ |
| | | x: 0, |
| | | y: 0, // 初始位置在底部 |
| | | direction: 'up', |
| | | y: 0, |
| | | direction, |
| | | delay: i * delayFrames, |
| | | style: { |
| | | width: '15px', |
| | | height: '15px', |
| | | backgroundColor: i % 2 === 0 ? '#911005' : '#911005', |
| | | position: 'absolute', |
| | | transform: `translate(0px, 0px)` |
| | | } |
| | | frameCount: 0, |
| | | el: null |
| | | }); |
| | | } else { |
| | | // 重置已有的 box 数据 |
| | | existing[i].x = 0; |
| | | existing[i].y = 0; |
| | | existing[i].direction = direction; |
| | | existing[i].delay = i * delayFrames; |
| | | existing[i].frameCount = 0; |
| | | } |
| | | } |
| | | const boxStart2 = () => { |
| | | boxes2.value = []; |
| | | for (let i = 0; i < numBoxes2.value; i++) { |
| | | boxes2.value.push({ |
| | | x: 0, |
| | | y: 0, // 初始位置在底部 |
| | | direction: 'up', |
| | | delay: i * delayFrames, |
| | | style: { |
| | | width: '15px', |
| | | height: '15px', |
| | | backgroundColor: i % 2 === 0 ? '#911005' : '#911005', |
| | | position: 'absolute', |
| | | transform: `translate(0px, 0px)` |
| | | if (existing.length > count) { |
| | | existing.length = count; |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | const boxStart3 = () => { |
| | | boxes3.value = []; |
| | | for (let i = 0; i < numBoxes3.value; i++) { |
| | | boxes3.value.push({ |
| | | x: 0, |
| | | y: 0, // 初始位置在底部 |
| | | direction: 'left', |
| | | delay: i * delayFrames, |
| | | style: { |
| | | width: '15px', |
| | | height: '15px', |
| | | backgroundColor: i % 2 === 0 ? '#911005' : '#911005', |
| | | position: 'absolute', |
| | | transform: `translate(0px, 0px)` |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | const boxStart4 = () => { |
| | | boxes4.value = []; |
| | | for (let i = 0; i < numBoxes4.value; i++) { |
| | | boxes4.value.push({ |
| | | x: 0, |
| | | y: 0, // 初始位置在底部 |
| | | direction: 'right', |
| | | delay: i * delayFrames, |
| | | style: { |
| | | width: '15px', |
| | | height: '15px', |
| | | backgroundColor: i % 2 === 0 ? '#911005' : '#911005', |
| | | position: 'absolute', |
| | | transform: `translate(0px, 0px)` |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | const animate = () => { |
| | | boxes.value.forEach((box) => { |
| | | }; |
| | | |
| | | // ✅ 动画函数 |
| | | const animateBox = (box, maxX, maxY) => { |
| | | if (box.delay > 0) { |
| | | box.delay--; |
| | | return; |
| | | } |
| | | if (box.direction === 'up') { |
| | | |
| | | switch (box.direction) { |
| | | case 'firstup': |
| | | box.y -= speed; |
| | | if (box.y <= maxY) { |
| | | // box.y = 0; // 确保不会超出界限 |
| | | box.direction = 'left'; |
| | | } |
| | | } else if (box.direction === 'left') { |
| | | if (box.y <= maxY) box.direction = 'firstleft'; |
| | | break; |
| | | case 'firstleft': |
| | | box.x -= speed; |
| | | if (box.x <= -maxX) { |
| | | box.x = 0; |
| | | box.y = 0; // 重新回到底部 |
| | | box.direction = 'up'; |
| | | box.y = 0; |
| | | box.direction = 'firstup'; |
| | | } |
| | | } |
| | | box.style = { |
| | | ...box.style, |
| | | transform: `translate(${box.x}px, ${box.y}px)` |
| | | }; |
| | | }); |
| | | boxes2.value.forEach((box) => { |
| | | if (box.delay > 0) { |
| | | box.delay--; |
| | | return; |
| | | } |
| | | if (box.direction === 'up') { |
| | | break; |
| | | case 'secondup': |
| | | box.y -= speed; |
| | | if (box.y <= maxY2) { |
| | | // box.y = 0; // 确保不会超出界限 |
| | | box.direction = 'left'; |
| | | } |
| | | } else if (box.direction === 'left') { |
| | | box.x -= speed; |
| | | if (box.x <= -maxX2) { |
| | | if (box.y <= maxY) { |
| | | box.x = 0; |
| | | box.y = 0; // 重新回到底部 |
| | | box.direction = 'up'; |
| | | box.y = 0; |
| | | box.direction = 'secondleft'; |
| | | } |
| | | } |
| | | box.style = { |
| | | ...box.style, |
| | | transform: `translate(${box.x}px, ${box.y}px)` |
| | | }; |
| | | }); |
| | | boxes3.value.forEach((box) => { |
| | | if (box.delay > 0) { |
| | | box.delay--; |
| | | return; |
| | | } |
| | | if (box.direction === 'up') { |
| | | box.y -= speed; |
| | | if (box.y <= maxY3) { |
| | | // box.y = 0; // 确保不会超出界限 |
| | | box.direction = 'left'; |
| | | box.x = 0; |
| | | box.y = 0; // 重新回到底部 |
| | | } |
| | | } else if (box.direction === 'left') { |
| | | break; |
| | | case 'secondleft': |
| | | box.x -= speed; |
| | | if (box.x <= -maxX3) { |
| | | |
| | | box.direction = 'up'; |
| | | if (box.x <= -maxX) { |
| | | box.direction = 'secondup'; |
| | | } |
| | | } |
| | | box.style = { |
| | | ...box.style, |
| | | transform: `translate(${box.x}px, ${box.y}px)` |
| | | }; |
| | | }); |
| | | boxes4.value.forEach((box) => { |
| | | if (box.delay > 0) { |
| | | box.delay--; |
| | | return; |
| | | } |
| | | if (box.direction === 'right') { |
| | | break; |
| | | case 'right': |
| | | box.x += speed; |
| | | if (box.x >= maxX4) { |
| | | // box.y = 0; // 确保不会超出界限 |
| | | if (box.x >= maxX) { |
| | | box.x = 0; |
| | | box.y = 0; // 重新回到底部 |
| | | box.y = 0; |
| | | } |
| | | break; |
| | | } |
| | | |
| | | box.frameCount++; |
| | | if (box.frameCount % 2 === 0 && box.el) { |
| | | box.el.style.top = `${box.y}px`; |
| | | if (box.x > 0) { |
| | | box.el.style.right = `${box.x}px`; |
| | | box.el.style.left = ''; |
| | | } else { |
| | | box.el.style.left = `${box.x}px`; |
| | | box.el.style.right = ''; |
| | | } |
| | | } |
| | | box.style = { |
| | | ...box.style, |
| | | transform: `translate(${box.x}px, ${box.y}px)` |
| | | }; |
| | | |
| | | const animate = () => { |
| | | boxes.value.forEach(box => animateBox(box, maxCoords.box1.x, maxCoords.box1.y)); |
| | | boxes2.value.forEach(box => animateBox(box, maxCoords.box2.x, maxCoords.box2.y)); |
| | | boxes3.value.forEach(box => animateBox(box, maxCoords.box3.x, maxCoords.box3.y)); |
| | | boxes4.value.forEach(box => animateBox(box, maxCoords.box4.x, maxCoords.box4.y)); |
| | | |
| | | rafId = requestAnimationFrame(animate); |
| | | }; |
| | | |
| | | const boxStart = () => { |
| | | initBoxes(boxes, numBoxes, 'firstup'); |
| | | initBoxes(boxes2, numBoxes2, 'firstup'); |
| | | initBoxes(boxes3, numBoxes3, 'secondleft'); |
| | | initBoxes(boxes4, numBoxes4, 'right'); |
| | | }; |
| | | |
| | | watch( |
| | | () => boxes.value, |
| | | async (newVal) => { |
| | | await nextTick(); |
| | | newVal.forEach((box, i) => { |
| | | box.el = boxRefs[i]; |
| | | }); |
| | | requestAnimationFrame(animate); |
| | | }; |
| | | }, |
| | | { deep: true, immediate: true } |
| | | |
| | | ); |
| | | watch( |
| | | () => boxes2.value, |
| | | async (newVal) => { |
| | | await nextTick(); |
| | | newVal.forEach((box, i) => { |
| | | box.el = boxRefs2[i]; |
| | | }); |
| | | }, |
| | | { deep: true, immediate: true } |
| | | ); |
| | | |
| | | watch( |
| | | () => boxes3.value, |
| | | async (newVal) => { |
| | | await nextTick(); |
| | | newVal.forEach((box, i) => { |
| | | box.el = boxRefs3[i]; |
| | | }); |
| | | }, |
| | | { deep: true, immediate: true } |
| | | ); |
| | | |
| | | watch( |
| | | () => boxes4.value, |
| | | async (newVal) => { |
| | | await nextTick(); |
| | | newVal.forEach((box, i) => { |
| | | box.el = boxRefs4[i]; |
| | | }); |
| | | }, |
| | | { deep: true, immediate: true } |
| | | ); |
| | | onMounted(() => { |
| | | boxStart(); |
| | | animate(); |
| | | }); |
| | | |
| | | onBeforeUnmount(() => { |
| | | cancelAnimationFrame(rafId); |
| | | rafId = null; |
| | | boxes.value.length = 0; |
| | | boxes2.value.length = 0; |
| | | boxes3.value.length = 0; |
| | | boxes4.value.length = 0; |
| | | }); |
| | | onUnmounted(() => { |
| | | if (rafId) { |
| | | cancelAnimationFrame(rafId); // 停止动画,防止内存泄漏 |
| | | } |
| | | }); |
| | | // onBeforeUnmount(() => { |
| | | // cancelAnimationFrame(rafId); |
| | | // }); |
| | | |
| | | //嵌入历史任务 |
| | | const iframeUrl1 = ref(''); |
| | | const iframe1 = ref(false); |
| | |
| | | const handlehistorical10 = () => { |
| | | iframe10.value = true; |
| | | iframeUrl10.value = `${window.location.origin}/#/largescreendisplay/statistics`; |
| | | }; |
| | | const iframeUrl11 = ref(''); |
| | | const iframe11 = ref(false); |
| | | const handlehistorical11 = () => { |
| | | iframe11.value = true; |
| | | iframeUrl11.value = `${window.location.origin}/#/largescreendisplay/productionstatistics`; |
| | | }; |
| | | const transposedData = computed(() => { |
| | | const keys = Object.keys(fieldNames); // 按 fieldNames 的顺序 |
| | |
| | | <iframe :src="iframeUrl10" marginwidth="2000px" marginheight="2000px" width="100%" height="750px" |
| | | frameborder="0"></iframe> |
| | | </el-dialog> |
| | | <el-dialog v-model="iframe11" top="5vh" width="95%" @close="iframeUrl11 = ''"> |
| | | <iframe :src="iframeUrl11" marginwidth="2000px" marginheight="2000px" width="100%" height="750px" |
| | | frameborder="0"></iframe> |
| | | </el-dialog> |
| | | |
| | | <div class="awatch" style="display: flex;"> |
| | | <!-- 生产统计 --> |
| | |
| | | <div ref="chartRef" style="width: 600px; height: 400px;" @dblclick="handlehistorical10()"></div> |
| | | </div> |
| | | <div style="width: 100%;height: 245px;display: flex;"> |
| | | <div ref="chartOne" style="width: 100%;"></div> |
| | | <div ref="chartLoad" style="width: 100%;"></div> |
| | | </div> |
| | | <div style="width: 100%;height: 245px;display: flex;"> |
| | | <div ref="chartTwo" style="width: 100%;"></div> |
| | | <div ref="chartEdg" style="width: 100%;"></div> |
| | | </div> |
| | | </div> |
| | | <div style="width: 50%;"> |
| | | <div class="img-screen" alt="Screen"> |
| | | <div> |
| | | <el-button type="primary" style="top:40px;left:670px;position: absolute;" @click="handlehistorical11()">{{ |
| | | $t('large.statistics') }}</el-button> |
| | | </div> |
| | | <!-- 钢化半透明色块 --> |
| | | <div |
| | | style="width: 80px;height: 40px;top: 315px;left: 130px;position: absolute;background-color: rgba(0, 0, 0, 0.5);color: white;font-size: 12px;z-index: 999;"> |
| | | <div>{{ $t('large.quantity') }}:{{ productionVO.length > 0 ? productionVO[6].countOut : 0 }}{{ $t('large.slice') }}</div> |
| | | <div>{{ $t('large.quantity') }}:{{ productionVO.length > 0 ? productionVO[6].countOut : 0 }}{{ |
| | | $t('large.slice') }}</div> |
| | | <div>{{ $t('large.are') }}:{{ productionVO.length > 0 ? productionVO[6].totalAreaOut : 0 }}m²</div> |
| | | </div> |
| | | <!-- 中空一线半透明色块 --> |
| | | <div |
| | | style="width: 80px;height: 40px;top: 235px;left: 450px;position: absolute;background-color: rgba(0, 0, 0, 0.5);color: white;font-size: 12px;z-index: 999;"> |
| | | <div>{{ $t('large.quantity') }}:{{ productionVO.length > 0 ? productionVO[6].hollowCountOutOne : 0 }}{{ $t('large.slice') }}</div> |
| | | <div>{{ $t('large.quantity') }}:{{ productionVO.length > 0 ? productionVO[6].hollowCountOutOne : 0 }}{{ |
| | | $t('large.slice') }}</div> |
| | | <div>{{ $t('large.are') }}:{{ productionVO.length > 0 ? productionVO[6].hollowTotalAreaOutOne : 0 }}m²</div> |
| | | </div> |
| | | <!-- 中空二线半透明色块 --> |
| | | <div |
| | | style="width: 80px;height: 40px;top: 130px;left: 450px;position: absolute;background-color: rgba(0, 0, 0, 0.5);color: white;font-size: 12px;z-index: 999;"> |
| | | <div>{{ $t('large.quantity') }}:{{ productionVO.length > 0 ? productionVO[6].hollowCountOutTwo : 0 }}{{ $t('large.slice') }}</div> |
| | | <div>{{ $t('large.quantity') }}:{{ productionVO.length > 0 ? productionVO[6].hollowCountOutTwo : 0 }}{{ |
| | | $t('large.slice') }}</div> |
| | | <div>{{ $t('large.are') }}:{{ productionVO.length > 0 ? productionVO[6].hollowTotalAreaOutTwo : 0 }}m²</div> |
| | | </div> |
| | | <!-- 切割二线半透明色块 --> |
| | | <div |
| | | style="width: 80px;height: 40px;top: 542px;left: 410px;position: absolute;background-color: rgba(0, 0, 0, 0.5);color: white;font-size: 12px;z-index: 999;"> |
| | | <div>{{ $t('large.quantity') }}:{{ productionVO.length > 0 ? productionVO[6].countOutOne : 0 }}{{ $t('large.slice') }}</div> |
| | | <div>{{ $t('large.quantity') }}:{{ productionVO.length > 0 ? productionVO[6].countOutOne : 0 }}{{ |
| | | $t('large.slice') }}</div> |
| | | <div>{{ $t('large.are') }}:{{ productionVO.length > 0 ? productionVO[6].totalAreaOutOne : 0 }}m²</div> |
| | | </div> |
| | | <!-- 切割一线半透明色块 --> |
| | | <div |
| | | style="width: 80px;height: 40px;top: 490px;left: 410px;position: absolute;background-color: rgba(0, 0, 0, 0.5);color: white;font-size: 12px;z-index: 999;"> |
| | | <div>{{ $t('large.quantity') }}:{{ productionVO.length > 0 ? productionVO[6].countOutTwo : 0 }}{{ $t('large.slice') }}</div> |
| | | <div>{{ $t('large.quantity') }}:{{ productionVO.length > 0 ? productionVO[6].countOutTwo : 0 }}{{ |
| | | $t('large.slice') }}</div> |
| | | <div>{{ $t('large.are') }}:{{ productionVO.length > 0 ? productionVO[6].totalAreaOutTwo : 0 }}m²</div> |
| | | </div> |
| | | <!-- 磨边一线半透明色块 --> |
| | | <div |
| | | style="width: 80px;height: 40px;top: 369px;left: 450px;position: absolute;background-color: rgba(0, 0, 0, 0.5);color: white;font-size: 12px;z-index: 999;"> |
| | | <div>{{ $t('large.quantity') }}:{{ productionVO.length > 0 ? productionVO[6].countOutOne : 0 }}{{ $t('large.slice') }}</div> |
| | | <div>{{ $t('large.quantity') }}:{{ productionVO.length > 0 ? productionVO[6].countOutOne : 0 }}{{ |
| | | $t('large.slice') }}</div> |
| | | <div>{{ $t('large.are') }}:{{ productionVO.length > 0 ? productionVO[6].totalAreaOutOne : 0 }}m²</div> |
| | | </div> |
| | | <!-- 磨边二线半透明色块 --> |
| | | <div |
| | | style="width: 80px;height: 40px;top: 410px;left: 450px;position: absolute;background-color: rgba(0, 0, 0, 0.5);color: white;font-size: 12px;z-index: 999;"> |
| | | <div>{{ $t('large.quantity') }}:{{ productionVO.length > 0 ? productionVO[6].countOutTwo : 0 }}{{ $t('large.slice') }}</div> |
| | | <div>{{ $t('large.quantity') }}:{{ productionVO.length > 0 ? productionVO[6].countOutTwo : 0 }}{{ |
| | | $t('large.slice') }}</div> |
| | | <div>{{ $t('large.are') }}:{{ productionVO.length > 0 ? productionVO[6].totalAreaOutTwo : 0 }}m²</div> |
| | | </div> |
| | | <!-- 钢化前大理{{ $t('large.slice') }}半透明色块 --> |
| | | <div |
| | | style="width: 80px;height: 40px;top: 380px;left: 290px;position: absolute;background-color: rgba(0, 0, 0, 0.5);color: white;font-size: 12px;z-index: 999;"> |
| | | <div>{{ $t('large.quantity') }}:{{ productionVO.length > 0 ? productionVO[6].countIn : 0 }}{{ $t('large.slice') }}</div> |
| | | <div>{{ $t('large.quantity') }}:{{ productionVO.length > 0 ? productionVO[6].countIn : 0 }}{{ |
| | | $t('large.slice') }}</div> |
| | | <div>{{ $t('large.are') }}:{{ productionVO.length > 0 ? productionVO[6].totalAreaIn : 0 }}m²</div> |
| | | </div> |
| | | |
| | |
| | | <!-- 磨边色块 --> |
| | | <div class="container" |
| | | style="position: relative;width: 15px;height: 15px;top: 500px;left: 638px;position: absolute;"> |
| | | <div v-for="(box, index) in boxes2" :key="index" class="box" :style="box.style"></div> |
| | | <div v-for="(box, i) in boxes2" :key="`b1-${i}`" class="moving-rect" :ref="el => boxRefs2[i] = el" /> |
| | | </div> |
| | | <div class="container" |
| | | style="position: relative;width: 15px;height: 15px;top: 548px;left: 690px;position: absolute;"> |
| | | <div v-for="(box, index) in boxes" :key="index" class="box" :style="box.style"></div> |
| | | <div v-for="(box, i) in boxes" :key="`b1-${i}`" class="moving-rect" :ref="el => boxRefs[i] = el" /> |
| | | </div> |
| | | <!-- 钢化运动色块 --> |
| | | <div class="container" |
| | | style="position: relative;width: 15px;height: 15px;top: 370px;left: 195px;position: absolute;background-color: red;"> |
| | | <div v-for="(box, index) in boxes3" :key="index" class="box" :style="box.style"></div> |
| | | style="position: relative;width: 15px;height: 15px;top: 370px;left: 195px;position: absolute;"> |
| | | <div v-for="(box, i) in boxes3" :key="`b1-${i}`" class="moving-rect" :ref="el => boxRefs3[i] = el" /> |
| | | </div> |
| | | <div class="container" |
| | | style="position: relative;width: 15px;height: 15px;top: 284px;left: 489px;position: absolute;"> |
| | | <div v-for="(box, index) in boxes4" :key="index" class="box" :style="box.style"></div> |
| | | <div v-for="(box, i) in boxes4" :key="`b1-${i}`" class="moving-rect" :ref="el => boxRefs4[i] = el" /> |
| | | </div> |
| | | <!-- <div class="moving-rect lipiana" v-show="true">1111111111</div> --> |
| | | <!-- 钢化前大理片色块 --> |
| | | <div class="container" v-for="(item, index) in bigStorageCageUsage.slice(0, 1)" :key="index" |
| | | :style='"position: relative;width: " + (37 * item.percentage / 100) + "px;height: 13px;top: 374px;left: 309px;position: absolute;background-color: #911005;"'> |
| | |
| | | <div ref="chartRef2" style="width: 600px; height: 400px;" @dblclick="handlehistorical10()"></div> |
| | | </div> |
| | | <div style="width: 100%;height: 245px;display: flex;"> |
| | | <div ref="chartThree" style="width: 100%;"></div> |
| | | <div ref="chartTemp" style="width: 100%;"></div> |
| | | </div> |
| | | <div style="width: 100%;height: 245px;display: flex;"> |
| | | <div ref="chartFour" style="width: 100%;"></div> |
| | | <div ref="chartHollow" style="width: 100%;"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | .box { |
| | | transition: transform 0.016s linear; |
| | | } |
| | | |
| | | .lipiana { |
| | | width: 15px; |
| | | height: 9px; |
| | | background-color: #529b2e; |
| | | top: 190px; |
| | | right: 530px; |
| | | transform: translateX(-50%); |
| | | animation: move-lipiana 1s infinite; |
| | | } |
| | | |
| | | @keyframes move-lipiana { |
| | | 0% { |
| | | right: 530px; |
| | | } |
| | | |
| | | 100% { |
| | | right: calc(100% - 350px); |
| | | } |
| | | } |
| | | |
| | | .moving-rect { |
| | | width: 15px; |
| | | height: 15px; |
| | | background-color: #911005; |
| | | position: absolute; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div> |
| | | <div style="display: flex;width: 1770px;"> |
| | | <el-date-picker v-model="timeRange" type="datetimerange" range-separator="至" |
| | | :start-placeholder="$t('reportmanage.starttime')" style="margin-left: 15px;" value-format="YYYY-MM-DD hh:mm:ss" |
| | | :end-placeholder="$t('reportmanage.endtime')"> |
| | | <el-date-picker v-model="timeRange" type="daterange" range-separator="至" |
| | | :start-placeholder="$t('reportmanage.starttime')" :end-placeholder="$t('reportmanage.endtime')" |
| | | value-format="YYYY-MM-DD" style="margin-left: 15px;"> |
| | | </el-date-picker> |
| | | <el-button type="primary" style="margin-left: 10px;margin-bottom: 10px;" |
| | | @click="sethistorical()">{{ $t('reportmanage.inquire') }}</el-button> |
| | | <el-button type="primary" style="margin-left: 10px;margin-bottom: 10px;" @click="sethistorical()">{{ |
| | | $t('reportmanage.inquire') }}</el-button> |
| | | </div> |
| | | <el-table ref="table" style="margin-top: 20px;height: 580px;width: 1770px;" :data="tableDatax" |
| | | :header-cell-style="{ background: '#F2F3F5 ', color: '#1D2129' }"> |
| | |
| | | <el-table-column prop="countOut" align="center" :label="$t('large.countOut')" min-width="40" /> |
| | | <el-table-column prop="totalAreaOut" align="center" :label="$t('large.totalAreaOut')" min-width="40" /> |
| | | <el-table-column prop="hollowCountOutOne" align="center" :label="$t('large.hollowCountOutOne')" min-width="40" /> |
| | | <el-table-column prop="hollowTotalAreaOutOne" align="center" :label="$t('large.hollowTotalAreaOutOne')" min-width="40" /> |
| | | <el-table-column prop="hollowTotalAreaOutOne" align="center" :label="$t('large.hollowTotalAreaOutOne')" |
| | | min-width="40" /> |
| | | <el-table-column prop="hollowCountOutTwo" align="center" :label="$t('large.hollowCountOutTwo')" min-width="40" /> |
| | | <el-table-column prop="hollowTotalAreaOutTwo" align="center" :label="$t('large.hollowTotalAreaOutTwo')" |
| | | min-width="40"/> |
| | |
| | | <groupId>org.springframework.boot</groupId> |
| | | <artifactId>spring-boot-starter-thymeleaf</artifactId> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>com.alibaba</groupId> |
| | | <artifactId>easyexcel</artifactId> |
| | | <version>3.1.3</version> |
| | | </dependency> |
| | | </dependencies> |
| | | |
| | | |
| | |
| | | package com.mes.largenscreen.controller; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.mes.largenscreen.entity.DailyProductionData; |
| | | import com.mes.largenscreen.entity.DailyProductionVO; |
| | | import com.mes.largenscreen.entity.DateRequest; |
| | | import com.mes.largenscreen.service.LargenScreenService; |
| | |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @Author : zhoush |
| | |
| | | return Result.build(200, "查询成功", largenScreenService.queryDailyProduction(query)); |
| | | } |
| | | |
| | | @ApiOperation("按照条件统计每日生产情况导出") |
| | | @GetMapping("/exportDailyProduction") |
| | | public void exportDailyProduction() { |
| | | largenScreenService.exportDailyProduction(); |
| | | } |
| | | |
| | | @ApiOperation("按照条件统计生产情况") |
| | | @PostMapping("/queryProduction") |
| | | public Result<List<DailyProductionData>> queryProduction(@RequestBody @Validated DateRequest query) { |
| | | return Result.build(200, "查询成功", largenScreenService.queryProduction(query)); |
| | | } |
| | | |
| | | |
| | | } |
New file |
| | |
| | | package com.mes.largenscreen.entity; |
| | | |
| | | import com.alibaba.excel.annotation.ExcelProperty; |
| | | import com.mes.utils.excel.ExcelMerge; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @Author : zhoush |
| | | * @Date: 2025/3/12 14:30 |
| | | * @Description: |
| | | */ |
| | | @Data |
| | | public class DailyProductionData { |
| | | |
| | | @ExcelProperty(value = "日期") |
| | | @ExcelMerge(merge = true, isPrimaryKey = true) |
| | | private String productDate; |
| | | |
| | | @ExcelProperty(value = {"切割", "开始时间"}) |
| | | @ExcelMerge(merge = true) |
| | | private String edgBeginTime; |
| | | @ExcelProperty(value = {"切割", "结束时间"}) |
| | | @ExcelMerge(merge = true) |
| | | private String edgEndTime; |
| | | @ExcelProperty(value = {"切割", "总耗时"}) |
| | | @ExcelMerge(merge = true) |
| | | private String edgTimeTotal; |
| | | @ExcelProperty(value = {"切割", "空闲时长"}) |
| | | @ExcelMerge(merge = true) |
| | | private String edgTimeFree; |
| | | @ExcelProperty(value = {"切割", "工作时长"}) |
| | | @ExcelMerge(merge = true) |
| | | private String edgTimeDiff; |
| | | @ExcelProperty(value = {"切割", "总片数"}) |
| | | @ExcelMerge(merge = true) |
| | | private String countOut; |
| | | @ExcelProperty(value = {"切割", "总面积(m^2)"}) |
| | | @ExcelMerge(merge = true) |
| | | private String totalAreaOut; |
| | | |
| | | |
| | | @ExcelProperty(value = {"钢化", "开始时间"}) |
| | | @ExcelMerge(merge = true) |
| | | private String bigBeginTime; |
| | | @ExcelProperty(value = {"钢化", "结束时间"}) |
| | | @ExcelMerge(merge = true) |
| | | private String bigEndTime; |
| | | @ExcelProperty(value = {"钢化", "总耗时"}) |
| | | @ExcelMerge(merge = true) |
| | | private String bigTimeTotal; |
| | | @ExcelMerge(merge = true) |
| | | @ExcelProperty(value = {"钢化", "空闲时长"}) |
| | | private String bigTimeFree; |
| | | @ExcelProperty(value = {"钢化", "工作时长"}) |
| | | @ExcelMerge(merge = true) |
| | | private String bigTimeDiff; |
| | | @ExcelProperty(value = {"钢化", "总炉数"}) |
| | | @ExcelMerge(merge = true) |
| | | private String temperingLayoutCount; |
| | | @ExcelProperty(value = {"钢化", "总片数"}) |
| | | @ExcelMerge(merge = true) |
| | | private String temperingGlassCount; |
| | | @ExcelProperty(value = {"钢化", "总面积(m^2)"}) |
| | | @ExcelMerge(merge = true) |
| | | private String temperingArea; |
| | | |
| | | |
| | | @ExcelProperty(value = {"中空", "开始时间"}) |
| | | @ExcelMerge(merge = true) |
| | | private String hollowBeginTime; |
| | | @ExcelProperty(value = {"中空", "结束时间"}) |
| | | @ExcelMerge(merge = true) |
| | | private String hollowEndTime; |
| | | @ExcelProperty(value = {"中空", "总耗时"}) |
| | | @ExcelMerge(merge = true) |
| | | private String hollowTimeTotal; |
| | | @ExcelProperty(value = {"中空", "空闲时长"}) |
| | | @ExcelMerge(merge = true) |
| | | private String hollowTimeFree; |
| | | @ExcelProperty(value = {"中空", "工作时长"}) |
| | | @ExcelMerge(merge = true) |
| | | private String hollowTimeDiff; |
| | | @ExcelProperty(value = {"中空", "总片数"}) |
| | | @ExcelMerge(merge = true) |
| | | private String hollowGlassCount; |
| | | @ExcelProperty(value = {"中空", "总面积(m^2)"}) |
| | | @ExcelMerge(merge = true) |
| | | private String hollowArea; |
| | | |
| | | @ExcelProperty(value = {"工程信息", "工程号"}) |
| | | private String engineerId; |
| | | @ExcelProperty(value = {"工程信息", "总片量"}) |
| | | private String glassTotal; |
| | | @ExcelProperty(value = {"工程信息", "总面积(m^2)"}) |
| | | private String glassTotalArea; |
| | | |
| | | |
| | | } |
New file |
| | |
| | | package com.mes.largenscreen.entity; |
| | | |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @Author : zhoush |
| | | * @Date: 2025/3/12 14:30 |
| | | * @Description: |
| | | */ |
| | | @Data |
| | | public class RunTime { |
| | | |
| | | @ApiModelProperty(value = "停机开始时间") |
| | | private String firstTimestamp; |
| | | |
| | | @ApiModelProperty(value = "停机结束时间") |
| | | private String secondTimestamp; |
| | | |
| | | @ApiModelProperty(value = "时间差") |
| | | private String diffMinutes; |
| | | |
| | | @ApiModelProperty(value = "开始时间") |
| | | private String startTimestamp; |
| | | |
| | | @ApiModelProperty(value = "结束时间按") |
| | | private String endTimestamp; |
| | | |
| | | } |
| | |
| | | package com.mes.largenscreen.mapper; |
| | | |
| | | import com.mes.largenscreen.entity.DailyProductionData; |
| | | import com.mes.largenscreen.entity.DailyProductionVO; |
| | | |
| | | import java.util.List; |
| | |
| | | public interface LargenScreenMapper { |
| | | |
| | | List<DailyProductionVO> queryDailyProduction(String beginDate, String endDate); |
| | | |
| | | List<DailyProductionData> exportDailyProduction(String beginDate, String endDate); |
| | | } |
| | |
| | | package com.mes.largenscreen.service; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.mes.largenscreen.entity.DailyProductionData; |
| | | import com.mes.largenscreen.entity.DailyProductionVO; |
| | | import com.mes.largenscreen.entity.DateRequest; |
| | | import com.mes.largenscreen.entity.PieChartVO; |
| | | |
| | | import java.util.List; |
| | | |
| | |
| | | |
| | | List<DailyProductionVO> querySameDayProduction(DateRequest query); |
| | | |
| | | void exportDailyProduction(); |
| | | |
| | | List<DailyProductionData> queryProduction(DateRequest query); |
| | | } |
| | |
| | | package com.mes.largenscreen.service.impl; |
| | | |
| | | import com.alibaba.excel.EasyExcel; |
| | | import com.alibaba.excel.support.ExcelTypeEnum; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.mes.largenscreen.entity.DailyProductionData; |
| | | import com.mes.largenscreen.entity.DailyProductionVO; |
| | | import com.mes.largenscreen.entity.DateRequest; |
| | | import com.mes.largenscreen.mapper.LargenScreenMapper; |
| | | import com.mes.largenscreen.service.LargenScreenService; |
| | | import com.mes.tools.PageUtil; |
| | | import com.mes.utils.excel.CustomCellWriteWidthConfig; |
| | | import com.mes.utils.excel.ExcelMergeStrategy; |
| | | import com.mes.utils.excel.ExcelUtil; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import javax.annotation.Resource; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.io.IOException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Calendar; |
| | | import java.util.Date; |
| | |
| | | |
| | | @Resource |
| | | private LargenScreenMapper largenScreenMapper; |
| | | @Resource |
| | | private HttpServletResponse response; |
| | | |
| | | @Override |
| | | public Page<DailyProductionVO> queryDailyProduction(DateRequest query) { |
| | |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | public void exportDailyProduction() { |
| | | try { |
| | | |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); |
| | | Calendar calendar = Calendar.getInstance(); |
| | | calendar.add(Calendar.DAY_OF_MONTH, -10); |
| | | |
| | | ExcelUtil.setExcelResponseProp(response, "MES试生产记录" + sdf.format(new Date())); |
| | | List<DailyProductionData> providerList = largenScreenMapper.exportDailyProduction(sdf.format(calendar.getTime()), sdf.format(new Date())); |
| | | EasyExcel.write(response.getOutputStream()) |
| | | .head(DailyProductionData.class) |
| | | .registerWriteHandler(new ExcelMergeStrategy(DailyProductionData.class)) |
| | | .registerWriteHandler(new CustomCellWriteWidthConfig()) |
| | | .excelType(ExcelTypeEnum.XLSX).sheet("生产记录").doWrite(providerList); |
| | | } catch (IOException e) { |
| | | throw new RuntimeException(e); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public List<DailyProductionData> queryProduction(DateRequest query) { |
| | | if (StringUtils.isEmpty(query.getBeginDate())) { |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); |
| | | Calendar calendar = Calendar.getInstance(); |
| | | calendar.add(Calendar.DAY_OF_MONTH, -6); |
| | | query.setBeginDate(sdf.format(calendar.getTime())); |
| | | query.setEndDate(sdf.format(new Date())); |
| | | } |
| | | return largenScreenMapper.exportDailyProduction(query.getBeginDate(), query.getEndDate()); |
| | | |
| | | |
| | | } |
| | | |
| | | |
| | | } |
New file |
| | |
| | | package com.mes.utils.excel; |
| | | |
| | | import com.alibaba.excel.write.handler.CellWriteHandler; |
| | | import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; |
| | | import org.apache.poi.ss.usermodel.*; |
| | | import org.apache.poi.xssf.usermodel.XSSFFont; |
| | | |
| | | /** |
| | | * @Author : zhoush |
| | | * @Date: 2025/5/12 20:32 |
| | | * @Description: |
| | | */ |
| | | public class CustomCellStyleHandler implements CellWriteHandler { |
| | | |
| | | @Override |
| | | public void afterCellDispose(CellWriteHandlerContext context) { |
| | | // 获取单元格 |
| | | Cell cell = context.getCell(); |
| | | CellStyle cellStyle = cell.getCellStyle(); |
| | | |
| | | // 设置水平和垂直居中 |
| | | cellStyle.setAlignment(HorizontalAlignment.CENTER); |
| | | cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); |
| | | |
| | | cellStyle.setFont(new XSSFFont()); |
| | | |
| | | cellStyle.setBorderBottom(BorderStyle.THIN); |
| | | cellStyle.setBorderLeft(BorderStyle.THIN); |
| | | cellStyle.setBorderRight(BorderStyle.THIN); |
| | | cellStyle.setBorderTop(BorderStyle.THIN); |
| | | |
| | | // 设置样式回到单元格 |
| | | cell.setCellStyle(cellStyle); |
| | | } |
| | | } |
New file |
| | |
| | | package com.mes.utils.excel; |
| | | |
| | | import com.alibaba.excel.enums.CellDataTypeEnum; |
| | | import com.alibaba.excel.metadata.Head; |
| | | import com.alibaba.excel.metadata.data.CellData; |
| | | import com.alibaba.excel.metadata.data.WriteCellData; |
| | | import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; |
| | | import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy; |
| | | import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; |
| | | import org.apache.poi.ss.usermodel.Cell; |
| | | import org.apache.poi.ss.usermodel.Sheet; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * @Author : zhoush |
| | | * @Date: 2025/5/12 20:34 |
| | | * @Description: |
| | | */ |
| | | public class CustomCellWriteWidthConfig extends AbstractColumnWidthStyleStrategy { |
| | | |
| | | private final Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<>(); |
| | | |
| | | @Override |
| | | protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) { |
| | | boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList); |
| | | if (needSetWidth) { |
| | | Map<Integer, Integer> maxColumnWidthMap = CACHE.computeIfAbsent(writeSheetHolder.getSheetNo(), k -> new HashMap<>()); |
| | | Integer columnWidth = this.dataLength(cellDataList, cell, isHead); |
| | | if (columnWidth > 0) { |
| | | if (columnWidth > 10) { |
| | | columnWidth = 20; |
| | | } else { |
| | | columnWidth = 10; |
| | | } |
| | | Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex()); |
| | | if (maxColumnWidth == null || columnWidth > maxColumnWidth) { |
| | | maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth); |
| | | Sheet sheet = writeSheetHolder.getSheet(); |
| | | sheet.setColumnWidth(cell.getColumnIndex(), 256 * columnWidth + 184); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 计算长度 |
| | | * |
| | | * @param cellDataList |
| | | * @param cell |
| | | * @param isHead |
| | | * @return |
| | | */ |
| | | private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) { |
| | | if (isHead) { |
| | | return cell.getStringCellValue().getBytes().length; |
| | | } else { |
| | | CellData<?> cellData = cellDataList.get(0); |
| | | CellDataTypeEnum type = cellData.getType(); |
| | | if (type == null) { |
| | | return -1; |
| | | } else { |
| | | switch (type) { |
| | | case STRING: |
| | | // 换行符(数据需要提前解析好) |
| | | int index = cellData.getStringValue().indexOf("\n"); |
| | | return index != -1 ? |
| | | cellData.getStringValue().substring(0, index).getBytes().length + 1 : cellData.getStringValue().getBytes().length + 1; |
| | | case BOOLEAN: |
| | | return cellData.getBooleanValue().toString().getBytes().length; |
| | | case NUMBER: |
| | | return cellData.getNumberValue().toString().getBytes().length; |
| | | default: |
| | | return -1; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package com.mes.utils.excel; |
| | | |
| | | import java.lang.annotation.*; |
| | | |
| | | /** |
| | | * @Author : zhoush |
| | | * @Date: 2025/5/12 16:51 |
| | | * @Description: |
| | | */ |
| | | @Target({ElementType.FIELD}) |
| | | @Retention(RetentionPolicy.RUNTIME) |
| | | @Documented |
| | | public @interface ExcelMerge { |
| | | /** |
| | | * 是否合并单元格 |
| | | * |
| | | * @return true || false |
| | | */ |
| | | boolean merge() default true; |
| | | |
| | | /** |
| | | * 是否为主键(即该字段相同的行合并) |
| | | * |
| | | * @return true || false |
| | | */ |
| | | boolean isPrimaryKey() default false; |
| | | } |
New file |
| | |
| | | package com.mes.utils.excel; |
| | | |
| | | import com.alibaba.excel.annotation.ExcelProperty; |
| | | import com.alibaba.excel.write.handler.RowWriteHandler; |
| | | import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; |
| | | import com.alibaba.excel.write.metadata.holder.WriteTableHolder; |
| | | import org.apache.poi.ss.usermodel.Cell; |
| | | import org.apache.poi.ss.usermodel.Row; |
| | | import org.apache.poi.ss.usermodel.Sheet; |
| | | import org.apache.poi.ss.util.CellRangeAddress; |
| | | |
| | | import java.lang.reflect.Field; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @Author : zhoush |
| | | * @Date: 2025/5/12 16:55 |
| | | * @Description: |
| | | */ |
| | | public class ExcelMergeStrategy implements RowWriteHandler { |
| | | |
| | | /** |
| | | * 主键下标 |
| | | */ |
| | | private Integer primaryKeyIndex; |
| | | |
| | | /** |
| | | * 需要合并的列的下标集合 |
| | | */ |
| | | private final List<Integer> mergeColumnIndexList = new ArrayList<>(); |
| | | |
| | | /** |
| | | * 数据类型 |
| | | */ |
| | | private final Class<?> elementType; |
| | | |
| | | public ExcelMergeStrategy(Class<?> elementType) { |
| | | this.elementType = elementType; |
| | | } |
| | | |
| | | @Override |
| | | public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) { |
| | | // 判断是否为标题 |
| | | if (isHead) { |
| | | return; |
| | | } |
| | | // 获取当前工作表 |
| | | Sheet sheet = writeSheetHolder.getSheet(); |
| | | // 初始化主键下标和需要合并字段的下标 |
| | | if (primaryKeyIndex == null) { |
| | | this.initPrimaryIndexAndMergeIndex(writeSheetHolder); |
| | | } |
| | | // 判断是否需要和上一行进行合并 |
| | | // 不能和标题合并,只能数据行之间合并 |
| | | if (row.getRowNum() <= 1) { |
| | | return; |
| | | } |
| | | // 获取上一行数据 |
| | | Row lastRow = sheet.getRow(row.getRowNum() - 1); |
| | | // 将本行和上一行是同一类型的数据(通过主键字段进行判断),则需要合并 |
| | | if (lastRow.getCell(primaryKeyIndex).getStringCellValue().equalsIgnoreCase(row.getCell(primaryKeyIndex).getStringCellValue())) { |
| | | for (Integer mergeIndex : mergeColumnIndexList) { |
| | | CellRangeAddress cellRangeAddress = new CellRangeAddress(row.getRowNum() - 1, row.getRowNum(), mergeIndex, mergeIndex); |
| | | sheet.addMergedRegionUnsafe(cellRangeAddress); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 初始化主键下标和需要合并字段的下标 |
| | | * |
| | | * @param writeSheetHolder WriteSheetHolder |
| | | */ |
| | | private void initPrimaryIndexAndMergeIndex(WriteSheetHolder writeSheetHolder) { |
| | | // 获取当前工作表 |
| | | Sheet sheet = writeSheetHolder.getSheet(); |
| | | // 获取标题行 |
| | | Row titleRow = sheet.getRow(0); |
| | | // 获取所有属性字段 |
| | | Field[] fields = this.elementType.getDeclaredFields(); |
| | | // 遍历所有字段 |
| | | for (Field field : fields) { |
| | | // 获取@ExcelProperty注解,用于获取该字段对应列的下标 |
| | | ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); |
| | | // 判断是否为空 |
| | | if (null == excelProperty) { |
| | | continue; |
| | | } |
| | | // 获取自定义注解,用于合并单元格 |
| | | ExcelMerge excelMerge = field.getAnnotation(ExcelMerge.class); |
| | | // 判断是否需要合并 |
| | | if (null == excelMerge) { |
| | | continue; |
| | | } |
| | | for (int i = 0; i < fields.length; i++) { |
| | | Cell cell = titleRow.getCell(i); |
| | | if (null == cell) { |
| | | continue; |
| | | } |
| | | // 将字段和表头匹配上 |
| | | if (excelProperty.value()[0].equalsIgnoreCase(cell.getStringCellValue())) { |
| | | if (excelMerge.isPrimaryKey()) { |
| | | primaryKeyIndex = i; |
| | | } |
| | | if (excelMerge.merge()) { |
| | | mergeColumnIndexList.add(i); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 没有指定主键,则异常 |
| | | if (null == this.primaryKeyIndex) { |
| | | throw new IllegalStateException("使用@ExcelMerge注解必须指定主键"); |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.mes.utils.excel; |
| | | |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.io.UnsupportedEncodingException; |
| | | import java.net.URLEncoder; |
| | | |
| | | /** |
| | | * @Author : zhoush |
| | | * @Date: 2025/5/12 17:03 |
| | | * @Description: |
| | | */ |
| | | public class ExcelUtil { |
| | | public static void setExcelResponseProp(HttpServletResponse response, String rawFileName) throws UnsupportedEncodingException { |
| | | response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); |
| | | response.setCharacterEncoding("utf-8"); |
| | | String fileName = URLEncoder.encode(rawFileName, "UTF-8").replaceAll("\\+", "%20"); |
| | | response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); |
| | | } |
| | | } |
| | |
| | | t6.product_date |
| | | order by t.date |
| | | </select> |
| | | <select id="exportDailyProduction" resultType="com.mes.largenscreen.entity.DailyProductionData"> |
| | | with edg_storage_history_temp as ( |
| | | select t.glass_id_in as glass_id, t.create_time, STR_TO_DATE(t.create_time, '%Y-%m-%d') as product_date |
| | | from edg_storage_device_task_history t |
| | | where t.task_type in |
| | | (1, 3) |
| | | and STR_TO_DATE(t.create_time, '%Y-%m-%d') BETWEEN #{beginDate} and #{endDate} |
| | | ) |
| | | , big_storage_cage_history_temp as ( |
| | | select t.glass_id, t.create_time, STR_TO_DATE(t.create_time, '%Y-%m-%d') as product_date |
| | | from big_storage_cage_history_task t |
| | | where t.task_type = 2 |
| | | and STR_TO_DATE(t.create_time, '%Y-%m-%d') BETWEEN #{beginDate} and #{endDate} |
| | | ) |
| | | , hollow_big_storage_cage_history_temp as ( |
| | | select t.glass_id, t.create_time, STR_TO_DATE(t.create_time, '%Y-%m-%d') as product_date |
| | | from hollow_big_storage_cage_history_task t |
| | | where t.task_type = 5 |
| | | and STR_TO_DATE(t.create_time, '%Y-%m-%d') BETWEEN #{beginDate} and #{endDate} |
| | | ) |
| | | , engineer_temp as ( |
| | | select STR_TO_DATE(create_time, '%Y-%m-%d') as product_date, engineer_id, glass_total, glass_total_area |
| | | from engineering |
| | | where STR_TO_DATE(create_time, '%Y-%m-%d') BETWEEN #{beginDate} and #{endDate} |
| | | ) |
| | | , edg_temp as ( |
| | | select t.product_date, |
| | | count(t.glass_id) as |
| | | count_out, |
| | | round(sum(t1.width * t1.height) / 1000000, 1) as total_area_out |
| | | from edg_storage_history_temp t |
| | | inner join glass_info t1 on t.glass_id = t1.glass_id |
| | | group by product_date |
| | | ) |
| | | , edg_time_one as ( |
| | | select product_date, |
| | | min(create_time) as edg_begin_time, |
| | | max(create_time) as edg_end_time, |
| | | TIMESTAMPDIFF(MINUTE, min(create_time), max(create_time)) AS edg_time_total |
| | | from edg_storage_history_temp |
| | | group by product_date |
| | | ) |
| | | , edg_time_two as ( |
| | | SELECT t1.product_date, |
| | | sum(TIMESTAMPDIFF(MINUTE, t1.create_time, t2.create_time)) AS edg_time_free |
| | | FROM (SELECT product_date, |
| | | create_time, |
| | | LEAD(create_time) OVER (ORDER BY create_time) AS next_timestamp |
| | | FROM edg_storage_history_temp |
| | | ) t1 |
| | | JOIN |
| | | (SELECT create_time, |
| | | LEAD(create_time) OVER (ORDER BY create_time) AS next_timestamp |
| | | FROM edg_storage_history_temp) t2 ON t1.next_timestamp = t2.create_time |
| | | WHERE TIMESTAMPDIFF(MINUTE, t1.create_time, t2.create_time) between 6 and 500 |
| | | group by t1.product_date |
| | | ) |
| | | , big_storage_temp as ( |
| | | select t.product_date, |
| | | count(distinct t1.engineer_id, t1.tempering_layout_id) as tempering_layout_count, |
| | | count(distinct t1.glass_id) as tempering_glass_count, |
| | | round(sum(t1.width * t1.height) / 1000000, 1) as tempering_area |
| | | from big_storage_cage_history_temp t |
| | | inner join glass_info t1 |
| | | on t.glass_id = t1.glass_id |
| | | group by t.product_date |
| | | ) |
| | | , big_storage_time_base as ( |
| | | select distinct product_date, create_time from big_storage_cage_history_temp |
| | | ) |
| | | , big_storage_time_one as (select product_date, |
| | | min(create_time) as big_begin_time, |
| | | max(create_time) as big_end_time, |
| | | TIMESTAMPDIFF(MINUTE, min(create_time), max(create_time)) AS big_time_total |
| | | from big_storage_time_base |
| | | group by product_date) |
| | | , big_storage_time_two as ( |
| | | SELECT t1.product_date, |
| | | IFNULL(sum(TIMESTAMPDIFF(MINUTE, t1.create_time, t2.create_time)), 0) AS big_time_free |
| | | FROM (SELECT product_date, |
| | | create_time, |
| | | LEAD(create_time) OVER (ORDER BY create_time) AS next_timestamp |
| | | FROM big_storage_time_base) t1 |
| | | JOIN |
| | | (SELECT create_time, |
| | | LEAD(create_time) OVER (ORDER BY create_time) AS next_timestamp |
| | | FROM big_storage_time_base) t2 ON t1.next_timestamp = t2.create_time |
| | | WHERE TIMESTAMPDIFF(MINUTE, t1.create_time, t2.create_time) between 11 and 500 |
| | | group by t1.product_date |
| | | ) |
| | | , hollow_big_storage_temp as ( |
| | | select t.product_date, |
| | | count(distinct t1.glass_id) as hollow_glass_count, |
| | | round(sum(t1.width * t1.height) / 1000000, 1) as hollow_area |
| | | from hollow_big_storage_cage_history_temp t |
| | | inner join glass_info t1 |
| | | on t.glass_id = t1.glass_id |
| | | group by t.product_date |
| | | ) |
| | | , hollow_big_storage_time_base as ( |
| | | select distinct product_date, create_time from hollow_big_storage_cage_history_temp |
| | | ) |
| | | , hollow_big_storage_time_one as (select product_date, |
| | | min(create_time) as hollow_begin_time, |
| | | max(create_time) as hollow_end_time, |
| | | TIMESTAMPDIFF(MINUTE, min(create_time), max(create_time)) AS hollow_time_total |
| | | from hollow_big_storage_time_base |
| | | group by product_date) |
| | | , hollow_big_storage_time_two as ( |
| | | SELECT t1.product_date, |
| | | IFNULL(sum(TIMESTAMPDIFF(MINUTE, t1.create_time, t2.create_time)), 0) AS hollow_time_free |
| | | FROM (SELECT product_date, |
| | | create_time, |
| | | LEAD(create_time) OVER (ORDER BY create_time) AS next_timestamp |
| | | FROM hollow_big_storage_time_base) t1 |
| | | JOIN |
| | | (SELECT create_time, |
| | | LEAD(create_time) OVER (ORDER BY create_time) AS next_timestamp |
| | | FROM hollow_big_storage_time_base) t2 ON t1.next_timestamp = t2.create_time |
| | | WHERE TIMESTAMPDIFF(MINUTE, t1.create_time, t2.create_time) between 11 and 500 |
| | | group by t1.product_date |
| | | ) |
| | | , result as ( |
| | | select t.count_out, |
| | | t.total_area_out, |
| | | t1.*, |
| | | t2.edg_time_free, |
| | | t1.edg_time_total - t2.edg_time_free as edg_time_diff, |
| | | t3.tempering_layout_count, |
| | | t3.tempering_glass_count, |
| | | t3.tempering_area, |
| | | t4.big_begin_time, |
| | | t4.big_end_time, |
| | | t4.big_time_total, |
| | | t5.big_time_free, |
| | | t4.big_time_total - t5.big_time_free as big_time_diff, |
| | | t6.hollow_glass_count, |
| | | t6.hollow_area, |
| | | t7.hollow_begin_time, |
| | | t7.hollow_end_time, |
| | | t7.hollow_time_total, |
| | | t8.hollow_time_free, |
| | | t7.hollow_time_total - t8.hollow_time_free as hollow_time_diff, |
| | | t9.engineer_id, |
| | | t9.glass_total, |
| | | t9.glass_total_area |
| | | from edg_temp t |
| | | LEFT JOIN edg_time_one t1 on t.product_date = t1.product_date |
| | | LEFT JOIN edg_time_two t2 on t.product_date = t2.product_date |
| | | LEFT JOIN big_storage_temp t3 on t.product_date = t3.product_date |
| | | LEFT JOIN big_storage_time_one t4 on t.product_date = t4.product_date |
| | | LEFT JOIN big_storage_time_two t5 on t.product_date = t5.product_date |
| | | LEFT JOIN hollow_big_storage_temp t6 on t.product_date = t6.product_date |
| | | LEFT JOIN hollow_big_storage_time_one t7 on t.product_date = t7.product_date |
| | | LEFT JOIN hollow_big_storage_time_two t8 on t.product_date = t8.product_date |
| | | LEFT JOIN engineer_temp t9 on t.product_date = t9.product_date |
| | | ) |
| | | select * |
| | | from result |
| | | order by product_date |
| | | </select> |
| | | |
| | | |
| | | </mapper> |
| | |
| | | //获取对应的设备状态信息 |
| | | ReadWriteEntity oneOutStateEntity = miloService.readFromOpcUa("WL1.WL1.slotState"); |
| | | ReadWriteEntity twoOutStateEntity = miloService.readFromOpcUa("WL2.WL2.slotState"); |
| | | ReadWriteEntity d06OutStateEntity = miloService.readFromOpcUa("WL2.WL2.d06SlotState"); |
| | | //状态有3中情况:0空闲 1忙碌 2禁用 |
| | | String oneOutState = "2"; |
| | | String twoOutState = "2"; |
| | | String oneOutState = Const.OUT_DISABLE; |
| | | String twoOutState = Const.OUT_DISABLE; |
| | | String d06OutState = Const.OUT_DISABLE; |
| | | if (null != oneOutStateEntity && null != oneOutStateEntity.getValue()) { |
| | | oneOutState = oneOutStateEntity.getValue().toString(); |
| | | } |
| | | if (null != twoOutStateEntity && null != twoOutStateEntity.getValue()) { |
| | | twoOutState = twoOutStateEntity.getValue().toString(); |
| | | } |
| | | |
| | | if ((cellFlag == 2 && Const.OUT_BUSY.equals(oneOutState) && Const.OUT_BUSY.equals(twoOutState)) || (Const.OUT_DISABLE.equals(oneOutState) && Const.OUT_DISABLE.equals(twoOutState))) { |
| | | //两条线都为禁用则不出玻璃 |
| | | if (Const.OUT_DISABLE.equals(oneOutState) && Const.OUT_DISABLE.equals(twoOutState)) { |
| | | log.info("A09、A10为{},{}非自动状态,无法出片", oneOutState, oneOutState); |
| | | return Boolean.FALSE; |
| | | } |
| | | if (null != d06OutStateEntity && null != d06OutStateEntity.getValue()) { |
| | | d06OutState = d06OutStateEntity.getValue().toString(); |
| | | } |
| | | //获取d06片台状态 |
| | | // 1:一对一的情况下不需要判断d06状态 |
| | | // 2:一号线一对多的情况下,获取二号线磨边前片台D07的状态,D07非禁用时,二号线将按照D06片台状态觉得出片 |
| | | // 3:二号线一对多的情况下,获取一号线磨边前片台C08的状态,C08非禁用时,一号线将按照D06片台状态觉得出片 |
| | | if (cellFlag == 2) { |
| | | if (deviceId == 1) { |
| | | twoOutState = Const.OUT_DISABLE.equals(twoOutState) ? Const.OUT_DISABLE : d06OutState; |
| | | } else { |
| | | oneOutState = Const.OUT_DISABLE.equals(oneOutState) ? Const.OUT_DISABLE : d06OutState; |
| | | } |
| | | if (Const.OUT_BUSY.equals(oneOutState) && Const.OUT_BUSY.equals(twoOutState)) { |
| | | log.info("A09、A10为{},{}非自动状态,无法出片", oneOutState, oneOutState); |
| | | return Boolean.FALSE; |
| | | } |
| | | } |
| | | |
| | | log.info("开始执行出片/直通任务,任务信息为:{},表名为:{},设备id:{},开始时间:{},一号线状态:{},二号线状态:{}", |
| | | task, tableName, deviceId, startDate, oneOutState, twoOutState); |
| | | //获取当前需要走那条线 |
| | |
| | | import com.mes.largenscreen.entity.DailyProductionVO; |
| | | import com.mes.largenscreen.entity.DateRequest; |
| | | import com.mes.largenscreen.entity.PieChartVO; |
| | | import com.mes.largenscreen.entity.RunTime; |
| | | import com.mes.largenscreen.service.LargenScreenService; |
| | | import com.mes.opctask.entity.EdgStorageDeviceTaskHistory; |
| | | import com.mes.opctask.service.EdgStorageDeviceTaskHistoryService; |
| | |
| | | @Scheduled(fixedDelay = 1000) |
| | | public void querySameDayProductionTask() { |
| | | JSONObject jsonObject = new JSONObject(); |
| | | List<DailyProductionVO> productionVO = largenScreenService.querySameDayProduction(new DateRequest()); |
| | | jsonObject.append("productionVO", productionVO); |
| | | |
| | | ArrayList<WebSocketServer> sendwServer = WebSocketServer.sessionMap.get("largenScreenProduction"); |
| | | if (sendwServer != null) { |
| | | List<DailyProductionVO> productionVO = largenScreenService.querySameDayProduction(new DateRequest()); |
| | | jsonObject.append("productionVO", productionVO); |
| | | for (WebSocketServer webserver : sendwServer) { |
| | | if (webserver != null) { |
| | | webserver.sendMessage(jsonObject.toString()); |
| | |
| | | } |
| | | } |
| | | |
| | | @Scheduled(fixedDelay = 1000) |
| | | @Scheduled(fixedDelay = 500) |
| | | public void largenScreen() { |
| | | JSONObject jsonObject = new JSONObject(); |
| | | //磨边信息 |
| | | |
| | | ArrayList<WebSocketServer> sendwServer = WebSocketServer.sessionMap.get("largenScreen"); |
| | | if (sendwServer != null) { |
| | | List<EdgGlassTaskInfo> edgOneTasks = edgGlassTaskInfoService.list( |
| | | new LambdaQueryWrapper<EdgGlassTaskInfo>() |
| | | .lt(EdgGlassTaskInfo::getState, 2) |
| | |
| | | jsonObject.append("engineeringTwo", engineeringTow); |
| | | List<PieChartVO> pieChartVOS = edgStorageCageDetailsService.queryPieChart(); |
| | | jsonObject.append("pieChartVOS", pieChartVOS); |
| | | ArrayList<WebSocketServer> sendwServer = WebSocketServer.sessionMap.get("largenScreen"); |
| | | if (sendwServer != null) { |
| | | List<RunTime> loadRunTimes = edgStorageDeviceTaskHistoryService.queryRunTimes(); |
| | | jsonObject.append("loadRunTimes", loadRunTimes); |
| | | for (WebSocketServer webserver : sendwServer) { |
| | | if (webserver != null) { |
| | | webserver.sendMessage(jsonObject.toString()); |
| | |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.mes.largenscreen.entity.DailyProductionVO; |
| | | import com.mes.largenscreen.entity.RunTime; |
| | | import com.mes.opctask.entity.EdgStorageDeviceTaskHistory; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * (EdgStorageDeviceTaskHistory)表数据库访问层 |
| | |
| | | public interface EdgStorageDeviceTaskHistoryMapper extends BaseMapper<EdgStorageDeviceTaskHistory> { |
| | | |
| | | DailyProductionVO queryEdgDailyProduction(@Param("beginDate") String beginDate, @Param("endDate") String endDate, @Param("deviceId") Integer deviceId); |
| | | |
| | | List<RunTime> queryRunTimes(); |
| | | } |
| | | |
| | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.mes.largenscreen.entity.DailyProductionVO; |
| | | import com.mes.largenscreen.entity.RunTime; |
| | | import com.mes.opctask.entity.EdgStorageDeviceTaskHistory; |
| | | import com.mes.opctask.entity.request.TaskHistoryRequest; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * (EdgStorageDeviceTaskHistory)表服务接口 |
| | |
| | | Page<EdgStorageDeviceTaskHistory> queryEdgStorageDeviceTaskHistory(TaskHistoryRequest request); |
| | | |
| | | DailyProductionVO queryEdgDailyProduction(TaskHistoryRequest request); |
| | | |
| | | List<RunTime> queryRunTimes(); |
| | | } |
| | | |
| | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.mes.largenscreen.entity.DailyProductionVO; |
| | | import com.mes.largenscreen.entity.RunTime; |
| | | import com.mes.opctask.entity.EdgStorageDeviceTaskHistory; |
| | | import com.mes.opctask.entity.request.TaskHistoryRequest; |
| | | import com.mes.opctask.mapper.EdgStorageDeviceTaskHistoryMapper; |
| | |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * (EdgStorageDeviceTaskHistory)表服务实现类 |
| | |
| | | } |
| | | return baseMapper.queryEdgDailyProduction(beginDate, endDate, request.getDeviceId()); |
| | | } |
| | | |
| | | @Override |
| | | public List<RunTime> queryRunTimes(){ |
| | | return baseMapper.queryRunTimes(); |
| | | } |
| | | } |
| | | |
| | |
| | | password: 123456 |
| | | kangaroohy: |
| | | milo: |
| | | enabled: false |
| | | enabled: true |
| | | primary: default |
| | | config: |
| | | default: |
| | | endpoint: opc.tcp://192.168.2.100:49320 |
| | | endpoint: opc.tcp://10.153.19.150:49320 |
| | | security-policy: basic256sha256 |
| | | username: admin |
| | | password: 1qaz2wsx3edc4rfv |
| | |
| | | <result column="hollow_total_area_out_two" property="hollowTotalAreaOutTwo"/> |
| | | </resultMap> |
| | | |
| | | |
| | | <select id="queryEdgDailyProduction" resultMap="baseMap"> |
| | | <select id="queryDailyProduction" resultMap="baseMap"> |
| | | SELECT |
| | | count( t.glass_id_out ) AS count_out_one, |
| | | round( sum( t1.width * t1.height )/ 1000000, 2 ) AS total_area_out_one |
| | |
| | | AND device_id = #{deviceId} |
| | | </if> |
| | | </select> |
| | | <select id="queryRunTimes" resultType="com.mes.largenscreen.entity.RunTime"> |
| | | with edg_storage_cage_history_task_temp as ( |
| | | select distinct create_time from edg_storage_device_task_history where task_type in (1,3) and |
| | | create_time BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 1 DAY) |
| | | ) |
| | | SELECT |
| | | t1.create_time AS first_timestamp, |
| | | t2.create_time AS second_timestamp, |
| | | TIMESTAMPDIFF(MINUTE, t1.create_time, t2.create_time) as diff_minutes |
| | | ,(select min(create_time) from edg_storage_cage_history_task_temp) as start_timestamp |
| | | ,(select max(create_time) from edg_storage_cage_history_task_temp) as end_timestamp |
| | | FROM |
| | | (SELECT |
| | | create_time, |
| | | LEAD(create_time) OVER (ORDER BY create_time) AS next_timestamp |
| | | FROM |
| | | edg_storage_cage_history_task_temp ) t1 |
| | | JOIN |
| | | (SELECT |
| | | create_time, |
| | | LEAD(create_time) OVER (ORDER BY create_time) AS next_timestamp |
| | | FROM |
| | | edg_storage_cage_history_task_temp ) t2 ON t1.next_timestamp = t2.create_time |
| | | |
| | | WHERE |
| | | TIMESTAMPDIFF(MINUTE, t1.create_time, t2.create_time) between 6 and 500 |
| | | GROUP BY t1.create_time,t2.create_time |
| | | ; |
| | | </select> |
| | | |
| | | |
| | | </mapper> |
| | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.mes.bigstoragecagetask.entity.BigStorageCageHistoryTask; |
| | | import com.mes.largenscreen.entity.DailyProductionVO; |
| | | import com.mes.largenscreen.entity.RunTime; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * (BigStorageCageHistoryTask)表数据库访问层 |
| | |
| | | public interface BigStorageCageHistoryTaskMapper extends BaseMapper<BigStorageCageHistoryTask> { |
| | | |
| | | DailyProductionVO queryBigDailyProduction(@Param("beginDate") String beginDate, @Param("endDate") String endDate); |
| | | |
| | | List<RunTime> queryRunTimes(); |
| | | } |
| | | |
| | |
| | | import com.mes.bigstoragecagetask.entity.BigStorageCageHistoryTask; |
| | | import com.mes.bigstoragecagetask.entity.request.BigStorageCageHistoryRequest; |
| | | import com.mes.largenscreen.entity.DailyProductionVO; |
| | | import com.mes.largenscreen.entity.RunTime; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * (BigStorageCageHistoryTask)表服务接口 |
| | |
| | | Page<BigStorageCageHistoryTask> queryBigStorageCageHistoryTask(BigStorageCageHistoryRequest request); |
| | | |
| | | DailyProductionVO queryBigDailyProduction(BigStorageCageHistoryRequest request); |
| | | |
| | | List<RunTime> queryRunTimes(); |
| | | } |
| | | |
| | |
| | | import com.mes.bigstoragecagetask.mapper.BigStorageCageHistoryTaskMapper; |
| | | import com.mes.bigstoragecagetask.service.BigStorageCageHistoryTaskService; |
| | | import com.mes.largenscreen.entity.DailyProductionVO; |
| | | import com.mes.largenscreen.entity.RunTime; |
| | | import com.mes.tools.DateUtil; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * (BigStorageCageHistoryTask)表服务实现类 |
| | |
| | | } |
| | | return baseMapper.queryBigDailyProduction(beginDate, endDate); |
| | | } |
| | | |
| | | @Override |
| | | public List<RunTime> queryRunTimes(){ |
| | | return baseMapper.queryRunTimes(); |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | import com.mes.bigstorage.entity.dto.BigStorageSummaryDTO; |
| | | import com.mes.bigstorage.service.BigStorageCageDetailsService; |
| | | import com.mes.bigstorage.service.BigStorageCageService; |
| | | import com.mes.bigstoragecagetask.entity.BigStorageCageHistoryTask; |
| | | import com.mes.bigstoragecagetask.entity.BigStorageCageTask; |
| | | import com.mes.bigstoragecagetask.entity.BigStorageTaskVO; |
| | | import com.mes.bigstoragecagetask.service.BigStorageCageHistoryTaskService; |
| | | import com.mes.bigstoragecagetask.service.BigStorageCageTaskService; |
| | | import com.mes.common.config.Const; |
| | | import com.mes.glassinfo.service.GlassInfoService; |
| | | import com.mes.largenscreen.entity.PieChartVO; |
| | | import com.mes.largenscreen.entity.RunTime; |
| | | import com.mes.temperingglass.entity.TemperingGlassInfo; |
| | | import com.mes.temperingglass.service.TemperingGlassInfoService; |
| | | import com.mes.tools.WebSocketServer; |
| | |
| | | private GlassInfoService glassInfoService; |
| | | @Resource |
| | | private BigStorageCageTaskService bigStorageCageTaskService; |
| | | @Resource |
| | | private BigStorageCageHistoryTaskService bigStorageCageHistoryTaskService; |
| | | |
| | | @Autowired(required = false) |
| | | MiloService miloService; |
| | |
| | | public void largenScreen() { |
| | | JSONObject jsonObject = new JSONObject(); |
| | | //理片笼使用情况 |
| | | ArrayList<WebSocketServer> sendwServer = WebSocketServer.sessionMap.get("largenScreen"); |
| | | if (sendwServer != null) { |
| | | List<Map<String, Object>> bigStorageCageUsage = bigStorageCageService.selectBigStorageCageUsage(); |
| | | jsonObject.append("bigStorageCageUsage", bigStorageCageUsage); |
| | | List<PieChartVO> pieChartVOS = bigStorageCageService.queryPieChart(); |
| | | jsonObject.append("pieChartVOS", pieChartVOS); |
| | | ArrayList<WebSocketServer> sendwServer = WebSocketServer.sessionMap.get("largenScreen"); |
| | | if (sendwServer != null) { |
| | | List<RunTime> tempRunTimes = bigStorageCageHistoryTaskService.queryRunTimes(); |
| | | jsonObject.append("tempRunTimes", tempRunTimes); |
| | | for (WebSocketServer webserver : sendwServer) { |
| | | if (webserver != null) { |
| | | webserver.sendMessage(jsonObject.toString()); |
| | |
| | | password: 123456 |
| | | kangaroohy: |
| | | milo: |
| | | enabled: true |
| | | enabled: false |
| | | primary: default |
| | | config: |
| | | default: |
| | |
| | | select * |
| | | from big_storage_in_temp |
| | | INNER join big_storage_out_temp on 1 = 1 |
| | | </select> |
| | | <select id="queryRunTimes" resultType="com.mes.largenscreen.entity.RunTime"> |
| | | with big_storage_cage_history_task_temp as ( |
| | | select distinct create_time |
| | | from big_storage_cage_history_task |
| | | where task_type = 2 |
| | | and create_time BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 1 DAY) |
| | | ) |
| | | SELECT t1.create_time AS first_timestamp, |
| | | t2.create_time AS second_timestamp, |
| | | TIMESTAMPDIFF(MINUTE, t1.create_time, t2.create_time) as diff_minutes |
| | | , |
| | | (select min(create_time) from big_storage_cage_history_task_temp) as start_timestamp |
| | | , |
| | | (select max(create_time) from big_storage_cage_history_task_temp) as end_timestamp |
| | | FROM (SELECT create_time, |
| | | LEAD(create_time) OVER (ORDER BY create_time) AS next_timestamp |
| | | FROM big_storage_cage_history_task_temp) t1 |
| | | JOIN |
| | | (SELECT create_time, |
| | | LEAD(create_time) OVER (ORDER BY create_time) AS next_timestamp |
| | | FROM big_storage_cage_history_task_temp) t2 ON t1.next_timestamp = t2.create_time |
| | | |
| | | WHERE TIMESTAMPDIFF(MINUTE, t1.create_time, t2.create_time) between 11 and 500 |
| | | GROUP BY t1.create_time, t2.create_time |
| | | ; |
| | | </select> |
| | | |
| | | </mapper> |
| | |
| | | import cn.hutool.core.collection.CollectionUtil; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.github.xingshuangs.iot.protocol.s7.serializer.S7Serializer; |
| | | import com.kangaroohy.milo.model.ReadWriteEntity; |
| | | import com.kangaroohy.milo.service.MiloService; |
| | | import com.mes.common.config.Const; |
| | |
| | | import com.mes.rawglassstation.service.RawGlassStorageStationService; |
| | | import com.mes.rawglasstask.entity.RawGlassStorageTask; |
| | | import com.mes.rawglasstask.service.RawGlassStorageTaskService; |
| | | import com.mes.s7.entity.S7Data; |
| | | import com.mes.s7.job.S7Job; |
| | | import com.mes.uppattenusage.entity.UpPattenUsage; |
| | | import com.mes.uppattenusage.entity.vo.UpPattenUsageVO; |
| | | import com.mes.uppattenusage.mapper.UpPattenUsageMapper; |
| | |
| | | @Autowired(required = false) |
| | | private MiloService miloService; |
| | | |
| | | @Autowired(required = false) |
| | | S7Serializer s7Serializer; |
| | | |
| | | |
| | | private static final String LOAD_GLASS_DEVICE_ONE_TASK = "load_glass_device_one_task"; |
| | | |
| | |
| | | |
| | | private static final List<Integer> LOAD_STATION_01 = Arrays.asList(101, 102); |
| | | private static final List<Integer> LOAD_STATION_02 = Arrays.asList(103, 104); |
| | | private static final List<Integer> LOAD_STATION_ALL = Arrays.asList(101, 102, 103, 104); |
| | | private static final Integer LEFTING_01 = 98; |
| | | private static final Integer LEFTING_02 = 99; |
| | | private static final Integer LOAD_GLASS_ONE_DEVICE = 5; |
| | |
| | | |
| | | @Scheduled(fixedDelay = 1000) |
| | | public void rawStorageTask() throws Exception { |
| | | ReadWriteEntity requestWord = miloService.readFromOpcUa("CC.CC.request"); |
| | | ReadWriteEntity confireWord = miloService.readFromOpcUa("CC.CC.confirmation"); |
| | | ReadWriteEntity reportWord = miloService.readFromOpcUa("CC.CC.reportWord"); |
| | | ReadWriteEntity taskWord = miloService.readFromOpcUa("CC.CC.taskWord"); |
| | | String requestValue = requestWord.getValue() + ""; |
| | | |
| | | String requestWord = S7Job.S7DataWL1.getRequest().toString(); |
| | | String confireWord = S7Job.S7DataWL1.getConfirmation().toString(); |
| | | String reportWord = S7Job.S7DataWL1.getReportWord().toString(); |
| | | String taskWord = S7Job.S7DataWL1.getTaskWord().toString(); |
| | | |
| | | // ReadWriteEntity requestWord = miloService.readFromOpcUa("CC.CC.request"); |
| | | // ReadWriteEntity confireWord = miloService.readFromOpcUa("CC.CC.confirmation"); |
| | | // ReadWriteEntity reportWord = miloService.readFromOpcUa("CC.CC.reportWord"); |
| | | // ReadWriteEntity taskWord = miloService.readFromOpcUa("CC.CC.taskWord"); |
| | | String requestValue = requestWord; |
| | | // String requestValue = requestWord.getValue() + ""; |
| | | if ("0".equals(requestValue)) { |
| | | if ("1".equals(confireWord.getValue() + "") && "0".equals(reportWord.getValue() + "")) { |
| | | List<ReadWriteEntity> list = new ArrayList<>(); |
| | | list.add(generateReadWriteEntity("CC.CC.confirmation", 0)); |
| | | miloService.writeToOpcWord(list); |
| | | if ("1".equals(confireWord) && "0".equals(reportWord)) { |
| | | // if ("1".equals(confireWord.getValue() + "") && "0".equals(reportWord.getValue() + "")) { |
| | | S7Data s7Data=new S7Data(); |
| | | s7Data.setConfirmation((short)0); |
| | | s7Serializer.write(s7Data); |
| | | // List<ReadWriteEntity> list = new ArrayList<>(); |
| | | // list.add(generateReadWriteEntity("CC.CC.confirmation", 0)); |
| | | // miloService.writeToOpcWord(list); |
| | | } |
| | | if ("1".equals(taskWord.getValue() + "")) { |
| | | List<ReadWriteEntity> list = new ArrayList<>(); |
| | | list.add(generateReadWriteEntity("CC.CC.taskWord", 0)); |
| | | list.add(generateReadWriteEntity("CC.CC.startSlot", 0)); |
| | | list.add(generateReadWriteEntity("CC.CC.endSlot", 0)); |
| | | miloService.writeToOpcWord(list); |
| | | if ("1".equals(taskWord)) { |
| | | // if ("1".equals(taskWord.getValue() + "")) { |
| | | S7Data s7Data=new S7Data(); |
| | | s7Data.setTaskWord((short)0); |
| | | s7Data.setStartSlot((short)0); |
| | | s7Data.setEndSlot((short)0); |
| | | s7Serializer.write(s7Data); |
| | | // List<ReadWriteEntity> list = new ArrayList<>(); |
| | | // list.add(generateReadWriteEntity("CC.CC.taskWord", 0)); |
| | | // list.add(generateReadWriteEntity("CC.CC.startSlot", 0)); |
| | | // list.add(generateReadWriteEntity("CC.CC.endSlot", 0)); |
| | | // miloService.writeToOpcWord(list); |
| | | } |
| | | return; |
| | | } |
| | |
| | | |
| | | @Scheduled(fixedDelay = 1000) |
| | | public void rawStorageInCar() throws Exception { |
| | | ReadWriteEntity entity = miloService.readFromOpcUa("CC.CC.inCar"); |
| | | String value = entity.getValue() + ""; |
| | | String value = S7Job.S7DataWL1.getInCar().toString(); |
| | | // ReadWriteEntity entity = miloService.readFromOpcUa("CC.CC.inCar"); |
| | | // String value = entity.getValue() + ""; |
| | | if (!"1".equals(value)) { |
| | | log.info("大车上没有架子"); |
| | | return; |
| | |
| | | |
| | | @Scheduled(fixedDelay = 1000) |
| | | public void rawStorageFinish() throws Exception { |
| | | ReadWriteEntity entity = miloService.readFromOpcUa("CC.CC.reportWord"); |
| | | String value = entity.getValue() + ""; |
| | | String value = S7Job.S7DataWL1.getReportWord().toString(); |
| | | // ReadWriteEntity entity = miloService.readFromOpcUa("CC.CC.reportWord"); |
| | | // String value = entity.getValue() + ""; |
| | | if ("0".equals(value)) { |
| | | log.info("当前任务未汇报,结束本次任务"); |
| | | return; |
| | |
| | | .set(RawGlassStorageTask::getTaskState, Const.RAW_GLASS_TASK_FAILURE)); |
| | | |
| | | } |
| | | List<ReadWriteEntity> list = new ArrayList<>(); |
| | | list.add(generateReadWriteEntity("CC.CC.confirmation", 1)); |
| | | miloService.writeToOpcWord(list); |
| | | S7Data s7Data=new S7Data(); |
| | | s7Data.setConfirmation((short)1); |
| | | s7Serializer.write(s7Data); |
| | | // List<ReadWriteEntity> list = new ArrayList<>(); |
| | | // list.add(generateReadWriteEntity("CC.CC.confirmation", 1)); |
| | | // miloService.writeToOpcWord(list); |
| | | } |
| | | |
| | | private boolean isHasRunningTask() { |
| | |
| | | if (CollectionUtils.isEmpty(loadStationList)) { |
| | | log.info("1号上片位生成调度任务"); |
| | | RawGlassStorageDetails rawDetails = getRawGlassStorageDetailsBySize(usageVOS.get(0), usageVOS.size()); |
| | | if(CollectionUtils.isEmpty(loadStationList)){ |
| | | log.info("仓库内无匹配的玻璃"); |
| | | return Boolean.FALSE; |
| | | } |
| | | rawGlassStorageDetailsService.generateTask(rawDetails.getSlot(), loadStation.get(0), rawDetails.getShelf(), rawDetails.getRemainQuantity(), Const.RAW_GLASS_TASK_TYPE_DISPATCH); |
| | | //结束调度任务 修改工位状态(起始位)状态为103 RAW_GLASS_STATE_RUNNING 任务完成后将状态改为101 目标位新增一条数据,架子号为(起始位) |
| | | rawGlassStorageDetailsService.update(new LambdaUpdateWrapper<RawGlassStorageDetails>() |
| | |
| | | .eq(RawGlassStorageDetails::getPatternWidth, usageVO.getWidth()) |
| | | .eq(RawGlassStorageDetails::getPatternHeight, usageVO.getHeight()) |
| | | .eq(RawGlassStorageDetails::getPatternThickness, usageVO.getThickness()) |
| | | .notIn(RawGlassStorageDetails::getSlot, LOAD_STATION_ALL) |
| | | .eq(RawGlassStorageDetails::getState, Const.GLASS_STATE_IN) |
| | | .orderByAsc(RawGlassStorageDetails::getRemainQuantity) |
| | | .last("order by abs(t.remain_quantity - " + remainQuantity + ") asc") |
| | |
| | | strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候回抛出异常,不启动会使用默认数据源. |
| | | datasource: |
| | | northGlassMes: |
| | | url: jdbc:mysql://127.0.0.1:3306/north_glass_mes?serverTimezone=GMT%2b8 |
| | | url: jdbc:mysql://127.0.0.1:3306/north_glass_mes?serverTimezone=GMT%2b8&allowMultiQueries=true |
| | | username: root |
| | | password: beibo.123/ |
| | | driver-class-name: com.mysql.cj.jdbc.Driver |
| | |
| | | password: beibo.123/ |
| | | driver-class-name: com.mysql.cj.jdbc.Driver |
| | | salve_northGlassMes: |
| | | url: jdbc:sqlserver://10.153.19.150:1433;databasename=north_glass_mes |
| | | url: jdbc:sqlserver://127.0.0.1:1433;databasename=north_glass_mes |
| | | username: sa |
| | | password: beibo.123/ |
| | | driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver |
| | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.mes.hollowtask.entity.HollowBigStorageCageHistoryTask; |
| | | import com.mes.largenscreen.entity.DailyProductionVO; |
| | | import com.mes.largenscreen.entity.RunTime; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * (HollowBigStorageCageHistoryTask)表数据库访问层 |
| | |
| | | public interface HollowBigStorageCageHistoryTaskMapper extends BaseMapper<HollowBigStorageCageHistoryTask> { |
| | | |
| | | DailyProductionVO queryHollowDailyProduction(@Param("beginDate") String beginDate, @Param("endDate") String endDate); |
| | | |
| | | List<RunTime> queryRunTimes(); |
| | | } |
| | | |
| | |
| | | import com.mes.hollowtask.entity.HollowBigStorageCageHistoryTask; |
| | | import com.mes.hollowtask.entity.request.HollowBigStorageCageHistoryRequest; |
| | | import com.mes.largenscreen.entity.DailyProductionVO; |
| | | import com.mes.largenscreen.entity.RunTime; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * (HollowBigStorageCageHistoryTask)表服务接口 |
| | |
| | | Page<HollowBigStorageCageHistoryTask> queryHollowBigStorageCageHistoryTask(HollowBigStorageCageHistoryRequest request); |
| | | |
| | | DailyProductionVO queryHollowDailyProduction(HollowBigStorageCageHistoryRequest request); |
| | | |
| | | List<RunTime> queryRunTimes(); |
| | | } |
| | | |
| | |
| | | import com.mes.hollowtask.mapper.HollowBigStorageCageHistoryTaskMapper; |
| | | import com.mes.hollowtask.service.HollowBigStorageCageHistoryTaskService; |
| | | import com.mes.largenscreen.entity.DailyProductionVO; |
| | | import com.mes.largenscreen.entity.RunTime; |
| | | import com.mes.tools.DateUtil; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * (HollowBigStorageCageHistoryTask)表服务实现类 |
| | |
| | | } |
| | | return baseMapper.queryHollowDailyProduction(beginDate, endDate); |
| | | } |
| | | |
| | | @Override |
| | | public List<RunTime> queryRunTimes(){ |
| | | return baseMapper.queryRunTimes(); |
| | | } |
| | | } |
| | | |
| | |
| | | import com.mes.hollow.service.HollowGlassRelationInfoService; |
| | | import com.mes.hollowqueue.entity.HollowGlassQueueInfo; |
| | | import com.mes.hollowqueue.service.HollowGlassQueueInfoService; |
| | | import com.mes.hollowtask.service.HollowBigStorageCageHistoryTaskService; |
| | | import com.mes.largenscreen.entity.PieChartVO; |
| | | import com.mes.largenscreen.entity.RunTime; |
| | | import com.mes.tools.WebSocketServer; |
| | | import com.mes.utils.RedisUtil; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | BigStorageCageTaskService bigStorageCageTaskService; |
| | | @Resource |
| | | HollowGlassRelationInfoService hollowGlassRelationInfoService; |
| | | @Resource |
| | | HollowBigStorageCageHistoryTaskService hollowBigStorageCageHistoryTaskService; |
| | | |
| | | @Autowired(required = false) |
| | | MiloService miloService; |
| | |
| | | //钢化饼图数据 |
| | | List<PieChartVO> pieChartVOS = hollowGlassOutRelationInfoService.queryPieChart(); |
| | | jsonObject.append("pieChartVOS", pieChartVOS); |
| | | List<RunTime> hollowRunTimes = hollowBigStorageCageHistoryTaskService.queryRunTimes(); |
| | | jsonObject.append("hollowRunTimes", hollowRunTimes); |
| | | ArrayList<WebSocketServer> sendwServer = WebSocketServer.sessionMap.get("largenScreen"); |
| | | if (sendwServer != null) { |
| | | for (WebSocketServer webserver : sendwServer) { |
| | |
| | | from hollow_out_one_temp |
| | | inner join hollow_out_two_temp on 1 = 1 |
| | | </select> |
| | | <select id="queryRunTimes" resultType="com.mes.largenscreen.entity.RunTime"> |
| | | with big_storage_cage_history_task_temp as ( |
| | | select distinct create_time from hollow_big_storage_cage_history_task where task_type = 5 and |
| | | create_time BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 1 DAY) |
| | | ) |
| | | SELECT |
| | | t1.create_time AS first_timestamp, |
| | | t2.create_time AS second_timestamp, |
| | | TIMESTAMPDIFF(MINUTE, t1.create_time, t2.create_time) as diff_minutes |
| | | FROM |
| | | (SELECT |
| | | create_time, |
| | | LEAD(create_time) OVER (ORDER BY create_time) AS next_timestamp |
| | | FROM |
| | | big_storage_cage_history_task_temp ) t1 |
| | | JOIN |
| | | (SELECT |
| | | create_time, |
| | | LEAD(create_time) OVER (ORDER BY create_time) AS next_timestamp |
| | | FROM |
| | | big_storage_cage_history_task_temp ) t2 ON t1.next_timestamp = t2.create_time |
| | | WHERE |
| | | TIMESTAMPDIFF(MINUTE, t1.create_time, t2.create_time) between 11 and 500; |
| | | </select> |
| | | </mapper> |