Browse Source

修改样式问题,修改除了一宫格外都用子码流

master
douyage 2 weeks ago
parent
commit
83dd32bf35
10 changed files with 300 additions and 51 deletions
  1. +2
    -2
      src/api/nvrVideo.js
  2. +11
    -0
      src/assets/styles/sidebar.scss
  3. +3
    -2
      src/router/index.js
  4. +10
    -2
      src/views/cards/assets/styles/index.scss
  5. +223
    -37
      src/views/cards/defectRecord/index.vue
  6. +5
    -0
      src/views/cards/patrolPlan/summary_editTask.vue
  7. +5
    -0
      src/views/cards/patrolPlan/summary_newTask.vue
  8. +2
    -2
      src/views/cards/patrolReport/patrolReportDetail.vue
  9. +33
    -4
      src/views/video/LivePreview.vue
  10. +6
    -2
      src/views/video/components/NvrDeviceTree.vue

+ 2
- 2
src/api/nvrVideo.js View File

@ -28,13 +28,13 @@ export function getNvrConfig() {
return unwrap(request({ url: '/patrol/video/config', method: 'get' }))
}
export function startLive(channel) {
export function startLive(channel, streamType = 1) {
return unwrap(request({
url: '/patrol/video/realtime/start',
method: 'post',
data: {
cameraCode: channel.cameraCode || channel.channelCode,
streamType: 1
streamType
}
}))
}


+ 11
- 0
src/assets/styles/sidebar.scss View File

@ -238,6 +238,17 @@
}
}
}
// 收起时仅展示图标隐藏菜单文字保留图标图标保持居中
// 菜单图标与文字被 .menu-item-inner 包裹需只隐藏其中的文字 span
.el-menu > div > a > .submenu-title-noDropdown .menu-item-inner,
.el-menu > div > .el-submenu > .el-submenu__title .menu-item-inner {
overflow: visible;
> span:not(.menu-icon-img):not(.menu-icon-placeholder) {
display: none !important;
}
}
}
.el-menu--collapse .el-menu .el-submenu {


+ 3
- 2
src/router/index.js View File

@ -170,12 +170,13 @@ export const constantRoutes = [
{
path: '/task-summary',
component: Layout,
redirect: '/task-summary/inspectionTask',
// 与默认首页 /index 渲染同一组件,重定向过去复用固定的巡视任务页签,避免出现两个同名页签
redirect: '/index',
meta: { title: '巡视任务' },
children: [
{
path: 'inspectionTask',
component: () => import('@/views/cards/taskSummary/inspectionTask'),
redirect: '/index',
name: 'InspectionTaskSummary',
meta: { title: '巡视任务', activeMenu: '/task-summary/inspectionTask' }
}


+ 10
- 2
src/views/cards/assets/styles/index.scss View File

@ -555,7 +555,8 @@
}
.site-point-add-dialog {
height: 92vh;
// 高度随内容自适应避免表单内容少时弹框底部出现大片空白
height: auto;
max-height: 92vh;
margin-top: 4vh;
display: flex;
@ -563,8 +564,15 @@
border: 0;
box-shadow: none;
// 去掉表单自带的深色背景与弹窗底色保持一致
// ruoyi.scss .card-page 的背景使用了 !important这里需同样处理
.el-form.card-page {
background-color: transparent !important;
border: 0 !important;
}
.el-dialog__body {
flex: 1 1 auto;
flex: 0 0 auto;
min-height: 0;
height: auto;
max-height: calc(92vh - 135px);


+ 223
- 37
src/views/cards/defectRecord/index.vue View File

@ -4,6 +4,62 @@
:showTitle="false"
>
<div class="page-container">
<!-- 查询栏总缺陷数 + 缺陷类型/点位名称/采集时间条件 -->
<div class="query-bar">
<div class="total-chip">
<span class="total-label">总缺陷数</span>
<span class="total-value">{{ total }}</span>
</div>
<el-select
v-model="defectTypeList"
class="query-item query-type"
multiple
collapse-tags
filterable
clearable
placeholder="缺陷类型"
@change="handleQuery"
>
<el-option
v-for="item in defectTypeOptions"
:key="item"
:label="item"
:value="item"
/>
</el-select>
<el-input
v-model="queryParams.pointName"
class="query-item query-input"
placeholder="点位名称"
clearable
@keyup.enter.native="handleQuery"
/>
<el-date-picker
v-model="dateRange"
class="query-item query-date"
type="daterange"
value-format="yyyy-MM-dd"
range-separator="-"
start-placeholder="采集开始日期"
end-placeholder="采集结束日期"
:picker-options="dateRangePickerOptions"
:clearable="true"
@change="handleQuery"
/>
<el-button
type="primary"
icon="el-icon-search"
class="query-btn"
@click="handleQuery"
>搜索</el-button
>
<el-button
icon="el-icon-refresh"
class="query-btn query-reset-btn"
@click="resetQuery"
>重置</el-button
>
</div>
<div class="table-card" ref="tableCardRef">
<el-table
v-loading="loading"
@ -80,7 +136,10 @@ export default {
components: { CardBox, CardPagination, ImagePreview, AsyncImage },
data() {
return {
queryParams: { pageNum: 1, pageSize: 20 },
queryParams: { pageNum: 1, pageSize: 20, pointName: "" },
defectTypeList: [],
allDataList: [], //
filteredList: [], //
dataList: [],
loading: false,
total: 0,
@ -106,8 +165,19 @@ export default {
previewImagePos: 0,
};
},
computed: {
//
defectTypeOptions() {
const set = new Set();
this.allDataList.forEach((item) => {
const desc = (item.desc || "").trim();
if (desc) set.add(desc);
});
return Array.from(set).sort((a, b) => a.localeCompare(b, "zh"));
},
},
created() {
this.getList();
this.loadData();
window.addEventListener("resize", this.setTableHeight);
},
mounted() {
@ -139,7 +209,91 @@ export default {
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
this.applyFilters();
},
//
resetQuery() {
this.defectTypeList = [];
this.queryParams.pointName = "";
this.dateRange = [];
this.handleQuery();
},
//
loadData() {
this.loading = true;
//
this.imageLoadCount = 0;
this.expectedImageCount = 0;
defectRecordList({ pageNum: 1, pageSize: 9999 })
.then((res) => {
this.allDataList = res.rows || [];
this.applyFilters();
})
.catch((error) => {
console.error("获取缺陷记录失败:", error);
this.allDataList = [];
this.applyFilters();
})
.finally(() => {
this.loading = false;
});
},
//
applyFilters() {
const keyword = (this.queryParams.pointName || "").trim().toLowerCase();
const types = this.defectTypeList || [];
const range =
this.dateRange && this.dateRange.length === 2 ? this.dateRange : null;
this.filteredList = this.allDataList.filter((row) => {
if (types.length && !types.includes((row.desc || "").trim())) {
return false;
}
if (
keyword &&
(row.pointName || "").toLowerCase().indexOf(keyword) === -1
) {
return false;
}
if (range) {
const day = (row.patrolTime || "").slice(0, 10);
if (!day || day < range[0] || day > range[1]) {
return false;
}
}
return true;
});
this.total = this.filteredList.length;
//
const maxPage = Math.max(
1,
Math.ceil(this.total / (this.queryParams.pageSize || 20))
);
if (this.queryParams.pageNum > maxPage) {
this.queryParams.pageNum = 1;
}
const start = (this.queryParams.pageNum - 1) * this.queryParams.pageSize;
this.dataList = this.filteredList.slice(
start,
start + this.queryParams.pageSize
);
//
this.expectedImageCount = this.dataList.filter(
(item) => item.imgAnalyse
).length;
//
if (this.expectedImageCount === 0) {
this.forceRefreshTable();
} else {
this.$nextTick(this.setTableHeight);
}
},
linkUrl() {
@ -201,42 +355,13 @@ export default {
});
},
//
getList() {
this.loading = true;
//
this.imageLoadCount = 0;
this.expectedImageCount = 0;
const params = { ...this.queryParams };
if (this.dateRange && this.dateRange.length === 2) {
params.beginTime = this.dateRange[0];
params.endTime = this.dateRange[1];
if (!this.allDataList.length) {
this.loadData();
return;
}
defectRecordList(params)
.then((res) => {
this.dataList = res.rows || [];
this.total = res.total || 0;
this.$nextTick(this.setTableHeight);
//
this.expectedImageCount = this.dataList.filter(
(item) => item.imgAnalyse
).length;
//
if (this.expectedImageCount === 0) {
this.forceRefreshTable();
}
})
.catch((error) => {
console.error("获取缺陷记录失败:", error);
this.dataList = [];
this.total = 0;
})
.finally(() => {
this.loading = false;
});
this.applyFilters();
},
onSubmit() {
@ -252,6 +377,67 @@ export default {
display: flex;
flex-direction: column;
}
// +
.query-bar {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px;
padding: 12px 14px;
margin-bottom: 12px;
border: 1px solid #0b2567;
border-radius: 2px;
background: rgba(11, 37, 103, 0.35);
.total-chip {
display: flex;
align-items: center;
gap: 10px;
padding: 5px 14px;
border: 1px solid rgba(63, 127, 216, 0.72);
border-radius: 2px;
background: rgba(11, 37, 103, 0.55);
.total-label {
font-size: 14px;
color: #dcecff;
}
.total-value {
font-size: 20px;
font-weight: 700;
color: #54ffff;
line-height: 1.2;
}
}
.query-type {
width: 220px;
}
.query-input {
width: 180px;
}
.query-date {
width: 260px;
}
//
.query-reset-btn {
background: linear-gradient(#01016b, #01016b) padding-box,
linear-gradient(135deg, #3988ff, #0351c6) border-box !important;
border: 1px solid transparent !important;
color: #43c0ff !important;
&:hover,
&:focus {
background: linear-gradient(#01016b, #01016b) padding-box,
linear-gradient(135deg, #3988ff, #0351c6) border-box !important;
color: #43c0ff !important;
}
}
}
.card-page {
height: 100%;
min-height: 0;


+ 5
- 0
src/views/cards/patrolPlan/summary_editTask.vue View File

@ -1195,6 +1195,11 @@ export default {
align-items: flex-start;
margin-bottom: 24px;
//
::v-deep .el-select .el-input.is-disabled .el-input__inner {
background-color: rgba(27, 42, 94, 0.94) !important;
}
.form-label {
width: 130px;
flex-shrink: 0;


+ 5
- 0
src/views/cards/patrolPlan/summary_newTask.vue View File

@ -1042,6 +1042,11 @@ export default {
align-items: flex-start;
margin-bottom: 24px;
//
::v-deep .el-select .el-input.is-disabled .el-input__inner {
background-color: rgba(27, 42, 94, 0.94) !important;
}
.form-label {
width: 130px;
flex-shrink: 0;


+ 2
- 2
src/views/cards/patrolReport/patrolReportDetail.vue View File

@ -19,13 +19,13 @@
:show-header="false"
:span-method="headerSpan"
>
<el-table-column prop="title1" label="">
<el-table-column prop="title1" label="" width="140">
<template slot-scope="scope">
<span class="header-title">{{ scope.row.title1 }}</span>
</template>
</el-table-column>
<el-table-column prop="info1" label=""> </el-table-column>
<el-table-column prop="title2" label="">
<el-table-column prop="title2" label="" width="140">
<template slot-scope="scope">
<span class="header-title">{{ scope.row.title2 }}</span>
</template>


+ 33
- 4
src/views/video/LivePreview.vue View File

@ -199,6 +199,7 @@ export default {
streamCleanupPromise: null,
selectedCode: '',
layout: 9,
fullscreenIndex: -1,
layouts: [
{ value: 1, label: '单画面' }, { value: 4, label: '四画面' },
{ value: 9, label: '九画面' }, { value: 16, label: '十六画面' }
@ -259,6 +260,8 @@ export default {
visibleSlots() { return this.slots.slice(0, this.capacity) },
activeSlot() { return this.slots[this.activeIndex] || null },
activeMuted() { return this.controlTarget ? this.controlTarget.muted !== false : true },
// 4/9/16
streamType() { return this.layout === 1 ? 1 : 2 },
gridStyle() {
const columns = { 1: 1, 4: 2, 9: 3, 16: 4 }[this.layout]
return { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`, gridTemplateRows: `repeat(${columns}, minmax(0, 1fr))` }
@ -269,6 +272,7 @@ export default {
created() {
this.$root.$on('video-layout-change', this.changeLayout)
this.$root.$on('video-control-panel-change', this.changeControlPanel)
document.addEventListener('fullscreenchange', this.handleFullscreenChange)
this.$root.$emit('video-layout-updated', this.layout)
this.$root.$emit('video-control-panel-updated', this.controlPanelExpanded)
this.loadConfig()
@ -293,6 +297,7 @@ export default {
this.stopTreeResize()
this.lifecycleId += 1
this.pageActive = false
document.removeEventListener('fullscreenchange', this.handleFullscreenChange)
this.$root.$off('video-layout-change', this.changeLayout)
this.$root.$off('video-control-panel-change', this.changeControlPanel)
this.closeAll()
@ -421,11 +426,11 @@ export default {
this.selectChannel(channel)
this.startChannel(channel)
},
async startChannel(channel, targetIndex = this.activeIndex, attemptedCameraCodes = []) {
async startChannel(channel, targetIndex = this.activeIndex, attemptedCameraCodes = [], streamType = this.streamType) {
if (!this.pageActive || !channel || !channel.cameraCode) return
const index = targetIndex
const current = this.slots[index]
if (current && current.cameraCode === channel.cameraCode) {
if (current && current.cameraCode === channel.cameraCode && current.streamType === streamType) {
this.controlTarget = current
return
}
@ -439,7 +444,8 @@ export default {
muted: true,
errorNotified: false,
attemptedCameraCodes: [...attemptedCameraCodes, channel.cameraCode],
timer: null
timer: null,
streamType
}
this.$set(this.slots, index, slot)
if (index === this.activeIndex) {
@ -449,7 +455,7 @@ export default {
this.presets = []
this.presetId = null
try {
slot.startPromise = startLive(channel)
slot.startPromise = startLive(channel, streamType)
const result = await slot.startPromise
if (this.slots[index] !== slot) return
this.applyLiveResult(index, slot, result)
@ -587,6 +593,29 @@ export default {
const card = this.$refs.cameraCards && this.$refs.cameraCards[index]
if (card && card.requestFullscreen) card.requestFullscreen()
},
// 退
async handleFullscreenChange() {
const fsElement = document.fullscreenElement
if (fsElement) {
const cards = this.$refs.cameraCards || []
const index = cards.indexOf(fsElement)
if (index >= 0) {
this.fullscreenIndex = index
await this.switchSlotStream(index, 1)
}
} else if (this.fullscreenIndex >= 0) {
const index = this.fullscreenIndex
this.fullscreenIndex = -1
await this.switchSlotStream(index, this.streamType)
}
},
async switchSlotStream(index, streamType) {
const slot = this.slots[index]
if (!this.pageActive || !slot || slot.streamType === streamType) return
await this.closeSlot(index, false)
if (!this.pageActive) return
await this.startChannel(slot, index, [], streamType)
},
openPlayback(slot) {
this.$router.push({ name: 'VideoHistoryPlayback', query: { cameraCode: slot.cameraCode }})
},


+ 6
- 2
src/views/video/components/NvrDeviceTree.vue View File

@ -155,7 +155,7 @@ export default {
.tree-filters .el-input { flex: 1; min-width: 0; }
.tree-filters .el-button { flex: 0 0 32px; padding: 0; border-color: #2787ed; color: #54ffff; background: #0a2b67; }
.tree-filters .el-button:hover, .tree-filters .el-button:focus { border-color: #2787ed; color: #ffffff; background: #176bc2; }
.tree-content { flex: 1; min-height: 0; overflow-x: auto; overflow-y: auto; padding: 0 8px 8px; scrollbar-width: thin; scrollbar-color: #2f7fe8 rgba(6,27,79,.7); }
.tree-content { flex: 1; min-height: 0; overflow-x: auto; overflow-y: auto; padding: 0 0 8px; scrollbar-width: thin; scrollbar-color: #2f7fe8 rgba(6,27,79,.7); }
.tree-content::-webkit-scrollbar { width: 8px; height: 8px; }
.tree-content::-webkit-scrollbar-track { background: rgba(6,27,79,.7); }
.tree-content::-webkit-scrollbar-thumb { background: #2f7fe8; border: 1px solid #0d327d; border-radius: 4px; }
@ -169,7 +169,11 @@ export default {
::v-deep .custom-tree-node,
::v-deep .node-label { font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif !important; font-weight: 400 !important; }
::v-deep .el-tree-node__content { display: flex; }
::v-deep .el-tree-node__content { height: 34px; }
/* 行高与点位管理设备树保持一致,展开后更紧凑 */
::v-deep .el-tree-node__content { height: 24px; }
/*
与前几级的间距节奏保持一致同点位管理设备树的处理 */
::v-deep .el-tree-node__expand-icon.is-leaf { display: inline-block; width: 12px; }
::v-deep .el-tree-node__content:hover, ::v-deep .el-tree-node:focus > .el-tree-node__content { background: transparent; }
::v-deep .el-tree-node.is-current > .el-tree-node__content { color: #54ffff; background: transparent; }
</style>


Loading…
Cancel
Save