Compare commits

...

8 Commits

Author SHA1 Message Date
zwq
1a1a3a9129 更新 2025-10-10 14:41:45 +08:00
e91fcab0c8 Merge pull request 'projects/qhd-line-zhp' (#443) from projects/qhd-line-zhp into projects/qhd-line-test
Reviewed-on: #443
2025-10-10 13:24:37 +08:00
‘937886381’
1a0d786774 xiugai 2025-09-11 08:40:41 +08:00
‘937886381’
1b6e6702ed xiugai 2025-08-20 15:11:27 +08:00
‘937886381’
f23e025974 xiugai 2025-08-20 15:04:09 +08:00
‘937886381’
d77bebd375 修改 2025-07-04 09:14:09 +08:00
4225ff35ae Merge pull request '删除dist' (#437) from projects/qhd-line-zjl into projects/qhd-line-test
Reviewed-on: #437
2025-06-20 16:54:04 +08:00
dd85c50077 Merge pull request 'projects/qhd-line-zjl' (#436) from projects/qhd-line-zjl into projects/qhd-line-test
Reviewed-on: #436
2025-06-20 16:43:06 +08:00
10 changed files with 906 additions and 778 deletions

View File

@@ -21,7 +21,7 @@ VUE_CLI_BABEL_TRANSPILE_MODULES = true
VUE_APP_TENANT_ENABLE = true VUE_APP_TENANT_ENABLE = true
# 验证码的开关 # 验证码的开关
VUE_APP_CAPTCHA_ENABLE = true VUE_APP_CAPTCHA_ENABLE = true
# 文档的开关 # 文档的开关
VUE_APP_DOC_ENABLE = true VUE_APP_DOC_ENABLE = true

BIN
dist.rar Normal file

Binary file not shown.

View File

@@ -37,7 +37,9 @@ export default {
this.outputData.push(item.outputNum) this.outputData.push(item.outputNum)
this.goodRateData.push(item.goodRate) this.goodRateData.push(item.goodRate)
}) })
this.$nextTick(()=>{
this.initChart(); this.initChart();
})
} }
}, },
data() { data() {
@@ -281,4 +283,4 @@ export default {
.qhd-chart-tooltip * { .qhd-chart-tooltip * {
color: #fff !important; color: #fff !important;
} }
</style> </style>

View File

@@ -1,17 +1,20 @@
<template> <template>
<div ref='dataBoardBoxB' class='dataBoardBoxB'> <div ref="dataBoardBoxB" class="dataBoardBoxB">
<div id="dataBoardBox" class='dataBoardBox' style="width: 1920px;height: 1080px;" :style="{ transform: 'scale(' + scaleNum + ')' }"> <div
id="dataBoardBox"
class="dataBoardBox"
style="width: 1920px; height: 1080px"
:style="{ transform: 'scale(' + scaleNum + ')' }">
<DataBoardHeader <DataBoardHeader
:is-full-screen="isFullScreen" :is-full-screen="isFullScreen"
@screenfullChange="screenfullChange" @screenfullChange="screenfullChange" />
/> <LeftTop :dataObj="dataObj" />
<LeftTop :dataObj='dataObj'/> <LeftBottom :dataObj="dataObj" />
<LeftBottom :dataObj='dataObj'/> <CenterTop :scaleNum="scaleNum" :dataObj="dataObj" />
<CenterTop :scaleNum='scaleNum' :dataObj='dataObj'/> <CenterBottomL :dataObj="dataObj" />
<CenterBottomL :dataObj='dataObj'/> <CenterBottomR :dataObj="dataObj" />
<CenterBottomR :dataObj='dataObj'/> <RightTop :dataObj="dataObj" />
<RightTop :dataObj='dataObj'/> <RightBottom :dataObj="dataObj" />
<RightBottom :dataObj='dataObj'/>
</div> </div>
</div> </div>
</template> </template>
@@ -28,207 +31,249 @@ import RightBottom from './components/RightBottom.vue';
import { debounce } from '@/utils/debounce'; import { debounce } from '@/utils/debounce';
import screenfull from 'screenfull'; import screenfull from 'screenfull';
import { getAccessToken } from '@/utils/auth'; import { getAccessToken } from '@/utils/auth';
import store from "@/store"; import store from '@/store';
export default { export default {
name: 'DataBoard', name: 'DataBoard',
components: { DataBoardHeader,LeftTop,LeftBottom,CenterTop,CenterBottomL,CenterBottomR,RightTop,RightBottom }, components: {
DataBoardHeader,
LeftTop,
LeftBottom,
CenterTop,
CenterBottomL,
CenterBottomR,
RightTop,
RightBottom,
},
props: {}, props: {},
data() { data() {
return { return {
isFullScreen: false, isFullScreen: false,
scaleNum: 0.8, scaleNum: 0.8,
dataObj:{}, dataObj: {},
sseReader: null, // 保存流读取器 sseReader: null, // 保存流读取器
abortController: null, // 用于中止 fetch 请求 abortController: null, // 用于中止 fetch 请求
retryCount: 0, // 当前重试次数 retryCount: 0, // 当前重试次数
isDestroyed: false // 标记组件是否已销毁 isDestroyed: false, // 标记组件是否已销毁
}; };
}, },
created() { created() {
this.init() this.init();
}, },
mounted() { mounted() {
this.boxReset = debounce(() => { this.boxReset = debounce(() => {
this.resetSize() this.resetSize();
}, 300) }, 300);
this.boxReset() this.boxReset();
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
this.boxReset() this.boxReset();
}) });
this.getData() this.getData();
}, },
beforeDestroy() { beforeDestroy() {
this.closeSSE(); this.closeSSE();
}, },
destroyed() {
window.removeEventListener('resize', this.boxReset);
},
computed: { computed: {
sidebarOpened() { sidebarOpened() {
return this.$store.state.app.sidebar.opened return this.$store.state.app.sidebar.opened;
} },
}, },
watch: { watch: {
sidebarOpened(newVal, oldVal) { sidebarOpened(newVal, oldVal) {
this.boxReset() this.boxReset();
} },
}, },
methods: { methods: {
async getData() { async getData() {
let _this = this; let _this = this;
if (_this.isDestroyed) return; if (_this.isDestroyed) return;
const url = process.env.VUE_APP_BASE_API+'/admin-api/monitoring/message/subscribe/'+store.getters.userId+'-'+Date.now(); const url =
const token = getAccessToken() process.env.VUE_APP_BASE_API +
'/admin-api/monitoring/message/subscribe/' +
store.getters.userId +
'-' +
Date.now();
const token = getAccessToken();
const headers = new Headers({ const headers = new Headers({
'Authorization': `Bearer ${token}`, Authorization: `Bearer ${token}`,
'Content-Type': 'text/event-stream' 'Content-Type': 'text/event-stream',
}); });
try { try {
// 创建中止控制器 // 创建中止控制器
this.abortController = new AbortController(); this.abortController = new AbortController();
// 发起 fetch 请求(替换为你的接口地址) // 发起 fetch 请求(替换为你的接口地址)
const response = await fetch(url, { const response = await fetch(url, {
method: 'GET', method: 'GET',
headers: headers, headers: headers,
signal: _this.abortController.signal // 绑定中止信号 signal: _this.abortController.signal, // 绑定中止信号
}); });
// 获取流读取器 // 获取流读取器
_this.sseReader = response.body.getReader(); _this.sseReader = response.body.getReader();
const decoder = new TextDecoder(); const decoder = new TextDecoder();
let buffer = '';
let receivedBytes = 0;
// 持续读取流数据 // 持续读取流数据
while (true) { while (true) {
const { done, value } = await _this.sseReader.read(); const { done, value } = await _this.sseReader.read();
if (done) { if (done) {
console.log('SSE 连接正常关闭'); console.log('SSE 连接正常关闭');
_this.handleReconnect(); // 触发重连 _this.handleReconnect(); // 触发重连
break; break;
}
// 处理 SSE 事件数据
const data = decoder.decode(value);
console.log('收到消息:', data);
if (_this.isValidData(data)){
_this.upDateMsg(data);
} }
} // const data = decoder.decode(value);
} catch (error) { // console.log('收到消息:', data);
// 主动中止的请求不报错
if (error.name === 'AbortError') return; receivedBytes += value.length;
console.error('SSE 连接异常:', error); console.log(
_this.handleReconnect(); // 触发重连 `收到数据块: ${value.length} 字节, 累计: ${receivedBytes} 字节`
} );
const chunk = decoder.decode(value, { stream: true });
buffer += chunk;
// 处理完整的消息
const messages = buffer.split('\n\n'); // SSE 消息以双换行分隔
buffer = messages.pop() || ''; // 保留最后一个不完整的消息
for (const message of messages) {
if (_this.isValidData(message)) {
// console.log('完整 SSE 消息:', message);
_this.upDateMsg(message);
}
}
// 处理 SSE 事件数据
// const data = decoder.decode(value);
// console.log('收到消息:', data);
// if (_this.isValidData(data)){
// _this.upDateMsg(data);
// }
}
} catch (error) {
// 主动中止的请求不报错
if (error.name === 'AbortError') return;
console.error('SSE 连接异常:', error);
_this.handleReconnect(); // 触发重连
}
}, },
closeSSE() { closeSSE() {
this.isDestroyed = true; // 标记销毁 this.isDestroyed = true; // 标记销毁
if (this.abortController) { if (this.abortController) {
this.abortController.abort(); // 中止 fetch 请求 this.abortController.abort(); // 中止 fetch 请求
} }
if (this.sseReader) { if (this.sseReader) {
this.sseReader.cancel(); // 关闭流读取器 this.sseReader.cancel(); // 关闭流读取器
this.sseReader = null; this.sseReader = null;
} }
console.log('SSE 连接已强制关闭'); console.log('SSE 连接已强制关闭');
}, },
handleReconnect() { handleReconnect() {
if (this.isDestroyed) return; if (this.isDestroyed) return;
// 指数退避策略(最大重试 5 次) // 指数退避策略(最大重试 5 次)
const maxRetries = 5; const maxRetries = 5;
if (this.retryCount < maxRetries) { if (this.retryCount < maxRetries) {
const delay = Math.pow(2, this.retryCount) * 1000; const delay = Math.pow(2, this.retryCount) * 1000;
setTimeout(() => { setTimeout(() => {
this.retryCount++; this.retryCount++;
this.initSSE(); this.initSSE();
}, delay); }, delay);
} else { } else {
console.error('SSE 重连次数已达上限'); console.error('SSE 重连次数已达上限');
} }
}, },
isValidData (data) { isValidData(data) {
return data.trim().startsWith('data:{') && !data.includes('heartbeat'); return data.trim().startsWith('data:{') && !data.includes('heartbeat');
}, },
upDateMsg(data) { upDateMsg(data) {
const jsonStr = data.replace(/^data:/, '').trim(); const jsonStr = data.replace(/^data:/, '').trim();
console.log('jsonStr', jsonStr);
try { try {
const dataObj = JSON.parse(jsonStr); const dataObj = JSON.parse(jsonStr);
this.dataObj = dataObj this.dataObj = dataObj;
console.log('dataObj',this.dataObj) console.log('dataObj', this.dataObj);
} catch (e) { } catch (e) {
console.error('JSON 解析失败:', e); console.error('JSON 解析失败:', e);
} }
}, },
change() { change() {
this.isFullScreen = screenfull.isFullscreen this.isFullScreen = screenfull.isFullscreen;
}, },
init() { init() {
if (!screenfull.isEnabled) { if (!screenfull.isEnabled) {
this.$message({ this.$message({
message: 'you browser can not work', message: 'you browser can not work',
type: 'warning' type: 'warning',
}) });
return false return false;
} }
screenfull.on('change', this.change) screenfull.on('change', this.change);
}, },
destroy() { destroy() {
if (!screenfull.isEnabled) { if (!screenfull.isEnabled) {
this.$message({ this.$message({
message: 'you browser can not work', message: 'you browser can not work',
type: 'warning' type: 'warning',
}) });
return false return false;
} }
screenfull.off('change', this.change) screenfull.off('change', this.change);
}, },
// 全屏 // 全屏
screenfullChange() { screenfullChange() {
if (!screenfull.isEnabled) { if (!screenfull.isEnabled) {
this.$message({ this.$message({
message: 'you browser can not work', message: 'you browser can not work',
type: 'warning' type: 'warning',
}) });
return false return false;
} }
screenfull.toggle(this.$refs.dataBoardBoxB) screenfull.toggle(this.$refs.dataBoardBoxB);
}, },
resetSize() { resetSize() {
const dataBoardBox = document.getElementById('dataBoardBox') const dataBoardBox = document.getElementById('dataBoardBox');
const rw = parseFloat(window.innerWidth) const rw = parseFloat(window.innerWidth);
const rh = parseFloat(window.innerHeight) const rh = parseFloat(window.innerHeight);
const bw = parseFloat(dataBoardBox.style.width) const bw = parseFloat(dataBoardBox.style.width);
const bh = parseFloat(dataBoardBox.style.height) const bh = parseFloat(dataBoardBox.style.height);
let wx = 0 let wx = 0;
let hy = 0 let hy = 0;
if (screenfull.isFullscreen) { if (screenfull.isFullscreen) {
wx = rw / bw wx = rw / bw;
hy = rh / bh hy = rh / bh;
} else { } else {
if (this.$store.state.app.sidebar.opened) { if (this.$store.state.app.sidebar.opened) {
wx = (rw - 283) / bw wx = (rw - 283) / bw;
} else { } else {
wx = (rw - 88) / bw wx = (rw - 88) / bw;
} }
hy = (rh - 150) / bh hy = (rh - 150) / bh;
} }
this.scaleNum = wx < hy ? wx : hy this.scaleNum = wx < hy ? wx : hy;
}, },
}, },
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.dataBoardBoxB { .dataBoardBoxB {
width: 100%; width: 100%;
height: calc(100vh - 150px); height: calc(100vh - 150px);
position: relative; position: relative;
overflow: auto;
.dataBoardBox {
position: absolute;
transform-origin: 16px 8px;
font-size: 16px;
top: 0px;
left: 0px;
background: url('../../../assets/images/dataBoard/background.png') no-repeat;
background-size: cover;
background-position: 0 0;
overflow: auto; overflow: auto;
.dataBoardBox {
position: absolute;
transform-origin: 16px 8px;
font-size: 16px;
top: 0px;
left: 0px;
background: url('../../../assets/images/dataBoard/background.png') no-repeat;
background-size: cover;
background-position: 0 0;
overflow: auto;
}
} }
}
</style> </style>

View File

@@ -48,7 +48,7 @@ const tableProps = [
prop: 'reportDate', prop: 'reportDate',
label: '日期', label: '日期',
width: 130, width: 130,
fixed: true fixed: true,
}, },
{ {
prop: 'factoryName', prop: 'factoryName',
@@ -289,7 +289,11 @@ export default {
{ {
type: 'select', type: 'select',
label: '维度', label: '维度',
selectOptions: [ selectOptions: [
// {
// id: '0',
// name: '班',
// },
{ {
id: 1, id: 1,
name: '日', name: '日',
@@ -413,14 +417,18 @@ export default {
}, },
buttonClick(val) { buttonClick(val) {
switch (val.btnName) { switch (val.btnName) {
case 'search': case 'search':
this.listQuery.pageNo = 1; this.listQuery.pageNo = 1;
this.listQuery.pageSize = 10; this.listQuery.pageSize = 10;
this.listQuery.factoryId = val.factoryId || undefined; this.listQuery.factoryId = val.factoryId || undefined;
this.listQuery.lineId = val.lineId ? [val.lineId] : []; this.listQuery.lineId = val.lineId ? [val.lineId] : [];
this.listQuery.reportType = val.reportType || undefined; console.log(val.reportType);
this.listQuery.reportType = val.reportType ? Number(val.reportType) :undefined
this.listQuery.startTime = val.timeVal ? val.timeVal[0] : undefined; this.listQuery.startTime = val.timeVal ? val.timeVal[0] : undefined;
this.listQuery.endTime = val.timeVal ? val.timeVal[1] : undefined; this.listQuery.endTime = val.timeVal ? val.timeVal[1] : undefined;
console.log(this.listQuery.reportType);
this.getDataList(); this.getDataList();
break; break;
case 'export': case 'export':
@@ -433,10 +441,41 @@ export default {
// 获取数据列表 // 获取数据列表
getDataList() { getDataList() {
this.dataListLoading = true; this.dataListLoading = true;
const arr = ['日', '周', '月', '年']; const arr = [
{
id: 0,
name: '班',
},
{
id: 1,
name: '日',
},
{
id: 2,
name: '周',
},
{
id: 3,
name: '月',
},
{
id: 4,
name: '年',
},
]
const reportTypeNameMap = arr.reduce((map, item) => {
map[item.id] = item.name;
return map;
}, {});
this.urlOptions.getDataListURL(this.listQuery).then((response) => { this.urlOptions.getDataListURL(this.listQuery).then((response) => {
this.tableData = response.data.list.map((item, index) => { this.tableData = response.data.list.map((item, index) => {
item.reportType = arr[item.reportType - 1]; const typeId = item.reportType;
item.reportType = reportTypeNameMap[typeId] || '未知';
// item.reportType = arr[item.reportType - 1];
item.reportDate = item.reportDate;
item.originalLossNum = item.original?.lossNum; item.originalLossNum = item.original?.lossNum;
item.originalLossArea = item.original?.lossArea; item.originalLossArea = item.original?.lossArea;
item.edgeLossNum = item.edge?.lossNum; item.edgeLossNum = item.edge?.lossNum;

View File

@@ -305,7 +305,7 @@ export default {
this.startTime = new Date(payload.recordTime) this.startTime = new Date(payload.recordTime)
this.queryParams.lineId = payload.lineId || null; this.queryParams.lineId = payload.lineId || null;
this.queryParams.sectionId = payload.sectionId || null; this.queryParams.sectionId = payload.sectionId || null;
this.queryParams.equipmentId = payload.equipmentId || null; this.queryParams.equipmentId = payload.equipmentId ? Number(payload.equipmentId) : undefined
this.queryParams.recordTime = payload.recordTime this.queryParams.recordTime = payload.recordTime
? [ ? [
payload.recordTime, payload.recordTime,

View File

@@ -132,7 +132,7 @@ export default {
}, },
], ],
queryParams: { queryParams: {
equipmentId: null, equipmentId: undefined,
recordTime: null, recordTime: null,
}, },
graphList: [], graphList: [],
@@ -153,7 +153,7 @@ export default {
this.$nextTick(() => { this.$nextTick(() => {
this.$refs['search-bar'].formInline.recordTime = formattedDate; this.$refs['search-bar'].formInline.recordTime = formattedDate;
}); });
this.queryParams.equipmentId = Number(this.$route.query.eqid); this.queryParams.equipmentId = this.$route.query.eqid? Number(this.$route.query.eqid): undefined
this.startTime = new Date(formattedDate); this.startTime = new Date(formattedDate);
this.queryParams.recordTime = formattedDate this.queryParams.recordTime = formattedDate
? [ ? [
@@ -207,7 +207,7 @@ export default {
/** 重置查询条件 */ /** 重置查询条件 */
initQuery() { initQuery() {
this.queryParams.equipmentId = null; this.queryParams.equipmentId = undefined;
this.queryParams.recordTime = null; this.queryParams.recordTime = null;
}, },
@@ -251,7 +251,7 @@ export default {
}, },
handleSearchBarBtnClick({ btnName, ...payload }) { handleSearchBarBtnClick({ btnName, ...payload }) {
this.queryParams.equipmentId = Number(this.$route.query.eqid); this.queryParams.equipmentId = this.$route.query.eqid ? Number(this.$route.query.eqid) :undefined
switch (btnName) { switch (btnName) {
case 'search': case 'search':
if (!payload.recordTime || payload.recordTime.length <= 0) { if (!payload.recordTime || payload.recordTime.length <= 0) {

View File

@@ -248,12 +248,11 @@ export default {
this.lineArr = response.data; this.lineArr = response.data;
this.lineArr.forEach((item, index) => { this.lineArr.forEach((item, index) => {
const num = [ const num = [
'', '20001',
'1672847052717821953', '20002',
'1672847052717821954', '20003',
'1686260211054157825', '20004',
'1701892552632770122', '20005',
'1714562503683465331',
].indexOf(item.id); ].indexOf(item.id);
if (num > 0) { if (num > 0) {
item.num = num; item.num = num;

View File

@@ -260,14 +260,16 @@ export default {
// Create a new array by duplicating each item // Create a new array by duplicating each item
const duplicatedArr = []; const duplicatedArr = [];
this.lineArr.forEach((item, index) => { this.lineArr.forEach((item, index) => {
console.log(item,'item');
// Process original item // Process original item
const num = [ const num = [
'', '',
'1672847052717821953', 20001,
'1672847052717821954', 20002,
'1686260211054157825', 20003,
'1701892552632770122', 20004,
'1714562503683465331', 20005,
].indexOf(item.id); ].indexOf(item.id);
if (num > 0) { if (num > 0) {
// Original item with copy=1 // Original item with copy=1

File diff suppressed because it is too large Load Diff