20260808:大屏样式-时间范围便捷性调整

This commit is contained in:
zhugongping 2026-08-08 12:38:53 +08:00
parent 59a1f8dc61
commit b2d9fe05c7
4 changed files with 345 additions and 4 deletions

View File

@ -26,6 +26,14 @@ INSERT INTO `sys_menu` VALUES(3604, '项目删除', 3600, 4, '#', '', NULL, 1, 0
-- 子菜单:项目导出 -- 子菜单:项目导出
INSERT INTO `sys_menu` VALUES(3605, '项目导出', 3600, 5, '#', '', NULL, 1, 0, 'F', '0', '0', 'iot:project:export', '#', 'admin', NOW(), '', NULL, ''); INSERT INTO `sys_menu` VALUES(3605, '项目导出', 3600, 5, '#', '', NULL, 1, 0, 'F', '0', '0', 'iot:project:export', '#', 'admin', NOW(), '', NULL, '');
INSERT IGNORE INTO fastbee.sys_menu_translate (id, zh_cn, en_us) VALUES
(3372, '项目管理', 'Project management'),
(3373, '项目查询', 'Project query'),
(3374, '项目新增', 'Project add'),
(3375, '项目修改', 'Project edit'),
(3376, '项目删除', 'Project remove'),
(3377, '项目导出', 'Project export');
-- ======================================== -- ========================================
-- 菜单说明: -- 菜单说明:
-- menu_id: 菜单ID(3600为主菜单,3601-3605为按钮权限) -- menu_id: 菜单ID(3600为主菜单,3601-3605为按钮权限)

View File

@ -30,7 +30,7 @@ router.beforeEach((to, from, next) => {
store.dispatch('GenerateRoutes').then((accessRoutes) => { store.dispatch('GenerateRoutes').then((accessRoutes) => {
// 根据roles权限生成可访问的路由表 // 根据roles权限生成可访问的路由表
router.addRoutes(accessRoutes); // 动态添加可访问路由表 router.addRoutes(accessRoutes); // 动态添加可访问路由表
next({ path: '/bigscreen', replace: true }); // 登录后跳转到大数据看板 next({ ...to, replace: true }); // 保持当前目标路由,避免刷新后跳转
}); });
}) })
.catch((err) => { .catch((err) => {

View File

@ -1097,7 +1097,22 @@
} }
.analysis-sidebar { .analysis-sidebar {
grid-template-rows: 275px 1fr; grid-template-rows: 400px 1fr;
}
// 分析条件卡片:固定高度,不滚动,避免布局跳动
.analysis-card:first-child {
overflow: hidden;
}
// 测点列表卡片:内部滚动
.analysis-card:last-child {
overflow: hidden;
.model-list {
height: calc(100% - 38px);
overflow: auto;
}
} }
.analysis-content { .analysis-content {
@ -1105,6 +1120,10 @@
} }
.analysis-form { .analysis-form {
.el-form-item {
margin-bottom: 12px;
}
.el-form-item__label { .el-form-item__label {
color: #a8cff0; color: #a8cff0;
} }
@ -1228,3 +1247,121 @@
.header-clock__week { .header-clock__week {
font-size: 26px; /* 星期 */ font-size: 26px; /* 星期 */
} }
/* ========== 时间范围选择器样式 ========== */
.time-preset-group {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 12px;
min-height: 64px; // 固定最小高度,容纳2行按钮
}
.time-preset-btn {
appearance: none;
-webkit-appearance: none;
outline: none;
height: 28px;
padding: 0 10px;
border: 1px solid rgba(99, 184, 255, .35);
border-radius: 3px;
color: #b8d9ff;
font-size: 12px;
background: rgba(4, 16, 56, .4);
cursor: pointer;
transition: color .18s, background .18s, border-color .18s;
margin: 0;
line-height: 1;
&:hover {
color: #eaf8ff;
border-color: rgba(104, 224, 255, .6);
background: rgba(12, 45, 110, .55);
}
&.active {
color: #fff;
border-color: rgba(69, 204, 255, .75);
background: linear-gradient(180deg, rgba(24, 96, 200, .9), rgba(10, 46, 120, .9));
box-shadow: 0 0 10px rgba(32, 150, 255, .35);
}
}
.time-range-display {
display: flex;
align-items: center;
gap: 8px;
margin-top: 8px;
padding: 8px 10px;
border: 1px solid rgba(71, 190, 255, .3);
border-radius: 4px;
background: rgba(6, 22, 62, .5);
height: 56px;
box-sizing: border-box;
}
.time-shift-btn {
appearance: none;
-webkit-appearance: none;
outline: none;
width: 32px;
height: 32px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid rgba(99, 184, 255, .35);
border-radius: 3px;
color: #b8d9ff;
background: rgba(4, 16, 56, .4);
cursor: pointer;
transition: color .18s, background .18s, border-color .18s;
margin: 0;
&:hover:not(:disabled) {
color: #eaf8ff;
border-color: rgba(104, 224, 255, .6);
background: rgba(12, 45, 110, .55);
}
&:disabled {
opacity: .4;
cursor: not-allowed;
}
i {
font-size: 14px;
font-weight: bold;
}
}
.time-range-text {
flex: 1;
min-width: 0;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
gap: 2px;
}
.time-range-start,
.time-range-end {
color: #dff7ff;
font-size: 12px;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 1.3;
}
.time-range-end {
color: #80b5de;
}
/* 自定义时间选择器样式增强 */
.analysis-form .el-date-editor--datetimerange {
width: 100%;
margin-top: 8px;
}

View File

@ -240,13 +240,68 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="时间范围"> <el-form-item label="时间范围">
<!-- 预设时间按钮组 -->
<div class="time-preset-group">
<button
v-for="preset in timePresets"
:key="preset.key"
type="button"
class="time-preset-btn"
:class="{ active: analysisQuery.activePresetKey === preset.key }"
@click.stop="selectTimePreset(preset.key)"
>
{{ preset.shortLabel }}
</button>
<button
type="button"
class="time-preset-btn"
:class="{ active: analysisQuery.isCustomMode }"
@click.stop="enableCustomMode"
>
自定义
</button>
</div>
<!-- 当前时间范围显示 -->
<div class="time-range-display">
<button
type="button"
class="time-shift-btn"
:disabled="!canShiftLeft"
@click="shiftTimeLeft"
title="向前移动"
>
<i class="el-icon-arrow-left"></i>
</button>
<div class="time-range-text">
<div class="time-range-start">
{{ (analysisQuery.timeRange && analysisQuery.timeRange[0]) || '-' }}
</div>
<div class="time-range-end">
{{ (analysisQuery.timeRange && analysisQuery.timeRange[1]) || '-' }}
</div>
</div>
<button
type="button"
class="time-shift-btn"
:disabled="!canShiftRight"
@click="shiftTimeRight"
title="向后移动"
>
<i class="el-icon-arrow-right"></i>
</button>
</div>
<!-- 自定义时间选择器(仅在自定义模式下显示) -->
<el-date-picker <el-date-picker
v-if="analysisQuery.isCustomMode"
v-model="analysisQuery.timeRange" v-model="analysisQuery.timeRange"
type="datetimerange" type="datetimerange"
value-format="yyyy-MM-dd HH:mm:ss" value-format="yyyy-MM-dd HH:mm:ss"
range-separator="-" range-separator="-"
start-placeholder="开始时间" start-placeholder="开始时间"
end-placeholder="结束时间" end-placeholder="结束时间"
@change="handleCustomTimeChange"
/> />
</el-form-item> </el-form-item>
<el-button type="primary" icon="el-icon-search" :loading="analysisLoading" @click="runAnalysis">查询数据和曲线</el-button> <el-button type="primary" icon="el-icon-search" :loading="analysisLoading" @click="runAnalysis">查询数据和曲线</el-button>
@ -397,13 +452,31 @@ export default {
pendingProjectToFocus: null, pendingProjectToFocus: null,
identifierList: [], identifierList: [],
analysisLineList: [], analysisLineList: [],
// 预设时间范围配置
timePresets: [
{ key: '5m', label: '过去 5 分钟', shortLabel: '5分钟', duration: { amount: 5, unit: 'minutes' }, moveStep: 5, moveUnit: 'minutes' },
{ key: '15m', label: '过去 15 分钟', shortLabel: '15分钟', duration: { amount: 15, unit: 'minutes' }, moveStep: 15, moveUnit: 'minutes' },
{ key: '1h', label: '过去 1 小时', shortLabel: '1小时', duration: { amount: 1, unit: 'hours' }, moveStep: 1, moveUnit: 'hours' },
{ key: '4h', label: '过去 4 小时', shortLabel: '4小时', duration: { amount: 4, unit: 'hours' }, moveStep: 4, moveUnit: 'hours' },
{ key: '12h', label: '过去 12 小时', shortLabel: '12小时', duration: { amount: 12, unit: 'hours' }, moveStep: 12, moveUnit: 'hours' },
{ key: '24h', label: '过去 24 小时', shortLabel: '24小时', duration: { amount: 24, unit: 'hours' }, moveStep: 24, moveUnit: 'hours' },
{ key: '2d', label: '过去 2 天', shortLabel: '2天', duration: { amount: 2, unit: 'days' }, moveStep: 2, moveUnit: 'days' },
{ key: '3d', label: '过去 3 天', shortLabel: '3天', duration: { amount: 3, unit: 'days' }, moveStep: 3, moveUnit: 'days' },
{ key: '5d', label: '过去 5 天', shortLabel: '5天', duration: { amount: 5, unit: 'days' }, moveStep: 5, moveUnit: 'days' },
{ key: '7d', label: '过去 7 天', shortLabel: '7天', duration: { amount: 7, unit: 'days' }, moveStep: 7, moveUnit: 'days' },
],
analysisQuery: { analysisQuery: {
deviceId: null, deviceId: null,
timeRange: [ timeRange: [
moment().subtract(2, 'hours').format('YYYY-MM-DD HH:mm:ss'), moment().subtract(1, 'hours').format('YYYY-MM-DD HH:mm:ss'),
moment().format('YYYY-MM-DD HH:mm:ss'), moment().format('YYYY-MM-DD HH:mm:ss'),
], ], // 默认过去1小时,避免初始为空
activePresetKey: '1h', // 默认选中"过去 1 小时"
isCustomMode: false, // 是否自定义时间模式
}, },
customTimeVisible: false, // 自定义时间选择器是否显示
canShiftLeft: true, // 是否可以向左移动
canShiftRight: true, // 是否可以向右移动(初始时可以,因为时间窗口可以向前移动)
scrollOption: { scrollOption: {
step: 0.8, step: 0.8,
limitMoveNum: 4, limitMoveNum: 4,
@ -602,6 +675,10 @@ export default {
mounted() { mounted() {
this.$nextTick(() => this.calcRate()); this.$nextTick(() => this.calcRate());
this.timeFn(); this.timeFn();
this.$nextTick(() => {
// 确保 timeRange 已初始化后再更新按钮状态
this.updateShiftButtonState();
});
this.loadCockpitData(); this.loadCockpitData();
this.fetchHeaderUser(); this.fetchHeaderUser();
}, },
@ -1182,6 +1259,125 @@ export default {
this.identifierList = []; this.identifierList = [];
}); });
}, },
// ========== 时间范围选择器方法 ==========
// 选择预设时间范围
selectTimePreset(presetKey) {
const preset = this.timePresets.find(p => p.key === presetKey);
if (!preset) return;
const endTime = moment();
const startTime = moment().subtract(preset.duration.amount, preset.duration.unit);
this.analysisQuery.timeRange = [
startTime.format('YYYY-MM-DD HH:mm:ss'),
endTime.format('YYYY-MM-DD HH:mm:ss')
];
this.analysisQuery.activePresetKey = presetKey;
this.analysisQuery.isCustomMode = false;
this.customTimeVisible = false;
// 更新移动按钮状态
this.updateShiftButtonState();
// 如果已经选择了设备,自动触发查询
if (this.analysisQuery.deviceId && this.activeMode === 'analysis') {
this.$nextTick(() => this.runAnalysis());
}
},
// 左移时间窗口(往前推)
shiftTimeLeft() {
if (!this.canShiftLeft) return;
const preset = this.getActivePreset();
if (!preset) return;
const [startTime, endTime] = this.analysisQuery.timeRange;
const newStartTime = moment(startTime).subtract(preset.moveStep, preset.moveUnit);
const newEndTime = moment(endTime).subtract(preset.moveStep, preset.moveUnit);
// 更新时间范围
this.analysisQuery.timeRange = [
newStartTime.format('YYYY-MM-DD HH:mm:ss'),
newEndTime.format('YYYY-MM-DD HH:mm:ss')
];
// 更新移动按钮状态
this.updateShiftButtonState();
// 自动触发查询
if (this.analysisQuery.deviceId) {
this.$nextTick(() => this.runAnalysis());
}
},
// 右移时间窗口(往后推)
shiftTimeRight() {
if (!this.canShiftRight) return;
const preset = this.getActivePreset();
if (!preset) return;
const [startTime, endTime] = this.analysisQuery.timeRange;
const newStartTime = moment(startTime).add(preset.moveStep, preset.moveUnit);
const newEndTime = moment(endTime).add(preset.moveStep, preset.moveUnit);
// 不允许超过当前时间
if (newEndTime.isAfter(moment())) return;
// 更新时间范围
this.analysisQuery.timeRange = [
newStartTime.format('YYYY-MM-DD HH:mm:ss'),
newEndTime.format('YYYY-MM-DD HH:mm:ss')
];
// 更新移动按钮状态
this.updateShiftButtonState();
// 自动触发查询
if (this.analysisQuery.deviceId) {
this.$nextTick(() => this.runAnalysis());
}
},
// 启用自定义时间模式
enableCustomMode() {
this.analysisQuery.isCustomMode = true;
this.analysisQuery.activePresetKey = null;
this.canShiftLeft = false;
this.canShiftRight = false;
// 强制触发视图更新
this.$forceUpdate();
},
// 自定义时间改变事件
handleCustomTimeChange() {
// 自定义时间模式下,禁用移动按钮
this.canShiftLeft = false;
this.canShiftRight = false;
},
// 获取当前激活的预设配置
getActivePreset() {
if (!this.analysisQuery.activePresetKey) return null;
return this.timePresets.find(p => p.key === this.analysisQuery.activePresetKey);
},
// 更新移动按钮状态
updateShiftButtonState() {
const preset = this.getActivePreset();
if (!preset || this.analysisQuery.isCustomMode) {
// 自定义时间模式下,禁用移动按钮
this.canShiftLeft = false;
this.canShiftRight = false;
return;
}
const [startTime, endTime] = this.analysisQuery.timeRange;
const now = moment();
// 检查是否可以向左移动(总是可以,除非已经是历史最早时间)
this.canShiftLeft = true;
// 检查是否可以向右移动(结束时间 + 步长 不能超过当前时间)
const testEndTime = moment(endTime).add(preset.moveStep, preset.moveUnit);
this.canShiftRight = testEndTime.isSameOrBefore(now);
},
runAnalysis() { runAnalysis() {
const device = this.selectedAnalysisDevice; const device = this.selectedAnalysisDevice;
if (!device) { if (!device) {